当前位置: 首页 > news >正文

【JavaEE初阶】网络编程TCP协议实现回显服务器以及如何处理多个客户端的响应

前言

🌟🌟本期讲解关于TCP/UDP协议的原理理解~~~

🌈感兴趣的小伙伴看一看小编主页:GGBondlctrl-CSDN博客

🔥 你的点赞就是小编不断更新的最大动力                                       

🎆那么废话不多说直接开整吧~~

 

目录

📚️1.TCP相关API

📚️2.回显服务器

2.1概念

2.2服务器的实现

1.初始化Socket类对象

2.启动连接服务器

 3.读取连接的阻塞

4.数据的响应的返回

5.缓冲区的刷新

2.3客户端的实现

1.初始化Socket类对象

2.启动客户端并阻塞

3.发送请求和接收响应

4.文件流close的操作

📚️3.处理多个客户端同时响应

3.1启动多个服务器

3.2处理多客户端请求

1.问题现象

2.问题分析

3.问题解决

4.方法扩展

📚️4.总结

 

📚️1.TCP相关API

 和前一期的UDP基本是大差不差的,但是这里提供的方法来模拟对于网卡的操作是有一定的区别的,所示API如下:

ServerSocket是Socket类对应到网卡给服务器使用的类
Socket对应到网卡,是给服务器或者客户端来进行使用的

而我们知道在UDP的使用中有DatagramPacket是用于在传输过程中的数据传送的单位,即“面向数据包”,但是这里是没有具体特有的数据传送的类的

注意:由于TCP是一个面向字节流的协议,所以使用的仍然是文件IO部分的操作字节流;

inputstream读数据(字节为单位)
outputstream写数据(字节为单位)

所以有了这些铺垫我们就可以使用TCP来实现一个回显服务器了;

📚️2.回显服务器

2.1概念

