java实现基于TCP协议网络socket编程(C/S通信)
作者:Charzous 发布时间:2021-10-15 23:41:26
一、前言:TCP原理简介
首先,保证文章完整性,TCP的理论原理还是需要简介一下,略显枯燥๑乛◡乛๑。
TCP(传输控制协议,Transmission Control Protocol)是一种面向连接的、可靠的、基于字节流的传输层通信协议。TCP旨在适应支持多网络应用的分层协议层次结构。也就是说,TCP是为了在不可靠的互联网络上提供可靠的端到端字节流而专门设计的一个传输协议。 连接到不同但互连的计算机通信网络的主计算机中的成对进程之间依靠TCP提供可靠的通信服务。
以上TCP的特点,也正是与UDP的明显不同之处。UDP(用户数据报协议)是一种无连接的、不可靠的、不以字节流传输通信协议。具体区别可对比之前这篇文章:
【基于UDP协议网络Socket编程(java实现C/S通信案例) 】 [https://www.jb51.net/article/198498.htm]
接着,“三次握手”则是众所周知的一个词,是建立TCP连接的重要过程。许多文章有详细解读,本篇则是详细记录在此原理之上,使用Java实现TCP的Socket网络通信,包含C/S软件架构的程序设计,偏向实践,更加有趣!
二、Socket编程通信
本篇使用Java进行Socket编程,Java的TCP/IP套接字编程将底层的细节进行了封装,其编程模型如图:
我们自顶向下观察,基于TCP的通信,必然有服务端Server和客户端Client。
首先,建立连接。两端分别有一个套接字Socket,用于两者之间的通信。客户端向服务器发送请求,创建socket进行连接。服务端则随时监听客户端发起的请求,接收并创建裂解Socket。
其次,开始通信。服务和客户两端的输入输出流互相通信。逻辑上可理解为通信进程的双方具有两个流(输出流和输入流)。逻辑上可将两个流理解为两个通信管道的全双工通信模式,一个用于向对方发送数据,另一个用于接收对方的数据。
最后,结束通信。客户端访问服务器结束,断开连接,关闭Socket和相关资源(输入输出流等)。服务端监听客户端状态,同时关闭Socket等连接。
建立通信规则:
Server和Client之间需要约定相同的规则,保证正常通信。之后的程序设计,我们约定:
客户端连接服务器,连接成功后,服务器首先给客户端发送一条欢迎信息;
客户端程序每发送一条信息给服务器,服务器接收并回送该信息到客户端,客户端接收并显示该信息;
当客户端发送"bye",则结束对话。
三、TCP服务器端(具体代码)
第一步,创建服务端套接字。
类成员变量:ServerSocket serverSocket,监听端口号port;
private int port =8008;//服务器监听窗口
private ServerSocket serverSocket;//定义服务器套接字
public TCPServer() throws IOException{
serverSocket =new ServerSocket(port);
System.out.println("服务器启动监听在"+port+"端口...");
}
第二步,定义输入输出流方法:
private PrintWriter getWriter(Socket socket) throws IOException{
//获得输出流缓冲区的地址
OutputStream socketOut=socket.getOutputStream();
//网络流写出需要使用flush,这里在printWriter构造方法直接设置为自动flush
return new PrintWriter(new OutputStreamWriter(socketOut,"utf-8"),true);
}
private BufferedReader getReader(Socket socket) throws IOException{
//获得输入流缓冲区的地址
InputStream socketIn=socket.getInputStream();
return new BufferedReader(new InputStreamReader(socketIn,"utf-8"));
}
第三步,服务端核心:
//单客户版本,每次只能与一个用户建立通信连接
public void Service(){
while (true){
Socket socket=null;
try {
//此处程序阻塞,监听并等待用户发起连接,有连接请求就生成一个套接字
socket=serverSocket.accept();
//本地服务器控制台显示客户连接的用户信息
System.out.println("New connection accepted:"+socket.getInetAddress());
BufferedReader br=getReader(socket);//字符串输入流
PrintWriter pw=getWriter(socket);//字符串输出流
pw.println("来自服务器消息:欢迎使用本服务!");
String msg=null;
//此处程序阻塞,每次从输入流中读入一行字符串
while ((msg=br.readLine())!=null){
//如果用户发送信息为”bye“,就结束通信
if(msg.equals("bye")){
pw.println("来自服务器消息:服务器断开连接,结束服务!");
System.out.println("客户端离开。");
break;
}
pw.println("来自服务器消息:"+msg);
}
}catch (IOException e){
e.printStackTrace();
}finally {
try {
if (socket!=null)
socket.close();//关闭socket连接以及相关的输入输出流
}catch (IOException e){
e.printStackTrace();
}
}
}
}
代码关键解析很清楚易懂。可以看到,服务端提供服务放到了一个While(true)里面,这是因为服务器程序需要一直运行,所以处理代码一般放在while(true)这种无限循环中,TCPServer运行一次,且自身不能终止运行,要终止它运行,只能通过强制方式(如在IDE环境强制关闭)。
四、TCP客户端(具体代码)
第一步,创建客户端套接字,定义类构造方法,实现输入输出流。
//单客户版本,每次只能与一个用户建立通信连接
public void Service(){
while (true){
Socket socket=null;
try {
//此处程序阻塞,监听并等待用户发起连接,有连接请求就生成一个套接字
socket=serverSocket.accept();
//本地服务器控制台显示客户连接的用户信息
System.out.println("New connection accepted:"+socket.getInetAddress());
BufferedReader br=getReader(socket);//字符串输入流
PrintWriter pw=getWriter(socket);//字符串输出流
pw.println("来自服务器消息:欢迎使用本服务!");
String msg=null;
//此处程序阻塞,每次从输入流中读入一行字符串
while ((msg=br.readLine())!=null){
//如果用户发送信息为”bye“,就结束通信
if(msg.equals("bye")){
pw.println("来自服务器消息:服务器断开连接,结束服务!");
System.out.println("客户端离开。");
break;
}
pw.println("来自服务器消息:"+msg);
}
}catch (IOException e){
e.printStackTrace();
}finally {
try {
if (socket!=null)
socket.close();//关闭socket连接以及相关的输入输出流
}catch (IOException e){
e.printStackTrace();
}
}
}
}
第二步,实现网络通信发送和接收方法。
public void send(String msg){
//输出字符流,由socket调用系统底层函数,经网卡发送字节流
pw.println(msg);
}
public String receive(){
String msg=null;
try {
//从网络输入字符流中读取信息,每次只能接受一行信息
//不够一行时(无行结束符),该语句阻塞
//直到条件满足,程序往下运行
msg=br.readLine();
}catch (IOException e){
e.printStackTrace();
}
return msg;
}
第三步,定义网络连接关闭方法供外部调用。
public void close(){
try {
if (socket!=null)
socket.close();
}catch (IOException e){
e.printStackTrace();
}
}
TCP连接的释放也有“四次握手”一说,必须经过2MSL后才真正释放。具体过程如下图:
五、通信效果演示
GIF动图演示:
六、“创意”机器人:价值一个亿的AI核心代码(具体代码)
这部分我们要实现“聊天机器人”,效果这样:
是不是迫不及待想知道如何实现呢!堪称“价值一个亿的AI核心代码”!!??
就这样实现了!
不卖关子了,就一行代码!
msg=msg.replace("?","!").replace("?","!").replace("吗","").replace("吗?","");
具体想实现机器人如何回复可以自行调整代码。
七、最后
本篇则是详细记录在此原理之上,使用Java实现TCP的Socket网络通信,包含C/S软件架构的程序设计,偏向实践,更加有趣!仔细阅读的朋友可以发现,在服务器端核心部分,有一行注释说明了该程序只支持单用户,也就是单线程通信,可以尝试一下,如果再开一个客户端连接该服务,是否因为单线程阻塞程序卡住了。
这个问题关键就在于:服务器和客户端互相约定通信规则,否则就可能有问题,例如,如果服务器在一个客户端连接成功后,并没有一条信息发送给客户端,客户端的读取欢迎信息的语句无法读取到内容,就被阻塞住,由于是单线程,甚至整个程序都会被卡住。要解决这个问题,等待更新下一篇!
另外,UI界面的设计可参考上一篇博客:【基于UDP协议网络Socket编程(java实现C/S通信案例) 】 [https://www.jb51.net/article/198498.htm]
来源:https://blog.csdn.net/Charzous/article/details/109260488


猜你喜欢
- 首先从字面意思理解两个词onTouchEvent:触发触摸事件onInterceptTouchEvent:触发拦截触摸事件通过查看源代码及类
- 1.1 JDK 14详细概述JDK 8 已经在 2014年 3月 18日正式可用,JDK 8作为长期支持(Long-Term-Support
- 1.面向接口编程和面向对象编程是什么关系首先,面向接口编程和面向对象编程并不是平级的,它并不是比面向对象编程更先进的一种独立的编程思想,而是
- 本文实例讲述了Android开发中应用程序分享功能。分享给大家供大家参考,具体如下:Intent shareIntent = new Int
- 本文实例为大家分享了Java通过JavaMail发送邮件的具体代码,供大家参考,具体内容如下这里我用的是SpringBoot项目1、引入相关
- 借用@Caching实现入参是基本类型的:@Caching(evict={@CacheEvict(value = Cache.CONSTAN
- 欢迎访问我的GitHub这里分类和汇总了欣宸的全部原创(含配套源码):https://github.com/zq2599/blog_demo
- 大家好,这是 [C#.NET 拾遗补漏] 系列的第 07 篇文章。在 C# 中,大多数方法都是通过 return 语句立即把程序的控制权交回
- Java多线程下载网图案例此案例依赖——文件操作工具类(FileUtils)使用 apache 的commons-io包下的FileUtil
- 1. 支持的视图技术前端模板引擎技术的出现,使前端开发人员无需关注后端业务的具体实现,只关注自己页面的呈现效果即可,并且解决了前端代码错综复
- 前言最近遇到很有意思转换二进制的问题,有部分童鞋俨然已了解,可能也有一部分童鞋没碰到过也就不知情,这里我们来深入学习下转换二进制所带来的问题
- 前言Kotlin一个强大之处就在于它的扩展函数,巧妙的运用这些扩展函数可以让你写出的代码更加优雅,阅读起来更加流畅,下面总结了在开发中经常用
- 一、负载均衡负载均衡(Load Balance): 建立在现有网络结构之上,它提供了一种廉价有效透明的方法扩展网络设备和服务器的带宽、增加吞
- 解决方法:maven的配置文件<mirror> <id>aliyunmaven</id> <mir
- 一段看似平平无奇的代码Classroom classroom = this.getOne( Wrappers.<Class
- java 回调机制的实例详解序言最近接触到了回调机制(CallBack)。初识时感觉比较混乱,而且在网上搜索到的相关的讲解,要么一言带过,要
- 关联篇:HandlerThread 使用及其源码完全解析关联篇:Handler内存泄漏详解及其解决方案一说到Android的消息机制,自然就
- 1、Dom4j概述dom4j is an easy to use, open source library for working with
- 一、编译步骤解压下载的GDAL源程序,并在命令行中切换到解压目录。tar -xzvf gdal-2.1.3.tar.gzcd gdal-2.
- 1. Mybatis分页插件1.1 分页插件介绍分页可以将很多条结果进行分页显示。如果当前在第一页,则没有上一页。如果当前在最后一页,则没有