JAVA通信编程(三)——TCP通讯
欢迎支持笔者新作:《深入理解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通讯相关推荐
- Java网络编程:TCP实现群聊私聊代码
Java网络编程:TCP实现群聊&私聊代码 和上一篇博客差不多,只不过是在群里的基础之上增加了私聊的功能,我们约定,私聊格式为:@xxx:msg 如何实现私聊呢,加入客户端c给服务器发送消息, ...
- JAVA通信编程(一)——串口通讯
欢迎支持笔者新作:<深入理解Kafka:核心设计与实践原理>和<RabbitMQ实战指南>,同时欢迎关注笔者的微信公众号:朱小厮的博客. 欢迎跳转到本文的原文链接:https: ...
- Java网络编程:TCP,UDP,sock编程
第一节 网络基础知识 常用的通信协议 MAC地址:(Media Access Control Address,媒体存取控制位址), IP:(Internet Protocol,网际协议) UDP:(U ...
- java 网络编程 UDP TCP
网络编程 网络编程主要用于解决计算机与计算机(手机.平板..)之间的数据传输问题. 网络编程: 不需要基于html页面就可以达到数据之间的传输. 比如: feiQ , QQ , 微信.... 网页编程 ...
- flash java 通信_FLASH与服务器通讯 (JAVA)
1.FLASH 通过URL地址获得服务器数据. 这种方式最为简单,就像在浏览器的地址栏里面敲一样. 先建立一个URLRequest,然后用URLLoader载入就行了. 下面这个是载入一个图片,htm ...
- Java——网络编程三要素
* A:计算机网络* 是指将地理位置不同的具有独立功能的多台计算机及其外部设备,通过通信线路连接起来,在网络操作系统.网络管理软件及网络通信协议的管理和协调下,实现资源共享和信息传递的计算机系统.* ...
- 进阶笔记——java并发编程三特性与volatile
欢迎关注专栏:Java架构技术进阶.里面有大量batj面试题集锦,还有各种技术分享,如有好文章也欢迎投稿哦.微信公众号:慕容千语的架构笔记.欢迎关注一起进步. 前言 前面讲过使用synchronize ...
- java闭锁_【Java并发编程三】闭锁
1.什么是闭锁? 闭锁(latch)是一种Synchronizer(Synchronizer:是一个对象,它根据本身的状态调节线程的控制流.常见类型的Synchronizer包括信号量.关卡和闭锁). ...
- java并发编程(三十五)——公平与非公平锁实战
前言 在 java并发编程(十六)--锁的七大分类及特点 一文中我们对锁有各个维度的分类,其中有一个维度是公平/非公平,本文我们来探讨下公平与非公平锁. 公平|非公平 首先,我们来看下什么是公平锁和非 ...
最新文章
- 移动端最强适配(rem适配之px2rem) 移动端结合Vuex实现简单loading加载效果
- 键盘各键对应的ASCII码值
- UA MATH524 复变函数14 Laurent级数
- 16岁成为全栈开发者:我从开发游戏到写加密货币投资机器人的心路历程
- 课程设计---图书登记管理系统
- 当null检查非常失败时
- 考试用计算机反思800字,考试反思作文800字
- jq绑定的事件不生效
- 经典SQL回顾之晋级篇
- Selenium自动化测试IE
- C++ and Java template class and function 模板类和模板函数
- ubuntu查看进程和结束进程
- 美丽的窗花java分形_“高冷奇葩”原来冰窗花可以这么美
- as 插件GsonFormat用法(json字符串快速生成javabean)
- PDA平台上MessageBox和SIP的冲突
- linux下单网卡设双置IP
- office 2010-初次接触
- 程序员史诗级必读书单吐血整理四个维度系列80+本书(珍藏版)
- yolo 深度学习_YoLo v1-v3深度学习网络-结构简介
- 安卓Web Service实现天气预报功能