回显服务器:所谓的回显服务器就是当客户端发送一个请求之后,服务器就直接返回这个响应,在对于请求的解析和操作中是没有任何的逻辑的;(总之就是用户输入什么就得到什么~~

2.2服务器的实现

1.初始化Socket类对象

这里和UDP的初始化几乎是一样的,即如下代码所示:

public class TcpEchoServer {private ServerSocket serverSocket = null;public TcpEchoServer(int port) throws IOException {serverSocket = new ServerSocket(port);  //操作模拟网卡的端口号}

这里就是通过erverSocket类来实现一个对象,达到模拟控制网卡的操作,用于数据的传输,其中这里的port就是一个服务器的端口号;

2.启动连接服务器

在上述的初始化过后,我们就可直接启动服务器了,代码所示:

 public void start() throws IOException {System.out.println("服务器启动了");//进入循环while (true) {//建立连接Socket Clientsocket = serverSocket.accept();processClient(Clientsocket);}}

解释:当我们启动服务器之后,这里就要进行服务器与客户端的连接,为啥要进行连接呢,主要是因为TCP是一个有连接的协议,这就类似于打电话一样,两边要接听后才能够进行通信,然后将数据传给另一个方法操作;

如下图所示:

此时应用程序中调用对应的API来尝试和服务器建立连接,然后内核态就会尝试发起建立连接的流程,然后服务器这边的内核态就会配合进行连接;

注意:内核发起连接是用户程序来进行操作的,所以这里就要调用accept来进行连接;

 3.读取连接的阻塞

当客户端和服务器建立连接,传入数据进行操作时,此时服务器就会进入阻塞状态,那么就有一下代码来进行实现:

private void processClient(Socket Clientsocket) { //处理连接来的数据System.out.printf("[%s:%d] 客户端上线!\n", Clientsocket.getInetAddress(), Clientsocket.getPort());try (InputStream inputStream = Clientsocket.getInputStream();OutputStream OutputStream = Clientsocket.getOutputStream()) {//循环读取客户端的请求并且进行响应while (true) {Scanner scanner = new Scanner(inputStream);if (!scanner.hasNext()) {System.out.printf("[%s:%d] 客户端下线!\n", Clientsocket.getInetAddress(), Clientsocket.getPort());break;}

解释:

由于TCP的面向字节流,所以我们可以通过inputstream来实现这里的操作,此时将这里的操作写到try里是为了自动执行close的关闭文件流的操作;

通过scanner来读取字节数据,然后通过scanner.hashnext来实现没有输入时就进行跳出循环操作,这里就是客户端下线了;

4.数据的响应的返回

在这里通过字节数据的请求操作实现对于客户端的响应,代码如下:

String request = scanner.next();//进行响应操作String response = process(request);//将响应传给客户端//给outputstream进行外包装PrintWriter printWriter = new PrintWriter(OutputStream);printWriter.println(response);

解释:这里的process操作就是直接返回需求作为响应,然后这里小编就通过printwriter来包装了outputstream这个写数据的操作,就是替代了这个代码:

OutputStream.write(response.getBytes(),0,response.getBytes().length)

这里两个的区别:
OutputStream :你需要将字符串手动转换为字节数组发送,例如;

String response = "收到你的消息";
byte[] responseBytes = response.getBytes();
outputStream.write(responseBytes);


PrintWriter :它提供了更方便的 print 和 println 方法,可以直接发送字符串;
 
 PrintWriter 会自动处理字符编码等细节,并且在构造函数的第二个参数传入 true 时能够自动刷新缓冲区,确保消息及时发送。这使得代码更加简洁易读,减少了因字节处理而可能产生的错误。

5.缓冲区的刷新

这里是printwriter提供的缓冲区在这里面进行了操作,解决代码如下:

 printWriter.flush();//刷新缓冲区,让数据发送出去

解释:这就是刷新缓冲区的意思,为啥要刷新缓冲区呢???

注意:这里的IO操作是一个比较低效的操作,所以就会尽量减少对于文件IO的操作,所以要操作网卡的数据存放到内存缓冲区里,当积攒到一定的量后再发给下一层协议

 所以当数据太少的时候,就会存在缓冲区里,并没有发送出去,所以这里要进行刷新的操作;

2.3客户端的实现

1.初始化Socket类对象

和上面的服务器初始化是一致的,只不过使用的类不一样,代码如下:

public class TcpEchoClient {private Socket socket=null;public TcpEchoClient(String ServerIP, int ServerPort) throws IOException {//由于tcp是有连接的,所以会自动保存这里的ip和端口号socket=new Socket(ServerIP,ServerPort);}

解释:这里通过socket对象,实现对于网卡的模拟操作,在构造函数的时候定义服务器的IP地址以及服务器的端口号;

2.启动客户端并阻塞

这里在启动客户端后直接进入循环,进行不断的从服务器读取响应,代码如下:

public void start(){System.out.println("客户端启动了");try (InputStream inputStream=socket.getInputStream();OutputStream outputStream= socket.getOutputStream()){Scanner scannerConsole=new Scanner(System.in);Scanner scannernetwork=new Scanner(inputStream);PrintWriter writer=new PrintWriter(outputStream);while (true){System.out.println("->");if(!scannerConsole.hasNext()){break;}

解释:这里还是通过inputstream和outputstream来进行操作,这里的两个scanner分别的用途如下所示;

第一个scanner是用于客户在控制台上进行字符串的输入;

第二个scanner是用于字节数据的读取,就是从服务器响应过后的数据接收;

第三个printwriter用于写数据给服务器,这里就是发送请求的意思

之后进入用户的输入阻塞,当不输入时,就直接跳出循环,客户端下线;

3.发送请求和接收响应

当执行上述步骤之后,我们就要执行对于服务器数据的发送请求和接收响应的操作了,代码如下:

 String request=scannerConsole.next();//发送数据用到写的操作writer.println(request);writer.flush();//接收数据String response=scannernetwork.next();System.out.println(response);

解释:这里将用户输入的请求通过writer写给服务器,并刷新了缓存,保证字节数组能够发送出去,最后通过scannernetwork来接收数据,并转化为字符串类型数据,最后在打印即可;

4.文件流close的操作

1.serversocket

解释:由于整个程序中只有一个serversocket对象,并且这个对象的生命周期很长,随着服务器的退出自动销毁,所以不需要进行close操作;

2.clientSocket

解释:由于clientsocket是每个客户端都有一个,由于连接的客户端越来越多,不释放socket就会导致将文件描述附表占满,所以这里要进行close的操作;

代码如下:

finally {try {clientSocket.close();} catch (IOException e) {throw new RuntimeException(e);}}

这里就添加在服务器try-catch的后面即可~~~

📚️3.处理多个客户端同时响应

3.1启动多个服务器

当我们执行代码,启动多个服务器的时候会发现此时idea会终止这个原来的进程,然后执行新的代码,即新的进程,那么解决办法如下所示:

点击后进入如下的画面,然后进入一个新的界面点击如下:

然后这里代表的就是允许多个实例的运行,那么就可以重复执行代码,实现多个服务器同时运行的实现;

3.2处理多客户端请求

1.问题现象

此时当我们对第一个客户端进行输入的时候,发现此时服务器对于客户端是有响应的,如下图所示:

此时是有客户端输入后,会得到响应的,但是此时我们对于第二个客户端进行打印的时候,这里是没有出现响应的:

此时我们可以看见服务器对于两个客户端的上线状态也是不一样的,如下图所示:

很明显这里就是只上线了一个客户端,那么这就是第二个客户端得不到响应的原因;

2.问题分析

流程:首先这里的服务器主循环是通过clientsocket来进行数据连接,然后再进入数据操作的循环,即有以下几个步骤:

1.读取请求并且进行解析;

2.对于解析做出响应;

3.将响应传回给客户端;

注意:这是一个死循环,只要这个循环不结束(即连接这个服务器的第一个客户端不结束)那么就会导致服务器一直在这个循环等待客户端1号的请求,并做出响应;

虽然这里第二个客户端实现了内核上运用accept与服务器建立了连接,但是无法将连接拿到程序里进行处理,这就是整个多客户端 请求不成功的主要原因;

3.问题解决

使用多线程

对这个processClient(Clientsocket)来进行多个线程处理多个客户端的请求与响应,具体代码如下所示:

while (true) {//建立连接Socket Clientsocket = serverSocket.accept();Thread t=new Thread(()->{processClient(Clientsocket);});t.start();}

解释:那么此时当申请一个客户端的时候,那么就会创建一个线程来对这个客户端进行服务,此时就解决了多客户端请求的问题;

使用线程池

由于上述的操作,会导致一个客户端执行,就会创建一个线程,一个客户端执行完了,就会销毁一个线程,那么此时就会造成线程频繁创建销毁的开销增大;

那么这里就引入了线程池,这个概念,具体代码如下:

 while (true) {//建立连接Socket Clientsocket = serverSocket.accept();ExecutorService pool= Executors.newCachedThreadPool();pool.submit(new Runnable() {@Overridepublic void run() {processClient(Clientsocket);}});}

 解释:那么此时当创建好线程后,客户端执行,那么就会从线程池中拿一个线程进行服务客户端,当客户端执行结束后,将线程入到线程池,就不会销毁,节省了线程创建的开销;

4.方法扩展

引入协程

这里的协程就是轻量级线程,用户态可以手动的调度这个协程,并发的执行多个客户端;那么此时由于协程的创建和销毁是用户态进行手动控制的,所以就省去了系统内核的调度开销;

IO多路复用

IO多路复用:这里就是一个系统内核级别的机制,主要的内容机制就是一个线程同时负责多个socket的处理;

本质:即每个socket需要操作的数据不是同一时间处理的;

举例:假如我去买街上买吃的,我可以点好餐后,等待后,拿到餐了,那么去买另一个东西;那么我也可以等买完餐后直接去买另一个东西,此时在等这两个东西完成后,再去拿;这里的本质就是每个东西的不是同一个时间执行的;

📚️4.总结

💬💬本期小编主要讲解了关于TCP实现回显服务器的操作过程中,服务器的操作,客户端的操作;以及如何处理多个客户端的同时响应,并进行了问题的多方解决~~~

🌅🌅🌅~~~~最后希望与诸君共勉,共同进步!!!


💪💪💪以上就是本期内容了, 感兴趣的话,就关注小编吧。

                             😊😊  期待你的关注~~~

相关文章:

【JavaEE初阶】网络编程TCP协议实现回显服务器以及如何处理多个客户端的响应

前言 🌟🌟本期讲解关于TCP/UDP协议的原理理解~~~ 🌈感兴趣的小伙伴看一看小编主页:GGBondlctrl-CSDN博客 🔥 你的点赞就是小编不断更新的最大动力 🎆那么废话不多说…...

Android 中的串口开发

一:背景 本文着重讲安卓下的串口。 由于开源的Android在各种智能设备上的使用越来越多,如车载系统等。在我们的认识中,Android OS的物理接口一般只有usb host接口和耳机接口,但其实安卓支持各种各样的工业接口,如HDM…...

TensorRt OP

在TensorRT中,OP(Operations,操作)是指网络中的基本计算单元,类似于数学中的运算符。每个OP执行一个特定的计算任务,例如卷积、矩阵乘法、激活函数等。TensorRT通过识别和优化这些OP来提高深度学习模型的推…...

构建负责任的人工智能:数据伦理与隐私保护

构建负责任的人工智能:数据伦理与隐私保护 目录 🌟 数据伦理的重要性📊 公平性评估:实现无偏差的模型🔒 数据去标识化:保护用户隐私的必要手段🔍 透明性与问责:建立可信的数据处理…...

微信小程序live-pusher和video同时使用,video播放声音时时大时小

一、遇到的问题 微信小程序live-pusher和video同时使用,video播放声音时有时无时大时小 二、排查流程 业务是模拟面试,每道题一个推流live-pusher和一个面试题video,一次面试有多道面试题,页面就一个live-pusher和一个video,切换面试题时给live-pusher和video重新赋值u…...

MySQL 分库分表实战

在当今互联网时代,数据量的增长呈爆炸式趋势,传统的单库单表架构已经难以满足大规模数据存储和高并发访问的需求。MySQL 分库分表技术应运而生,它可以有效地提高数据库的性能、扩展性和可用性。本文将详细介绍 MySQL 分库分表的实战经验。 一…...

MySQL—CRUD—进阶—(二) (ಥ_ಥ)

文本目录: ❄️一、新增: ❄️二、查询: 1、聚合查询: 1)、聚合函数: 2)、GROUP BY子句: 3)、HAVING 子句: 2、联合查询: 1)、内连接…...

时序分解 | TTNRBO-VMD改进牛顿-拉夫逊算法优化变分模态分解

时序分解 | TTNRBO-VMD改进牛顿-拉夫逊算法优化变分模态分解 目录 时序分解 | TTNRBO-VMD改进牛顿-拉夫逊算法优化变分模态分解效果一览基本介绍程序设计参考资料 效果一览 基本介绍 (创新独家)TTNRBO-VMD改进牛顿-拉夫逊优化算优化变分模态分解TTNRBO–VMD 优化VMD分解层数K和…...

2024“源鲁杯“高校网络安全技能大赛-Misc-WP

Round 1 hide_png 题目给了一张图片,flag就在图片上,不过不太明显,写个python脚本处理一下 from PIL import Image ​ # 打开图像并转换为RGB模式 img Image.open("./attachments.png").convert("RGB") ​ # 获取图像…...

CSS行块标签的显示方式

块级元素 标签:h1-h6,p,div,ul,ol,li,dd,dt 特点: (1)如果块级元素不设置默认宽度,那么该元素的宽度等于其父元素的宽度。 (2)所有的块级元素独占一行显示. (3&#xff…...

Go 语言中的 for range 循环教程

在 Go 语言中,for range 循环是一个方便的语法结构,用于遍历数组、切片、映射和字符串。本教程将通过示例代码来帮助理解如何在 Go 中使用 for range 循环。 package mainimport "fmt"func main() {// 遍历切片并计算和nums : []int{2, 3, 4}…...

青训营 X 豆包MarsCode 技术训练营--小M的比赛胜场计算

问题描述 小M参加了一场n个人的比赛,比赛规则是所有选手两两对决。每个人有一个能力值,对应着他们的序号。参赛者同时被分为黄色或蓝色两种颜色。比赛胜负的规则如下: 当比赛双方颜色不同时,能力值大的选手获胜; 当比…...

海王3纯源码

海王3是一款热门的捕鱼类游戏,其纯源码为开发者提供了一个完整的游戏开发基础。该源码包括客户端和服务端的完整架构,支持多人在线竞技模式和丰富的游戏玩法。服务端采用C语言编写,并使用MySQL数据库来存储玩家数据,确保数据处理的…...

【ShuQiHere】Linux 系统中的硬盘管理详解:命令与技巧

【ShuQiHere】 💽 在 Linux 系统中,硬盘管理不仅仅是存储数据的操作,更涉及系统性能、数据安全和稳定性的优化。无论你是系统管理员、开发者还是 Linux 爱好者,掌握硬盘管理的基础操作都非常有用。本文将从硬盘健康检查、分区管理…...

数据结构之堆和二叉树的简介

1.树 1.1 树的概念与结构 如图所示,树是⼀种非线性的数据结构,它是由 n (n>0) 个有限结点组成⼀个具有层次关系的集合。把它叫做树是因为它看起来像一棵倒挂的树,也就是说它是根朝上,而叶朝下的。 …...

微信小程序上传图片添加水印

微信小程序使用wx.chooseMedia拍摄或从手机相册中选择图片并添加水印&#xff0c; 代码如下&#xff1a; // WXML代码&#xff1a;<canvas canvas-id"watermarkCanvas" style"width: {{canvasWidth}}px; height: {{canvasHeight}}px;"></canvas&…...

xshell5找不到匹配的host key算法

xshell5找不到匹配的host key算法&#xff0c;是因为电脑客户端不支持服务器的算法&#xff0c;因此需要再服务器增加算法。 下面以Ubuntu系统为例&#xff0c;修改下面的文件 sudo vim /etc/ssh/sshd_config 增加下面算法 KexAlgorithms diffie-hellman-group-exchange-…...

Linux中安装Tomcat

文章目录 一、Tomcat介绍1.1、Tomcat是什么1.2、Tomcat的工作原理1.3、Tomcat适用的场景1.4、Tomcat与Nginx、Apache比较1.4.1、优势1.4.2、劣势1.4.3、定位功能 1.5、Tomcat 的主要组件1.6、Tomcat 的主要配置文件 二、Tomcat安装2.1、查看可用的JDK2.2、安装OpenJDK 112.3、配…...

RV1126音视频学习(二)-----VI模块

文章目录 前言2.RV1126的视频输入vi模块2.1什么是VI模块2.3RV1126VI模块主要APIRK_MPI_SYS_Init()RK_MPI_VI_SetChnAttrRK_MPI_VI_EnableChnRK_S32 RK_MPI_VI_DisableChnRK_MPI_VI_StartStreamRK_MPI_SYS_GetMediaBufferRK_MPI_MB_GetPtrRK_MPI_MB_GetSizeRK_MPI_MB_ReleaseBuf…...

「C/C++」C++17 之 std::string_view 轻量级字符串视图

✨博客主页何曾参静谧的博客&#x1f4cc;文章专栏「C/C」C/C程序设计&#x1f4da;全部专栏「VS」Visual Studio「C/C」C/C程序设计「UG/NX」BlockUI集合「Win」Windows程序设计「DSA」数据结构与算法「UG/NX」NX二次开发「QT」QT5程序设计「File」数据文件格式「PK」Parasoli…...

JavaSec-RCE

简介 RCE(Remote Code Execution)&#xff0c;可以分为:命令注入(Command Injection)、代码注入(Code Injection) 代码注入 1.漏洞场景&#xff1a;Groovy代码注入 Groovy是一种基于JVM的动态语言&#xff0c;语法简洁&#xff0c;支持闭包、动态类型和Java互操作性&#xff0c…...

Linux 文件类型,目录与路径,文件与目录管理

文件类型 后面的字符表示文件类型标志 普通文件&#xff1a;-&#xff08;纯文本文件&#xff0c;二进制文件&#xff0c;数据格式文件&#xff09; 如文本文件、图片、程序文件等。 目录文件&#xff1a;d&#xff08;directory&#xff09; 用来存放其他文件或子目录。 设备…...

前端倒计时误差!

提示:记录工作中遇到的需求及解决办法 文章目录 前言一、误差从何而来?二、五大解决方案1. 动态校准法(基础版)2. Web Worker 计时3. 服务器时间同步4. Performance API 高精度计时5. 页面可见性API优化三、生产环境最佳实践四、终极解决方案架构前言 前几天听说公司某个项…...

解锁数据库简洁之道:FastAPI与SQLModel实战指南

在构建现代Web应用程序时&#xff0c;与数据库的交互无疑是核心环节。虽然传统的数据库操作方式&#xff08;如直接编写SQL语句与psycopg2交互&#xff09;赋予了我们精细的控制权&#xff0c;但在面对日益复杂的业务逻辑和快速迭代的需求时&#xff0c;这种方式的开发效率和可…...

css的定位(position)详解:相对定位 绝对定位 固定定位

在 CSS 中&#xff0c;元素的定位通过 position 属性控制&#xff0c;共有 5 种定位模式&#xff1a;static&#xff08;静态定位&#xff09;、relative&#xff08;相对定位&#xff09;、absolute&#xff08;绝对定位&#xff09;、fixed&#xff08;固定定位&#xff09;和…...

IT供电系统绝缘监测及故障定位解决方案

随着新能源的快速发展&#xff0c;光伏电站、储能系统及充电设备已广泛应用于现代能源网络。在光伏领域&#xff0c;IT供电系统凭借其持续供电性好、安全性高等优势成为光伏首选&#xff0c;但在长期运行中&#xff0c;例如老化、潮湿、隐裂、机械损伤等问题会影响光伏板绝缘层…...

嵌入式学习笔记DAY33(网络编程——TCP)

一、网络架构 C/S &#xff08;client/server 客户端/服务器&#xff09;&#xff1a;由客户端和服务器端两个部分组成。客户端通常是用户使用的应用程序&#xff0c;负责提供用户界面和交互逻辑 &#xff0c;接收用户输入&#xff0c;向服务器发送请求&#xff0c;并展示服务…...

uniapp手机号一键登录保姆级教程(包含前端和后端)

目录 前置条件创建uniapp项目并关联uniClound云空间开启一键登录模块并开通一键登录服务编写云函数并上传部署获取手机号流程(第一种) 前端直接调用云函数获取手机号&#xff08;第三种&#xff09;后台调用云函数获取手机号 错误码常见问题 前置条件 手机安装有sim卡手机开启…...

【SpringBoot自动化部署】

SpringBoot自动化部署方法 使用Jenkins进行持续集成与部署 Jenkins是最常用的自动化部署工具之一&#xff0c;能够实现代码拉取、构建、测试和部署的全流程自动化。 配置Jenkins任务时&#xff0c;需要添加Git仓库地址和凭证&#xff0c;设置构建触发器&#xff08;如GitHub…...

Python 高效图像帧提取与视频编码:实战指南

Python 高效图像帧提取与视频编码:实战指南 在音视频处理领域,图像帧提取与视频编码是基础但极具挑战性的任务。Python 结合强大的第三方库(如 OpenCV、FFmpeg、PyAV),可以高效处理视频流,实现快速帧提取、压缩编码等关键功能。本文将深入介绍如何优化这些流程,提高处理…...