欢迎支持笔者新作:《深入理解Kafka:核心设计与实践原理》和《RabbitMQ实战指南》,同时欢迎关注笔者的微信公众号:朱小厮的博客。

欢迎跳转到本文的原文链接:https://honeypps.com/network/java-tcp-comm/

继上一篇小插曲之后继续回到正题,本篇讲述的是java的TCP通讯。TCP编程分为server端和client端,一般在网上都能搜到相关的例子,为了方便大家,我这里先整理下server端和client端的应用案例,然后再根据在本系列中第一篇串口通讯中的结构一样实现CommBuff接口。

java tcp socket编程server端:

 import java.io.*;import java.net.*;import java.applet.Applet;public class TalkServer{public static void main(String args[]) {try{ServerSocket server=null;try{server=new ServerSocket(4700);//创建一个ServerSocket在端口4700监听客户请求}catch(Exception e) {System.out.println("can not listen to:"+e);//出错,打印出错信息}Socket socket=null;try{socket=server.accept();//使用accept()阻塞等待客户请求,有客户//请求到来则产生一个Socket对象,并继续执行}catch(Exception e) {System.out.println("Error."+e);//出错,打印出错信息}String line;BufferedReader is=new BufferedReader(new InputStreamReader(socket.getInputStream()));//由Socket对象得到输入流,并构造相应的BufferedReader对象PrintWriter os=newPrintWriter(socket.getOutputStream());//由Socket对象得到输出流,并构造PrintWriter对象BufferedReader sin=new BufferedReader(new InputStreamReader(System.in));//由系统标准输入设备构造BufferedReader对象System.out.println("Client:"+is.readLine());//在标准输出上打印从客户端读入的字符串line=sin.readLine();//从标准输入读入一字符串while(!line.equals("bye")){//如果该字符串为 "bye",则停止循环os.println(line);//向客户端输出该字符串os.flush();//刷新输出流,使Client马上收到该字符串System.out.println("Server:"+line);//在系统标准输出上打印读入的字符串System.out.println("Client:"+is.readLine());//从Client读入一字符串,并打印到标准输出上line=sin.readLine();//从系统标准输入读入一字符串}  //继续循环os.close(); //关闭Socket输出流is.close(); //关闭Socket输入流socket.close(); //关闭Socketserver.close(); //关闭ServerSocket}catch(Exception e){System.out.println("Error:"+e);//出错,打印出错信息}}}

java tcp socket编程client端:

  import java.io.*;import java.net.*;public class TalkClient {public static void main(String args[]) {try{Socket socket=new Socket("127.0.0.1",4700);//向本机的4700端口发出客户请求BufferedReader sin=new BufferedReader(new InputStreamReader(System.in));//由系统标准输入设备构造BufferedReader对象PrintWriter os=new PrintWriter(socket.getOutputStream());//由Socket对象得到输出流,并构造PrintWriter对象BufferedReader is=new BufferedReader(new InputStreamReader(socket.getInputStream()));//由Socket对象得到输入流,并构造相应的BufferedReader对象String readline;readline=sin.readLine(); //从系统标准输入读入一字符串while(!readline.equals("bye")){//若从标准输入读入的字符串为 "bye"则停止循环os.println(readline);//将从系统标准输入读入的字符串输出到Serveros.flush();//刷新输出流,使Server马上收到该字符串System.out.println("Client:"+readline);//在系统标准输出上打印读入的字符串System.out.println("Server:"+is.readLine());//从Server读入一字符串,并打印到标准输出上readline=sin.readLine(); //从系统标准输入读入一字符串} //继续循环os.close(); //关闭Socket输出流is.close(); //关闭Socket输入流socket.close(); //关闭Socket}catch(Exception e) {System.out.println("Error"+e); //出错,则打印出错信息}}
}

通过上面两个实例可以大概的了解到java tcp编程的流程。
这里有必要补充下Socket的相关概念:

1. 什么是Socket

网络上的两个程序通过一个双向的通讯连接实现数据的交换,这个双向链路的一端称为一个Socket。Socket通常用来实现客户方和服务方的连接。Socket是TCP/IP协议的一个十分流行的编程界面,一个Socket由一个IP地址和一个端口号唯一确定。但是,Socket所支持的协议种类也不光TCP/IP一种,因此两者之间是没有必然联系的。在Java环境下,Socket编程主要是指基于TCP/IP协议的网络编程。

2. Socket通讯的过程

Server端Listen(监听)某个端口是否有连接请求,Client端向Server 端发出Connect(连接)请求,Server端向Client端发回Accept(接受)消息。一个连接就建立起来了。Server端和Client 端都可以通过Send,Write等方法与对方通信。

对于一个功能齐全的Socket,都要包含以下基本结构,其工作过程包含以下四个基本的步骤:

  (1) 创建Socket;

  (2) 打开连接到Socket的输入/出流;

  (3) 按照一定的协议对Socket进行读/写操作;

  (4) 关闭Socket.(在实际应用中,并未使用到显示的close,虽然很多文章都推荐如此,不过在我的程序中,可能因为程序本身比较简单,要求不高,所以并未造成什么影响。)

3. 创建Socket

java在包java.net中提供了两个类Socket和ServerSocket,分别用来表示双向连接的客户端和服务端。这是两个封装得非常好的类,使用很方便。其构造方法如下:

  Socket(InetAddress address, int port);

  Socket(InetAddress address, int port, boolean stream);

  Socket(String host, int prot);

  Socket(String host, int prot, boolean stream);

  Socket(SocketImpl impl)

  Socket(String host, int port, InetAddress localAddr, int localPort)

  Socket(InetAddress address, int port, InetAddress localAddr, int localPort)

  ServerSocket(int port);

  ServerSocket(int port, int backlog);

  ServerSocket(int port, int backlog, InetAddress bindAddr)

  其中address、host和port分别是双向连接中另一方的IP地址、主机名和端 口号,stream指明socket是流socket还是数据报socket,localPort表示本地主机的端口号,localAddr和 bindAddr是本地机器的地址(ServerSocket的主机地址),impl是socket的父类,既可以用来创建serverSocket又可 以用来创建Socket。count则表示服务端所能支持的最大连接数。例如:学习视频网 http://www.xxspw.com

  Socket client = new Socket("127.0.01.", 80);

  ServerSocket server = new ServerSocket(80);

  注意,在选择端口时,必须小心。每一个端口提供一种特定的服务,只有给出正确的端口,才 能获得相应的服务。0~1023的端口号为系统所保留,例如http服务的端口号为80,telnet服务的端口号为21,ftp服务的端口号为23, 所以我们在选择端口号时,最好选择一个大于1023的数以防止发生冲突。

  在创建socket时如果发生错误,将产生IOException,在程序中必须对之作出处理。所以在创建Socket或ServerSocket是必须捕获或抛出例外。

介绍了tcp socket编程的相关概念,可以回到正题了,下面所示是实现了CommBuff的服务端Socket程序(CommBuff接口可以参看点击打开链接)

package com.zzh.comm;import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.ServerSocket;
import java.net.Socket;
import java.util.Map;import org.apache.log4j.Logger;public class TcpServerImpl implements CommBuff
{private Logger logger = Logger.getLogger(Object.class.getName());private int port;private ServerSocket server = null;private Socket socket = null;InputStream in = null;OutputStream out = null;private static byte[] recvBuff = new byte[4096];private static int recvLen = 0;private String fileName = "/tcp.properties";public TcpServerImpl(){Map<String,String> map = new ReadProperties().getPropertiesMap(fileName);try{port = Integer.parseInt(map.get("tcp_port"));}catch (Exception e){logger.error(e.getMessage());}}@Overridepublic synchronized byte[] readBuff(){if(in ==null){close();return new byte[0];}byte[] readBuffer = new byte[1024];try{while(in.available()>0){int numBytes = in.read(readBuffer);if(recvLen + numBytes > 4096){throw new RuntimeException("接收缓存数组内容退出");}else{logger.info("网口接收:"+CommUtil.bytesToHexWithLen(readBuffer,numBytes));System.arraycopy(readBuffer, 0, recvBuff, recvLen, numBytes);recvLen = recvLen + numBytes;}}}catch (IOException e){logger.error(e.getMessage());}byte[] ans = new byte[0];if(recvLen>0){ans = new byte[recvLen];System.arraycopy(recvBuff,0,ans,0,recvLen);recvLen = 0;}return ans;}@Overridepublic synchronized void writeBuff(byte[] message){if(out ==null){close();return;}try{out.write(message);out.flush();logger.info("发送成功: "+CommUtil.bytesToHex(message));}catch (IOException e){logger.error(e.getMessage());}}@Overridepublic void open() {logger.info("Try to open tcpServer");try{server = new ServerSocket(port);}catch (IOException e){logger.error(e.getMessage());}try{socket = server.accept();}catch (IOException e){logger.error(e.getMessage());}logger.info("TcpServer正在监听....");try{in = socket.getInputStream();out = socket.getOutputStream();}catch (IOException e){logger.error(e.getMessage());}logger.info("成功开启TCP Server");}@Overridepublic void close() {try{if(out != null){out.close();}if(in != null){in.close();}if(socket != null){socket.close();}if(server!=null){server.close();}}catch (IOException e){e.printStackTrace();}}@Overridepublic Object getInfo(){return socket;}}

可以看到TcpServerImpl类实现了CommBuff中的方法,如果采用简单工厂模式就可以写出无关TCP或者串口通讯的底层程序,即上层应用既可以通过TCP通讯也可以通过串口进行通讯,至于底层采用什么具体的通讯方式是可配置化的,这样进一步提高了程序的灵活性。

下面展示的是TcpClientImpl类,这个采用的是TCP Client的方式实现CommBuff接口,其实本质上和TcpServerImpl并无多大差别。

package com.zzh.comm;import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.ConnectException;
import java.net.Socket;
import java.net.UnknownHostException;
import java.util.Map;
import java.util.concurrent.TimeUnit;import org.apache.log4j.Logger;public class TcpClientImpl implements CommBuff
{private Logger logger = Logger.getLogger(Object.class.getName());private int port;private String tcp_server_ip;private Socket socket = null;InputStream in = null;OutputStream out = null;private static byte[] recvBuff = new byte[4096];private static int recvLen = 0;private String fileName = "/tcp.properties";public TcpClientImpl(){Map<String,String> map = new ReadProperties().getPropertiesMap(fileName);try{port = Integer.parseInt(map.get("tcp_port"));tcp_server_ip = map.get("tcp_server_ip");}catch (Exception e){logger.error(e.getMessage());}}@Overridepublic synchronized byte[] readBuff(){if(in ==null){close();return new byte[0];}byte[] readBuffer = new byte[1024];try{while(in.available()>0){int numBytes = in.read(readBuffer);if(recvLen + numBytes > 4096){throw new RuntimeException("接收缓存数组内容退出");}else{logger.info("网口接收:"+CommUtil.bytesToHexWithLen(readBuffer,numBytes));System.arraycopy(readBuffer, 0, recvBuff, recvLen, numBytes);recvLen = recvLen + numBytes;}}}catch (IOException e){logger.error(e.getMessage());}byte[] ans = new byte[0];if(recvLen>0){ans = new byte[recvLen];System.arraycopy(recvBuff,0,ans,0,recvLen);recvLen = 0;}return ans;}@Overridepublic synchronized void writeBuff(byte[] message){if(out ==null){close();return;}try{out.write(message);out.flush();logger.info("发送成功: "+CommUtil.bytesToHex(message));}catch (IOException e){logger.error(e.getMessage());logger.info("网络断开");close();logger.info("5s后重新启动网络.....");try{TimeUnit.MILLISECONDS.sleep(5000);}catch (InterruptedException ee){logger.error(ee.getMessage());}open();}}@Overridepublic void open(){logger.info("Connecting to "+tcp_server_ip+":"+port);while(true){try{socket = new Socket(tcp_server_ip,port);}catch (UnknownHostException e){logger.error(e.getMessage());}catch (ConnectException e){logger.error(e.getMessage());}catch (IOException e){logger.error(e.getMessage());}if(socket != null){logger.info("连接成功!");break;}else{logger.info("连接失败!5s后重试连接....");try{TimeUnit.MILLISECONDS.sleep(5000);}catch (InterruptedException e){logger.error(e.getMessage());}}}try{in = socket.getInputStream();out = socket.getOutputStream();}catch (IOException e){logger.error(e.getMessage());}}@Overridepublic void close(){if(in != null){try{in.close();in = null;}catch (IOException e){logger.error(e.getMessage());}}if(out != null){try{out.close();out = null;}catch (IOException e){logger.error(e.getMessage());}}if(socket != null){try{socket.close();socket = null;}catch (IOException e){logger.error(e.getMessage());}}}@Overridepublic Object getInfo(){return socket;}
}

TCP的先讲述到这里,在下一篇会讲述到如何采用UDP进行通讯的编程实践。

欢迎跳转到本文的原文链接:https://honeypps.com/network/java-tcp-comm/

欢迎支持笔者新作:《深入理解Kafka:核心设计与实践原理》和《RabbitMQ实战指南》,同时欢迎关注笔者的微信公众号:朱小厮的博客。

JAVA通信编程(三)——TCP通讯相关推荐

  1. Java网络编程:TCP实现群聊私聊代码

    Java网络编程:TCP实现群聊&私聊代码 和上一篇博客差不多,只不过是在群里的基础之上增加了私聊的功能,我们约定,私聊格式为:@xxx:msg 如何实现私聊呢,加入客户端c给服务器发送消息, ...

  2. JAVA通信编程(一)——串口通讯

    欢迎支持笔者新作:<深入理解Kafka:核心设计与实践原理>和<RabbitMQ实战指南>,同时欢迎关注笔者的微信公众号:朱小厮的博客. 欢迎跳转到本文的原文链接:https: ...

  3. Java网络编程:TCP,UDP,sock编程

    第一节 网络基础知识 常用的通信协议 MAC地址:(Media Access Control Address,媒体存取控制位址), IP:(Internet Protocol,网际协议) UDP:(U ...

  4. java 网络编程 UDP TCP

    网络编程 网络编程主要用于解决计算机与计算机(手机.平板..)之间的数据传输问题. 网络编程: 不需要基于html页面就可以达到数据之间的传输. 比如: feiQ , QQ , 微信.... 网页编程 ...

  5. flash java 通信_FLASH与服务器通讯 (JAVA)

    1.FLASH 通过URL地址获得服务器数据. 这种方式最为简单,就像在浏览器的地址栏里面敲一样. 先建立一个URLRequest,然后用URLLoader载入就行了. 下面这个是载入一个图片,htm ...

  6. Java——网络编程三要素

    * A:计算机网络* 是指将地理位置不同的具有独立功能的多台计算机及其外部设备,通过通信线路连接起来,在网络操作系统.网络管理软件及网络通信协议的管理和协调下,实现资源共享和信息传递的计算机系统.* ...

  7. 进阶笔记——java并发编程三特性与volatile

    欢迎关注专栏:Java架构技术进阶.里面有大量batj面试题集锦,还有各种技术分享,如有好文章也欢迎投稿哦.微信公众号:慕容千语的架构笔记.欢迎关注一起进步. 前言 前面讲过使用synchronize ...

  8. java闭锁_【Java并发编程三】闭锁

    1.什么是闭锁? 闭锁(latch)是一种Synchronizer(Synchronizer:是一个对象,它根据本身的状态调节线程的控制流.常见类型的Synchronizer包括信号量.关卡和闭锁). ...

  9. java并发编程(三十五)——公平与非公平锁实战

    前言 在 java并发编程(十六)--锁的七大分类及特点 一文中我们对锁有各个维度的分类,其中有一个维度是公平/非公平,本文我们来探讨下公平与非公平锁. 公平|非公平 首先,我们来看下什么是公平锁和非 ...

最新文章

  1. 移动端最强适配(rem适配之px2rem) 移动端结合Vuex实现简单loading加载效果
  2. 键盘各键对应的ASCII码值
  3. UA MATH524 复变函数14 Laurent级数
  4. 16岁成为全栈开发者:我从开发游戏到写加密货币投资机器人的心路历程
  5. 课程设计---图书登记管理系统
  6. 当null检查非常失败时
  7. 考试用计算机反思800字,考试反思作文800字
  8. jq绑定的事件不生效
  9. 经典SQL回顾之晋级篇
  10. Selenium自动化测试IE
  11. C++ and Java template class and function 模板类和模板函数
  12. ubuntu查看进程和结束进程
  13. 美丽的窗花java分形_“高冷奇葩”原来冰窗花可以这么美
  14. as 插件GsonFormat用法(json字符串快速生成javabean)
  15. PDA平台上MessageBox和SIP的冲突
  16. linux下单网卡设双置IP
  17. office 2010-初次接触
  18. 程序员史诗级必读书单吐血整理四个维度系列80+本书(珍藏版)
  19. yolo 深度学习_YoLo v1-v3深度学习网络-结构简介
  20. 安卓Web Service实现天气预报功能

热门文章

  1. java 技术点_Java的21个技术点和知识点归纳
  2. pytorch教程龙曲良26-30
  3. 窗体间传值和窗体间互操作
  4. Java中关于枚举的7种用法
  5. linux下PHP7环境搭建
  6. Spring学习之AOP(面向切面编程)
  7. 成功更新至Win8.1 update
  8. ORACLE开发:创建与管理表空间和数据文件1
  9. 我的醉驾拘留15日记----第二日凌晨 午夜惊梦
  10. URL(统一资源定位符)