【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ÿ…...
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拍摄或从手机相册中选择图片并添加水印, 代码如下: // WXML代码:<canvas canvas-id"watermarkCanvas" style"width: {{canvasWidth}}px; height: {{canvasHeight}}px;"></canvas&…...
xshell5找不到匹配的host key算法
xshell5找不到匹配的host key算法,是因为电脑客户端不支持服务器的算法,因此需要再服务器增加算法。 下面以Ubuntu系统为例,修改下面的文件 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 轻量级字符串视图
✨博客主页何曾参静谧的博客📌文章专栏「C/C」C/C程序设计📚全部专栏「VS」Visual Studio「C/C」C/C程序设计「UG/NX」BlockUI集合「Win」Windows程序设计「DSA」数据结构与算法「UG/NX」NX二次开发「QT」QT5程序设计「File」数据文件格式「PK」Parasoli…...
Linux内核-内核模块内核参数
作者介绍:简历上没有一个精通的运维工程师。希望大家多多关注作者,下面的思维导图也是预计更新的内容和当前进度(不定时更新)。 我们的Linux进阶部分,到目前为止,已经讲过:硬件,日常运维,基础软…...
中电信翼康工程师:我在 Apache SeaTunnel 社区的贡献之旅
贡献者Github ID:luckyLJY 文章整理:曾辉 Apache SeaTunnel 作为一款强大的数据同步和转换工具,凭借其部署易用性、容错机制、数据源支持、性能优势、功能丰富性以及活跃的社区支持,成为了数据工程师们不可或缺的利器。 因其具有的…...
【ESP32S3】VSCode 开发环境搭建
ESP32S3 有多种开发方式,主流的有 Eclipse 和 VSCode 两种。本文来介绍一下基于 VSCode 的开发环境搭建。 VSCode 环境需要依赖于 ESP-IDF 插件,因此需要在 VSCode 插件市场中搜索并安装 ESP-IDF 插件: 安装完成后侧边栏会多出一个 ESP-IDF …...
大模型,多模态大模型面试问题基础记录24/10/24
大模型,多模态大模型面试问题基础记录24/10/24 问题一:LoRA是用在节省资源的场景下,那么LoRA具体是节省了内存带宽还是显存呢?问题二:假如用pytorch完成一个分类任务,那么具体的流程是怎么样的?…...
使用TimeShift备份和恢复Ubuntu Linux
您是否曾经想过如何备份和恢复您的Ubuntu或Debian系统?TimeShift是一个强大的备份和还原工具。TimeShift允许您创建系统快照,提供了一种在出现意外问题或系统故障时恢复到先前状态的简便方式。您可以使用RSYNC或BTRFS创建快照。 有了这个介绍࿰…...
win7现在还能用吗_哪些配置的电脑还可以安装win7系统
2024年了都,win7现在还能用吗?答案是肯定的。那么哪些配置的电脑还可以安装win7系统呢?下面就针对这两个问题详细分区。 win7现在还能用吗? Windows 7系统虽然已经停止官方支持,但仍然可以使用。以下是关于Windows 7系…...
基于GPT的智能客服落地实践
📍前言 在日常生活中,「客服」这个角色几乎贯穿着我们生活的方方面面。比如,淘宝买东西时,需要客服帮你解答疑惑。快递丢失时,需要客服帮忙找回。报名参加培训课程时,需要客服帮忙解答更适合的课程…… 基…...
Sourcetree和GitLab的结合使用
一、写在前面 为什么是Sourcetree和GitLab?因为遇到的坑最少,在不用梯子的情况下,推送速度还可以。 这篇文章主要介绍的是,怎么把自己写的代码文件打包放到GitLab上去,方便别人下载使用,也方便自己在另一…...
双十一开启极速达夜派;黑神话获泰国年度最佳游戏;AI 模型可帮助识别 17000 多种疾病的候选药物....| 网易数智日报
双 11 菜鸟在北京、上海、广州、杭州等城市开启「预售极速达夜派」服务 10 月 21 日,菜鸟在北京、上海、广州、杭州等城市开启「预售极速达夜派」服务,批量大促包裹实现小时级送达。 据介绍,在消费者支付尾款前,菜鸟供应链就已经…...
深入理解JAVA虚拟机(一)
介绍JAVA虚拟机的运行时数据区域 按照物理结构来划分:java虚拟机主要由以下几部分构成栈、堆和程序计数器,其中栈又可以分为虚拟机栈VM stack 和 本地方法栈 Native Method Statck,堆可以划分方法区和普通的堆内存。按照逻辑划分线程私有空间…...
手机wap网站多少钱/美国搜索引擎排名
我已将我的Windows环境从8更新到10,并发现未返回默认打印机,而是返回另一个.我已关闭“让Windows管理我的默认打印机”设置,但仍然没有获得默认打印机.我有以下代码:private void testPrinter() {PrintService[] array PrintServiceLookup.lookupPrintServices(nul…...
公司网站建设方案报告/最近一两天的新闻有哪些
前几天接了一个数据库的单子,就是让写一个管理系统,我用了几天写完了,特此分享一下: 登录界面 管理员界面: 搜索姓名: 修改信息: 删除信息: 绩点升序排序: 导出…...
wordpress忘记管理员/googleseo服务公司
一 top命令 -H:开启线程 -p: 进程号 1.使用 top -H -p pid(具体的进程号) 查看该进程下所有线程占用CPU情况 2.pstack保存该进程的线程快照:根据命令下输出的线程id,查看体线程堆栈信息。...
网站指定关键词优化/运营和营销是一回事吗
web2.0学习平台作为一种新兴的网络学习系统,它与传统网络学习系统相比有许多新兴特点,概括下来,有以下几个方面: 从系统设计的角度看,传统的在线学习系统是一数据为核心的系统,培训机构将所有与在线课程相关的资料放在网上,学员在上…...
哈尔滨百度推广排名/上海seo公司哪个靠谱
堆是一棵完全二叉树,对于最大堆来说,任何一个非叶子节点满足:结点的值大于其孩子结点的值。使用数组可以实现完全二叉树的结构。下标从1开始,一个结点i的左孩子下标为2*i,有孩子下标为2*i1。下面是实现一个简单的最大堆…...
wordpress清理插件哪个好/政府免费培训面点班
Altium Designer 10 please wait a moment2012-03-22 16:00:53 最近一直困扰Altium Designer 10的bug,AD打开protel格式的原理图后出错,每次启动都process wait a moment,然后退出。开始以为是设计项目文件错误,把所有的工作文件夹…...