Socket网络编程(五)——TCP数据发送与接收并行
目录
- 主要实现需求
- TCP 服务端收发并行重构
- 启动main方法重构
- 重构分离收发消息的操作
- 重构接收消息的操作
- 重构发送消息
- TCPServer调用发送消息的逻辑
- 监听客户端链接逻辑重构
- Socket、流的退出与关闭
- TCP 客户端收发并行重构
- 客户端 main函数重构
- 客户端接收消息重构
- 客户端发送消息重构
- 客户端 linkWith 主方法重构
- TCP 收发并行重构测试
- 服务端重构后执行日志
- 客户端重构后执行日志
- 源码下载
主要实现需求
多线程收发并行
TCP多线程收发协作
TCP 服务端收发并行重构
TCP 服务端收发并行重构
启动main方法重构
原有的main逻辑如下:

重构后如下:
public class Server {public static void main(String[] args) throws IOException {TCPServer tcpServer = new TCPServer(TCPConstants.PORT_SERVER);boolean isSucceed = tcpServer.start();if(!isSucceed){System.out.println("Start TCP server failed.");}UDPProvider.start(TCPConstants.PORT_SERVER);// 键盘输入:BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(System.in));String str;do {str = bufferedReader.readLine();tcpServer.broadcast(str);} while (!"00bye00".equalsIgnoreCase(str));UDPProvider.stop();tcpServer.stop();}
}
重构后,从while循环不断读取键盘输入信息,当输入“00bye00” 时退出读取。此处只读取键盘输入数据,客户端发送的数据在会重新拆分出来新的线程单独处理。
重构分离收发消息的操作
创建 ClientHandler.java 重构收发消息操作:
public class ClientHandler {private final Socket socket;private final ClientReadHandler readHandler;private final ClientWriteHandler writeHandler;private final CloseNotiry closeNotiry;public ClientHandler(Socket socket, CloseNotiry closeNotiry ) throws IOException {this.socket = socket;this.readHandler = new ClientReadHandler(socket.getInputStream());this.writeHandler = new ClientWriteHandler(socket.getOutputStream());this.closeNotiry = closeNotiry;System.out.println("新客户链接: " + socket.getInetAddress() + "\tP:" + socket.getPort());}
}
重构接收消息的操作
/*** 接收数据*/class ClientReadHandler extends Thread {private boolean done = false;private final InputStream inputStream;ClientReadHandler(InputStream inputStream){this.inputStream = inputStream;}@Overridepublic void run(){super.run();try {// 得到输入流,用于接收数据BufferedReader socketInput = new BufferedReader(new InputStreamReader(inputStream));do {// 客户端拿到一条数据String str = socketInput.readLine();if(str == null){System.out.println("客户端已无法读取数据!");// 退出当前客户端ClientHandler.this.exitBySelf();break;}// 打印到屏幕System.out.println(str);}while (!done);socketInput.close();}catch (IOException e){if(!done){System.out.println("连接异常断开");ClientHandler.this.exitBySelf();}}finally {// 连接关闭CloseUtils.close(inputStream);}}void exit(){done = true;CloseUtils.close(inputStream);}}
创建一个单独的线程进行接收消息,该线程不需要关闭。
重构发送消息
/*** 发送数据*/class ClientWriteHandler {private boolean done = false;private final PrintStream printStream;private final ExecutorService executorService;ClientWriteHandler(OutputStream outputStream) {this.printStream = new PrintStream(outputStream);// 发送消息使用线程池来实现this.executorService = Executors.newSingleThreadExecutor();}void exit(){done = true;CloseUtils.close(printStream);executorService.shutdown();}void send(String str) {executorService.execute(new WriteRunnable(str));}class WriteRunnable implements Runnable{private final String msg;WriteRunnable(String msg){this.msg = msg;}@Overridepublic void run(){if(ClientWriteHandler.this.done){return;}try {ClientWriteHandler.this.printStream.println(msg);}catch (Exception e){e.printStackTrace();}}}}
TCPServer调用发送消息的逻辑
public void broadcast(String str) {for (ClientHandler client : clientHandlerList){// 发送消息client.send(str);}}
监听客户端链接逻辑重构
private List<ClientHandler> clientHandlerList = new ArrayList<>();/*** 监听客户端链接*/private class ClientListener extends Thread {private ServerSocket server;private boolean done = false;private ClientListener(int port) throws IOException {server = new ServerSocket(port);System.out.println("服务器信息: " + server.getInetAddress() + "\tP:" + server.getLocalPort());}@Overridepublic void run(){super.run();System.out.println("服务器准备就绪~");// 等待客户端连接do{// 得到客户端Socket client;try {client = server.accept();}catch (Exception e){continue;}try {// 客户端构建异步线程ClientHandler clientHandler = new ClientHandler(client, handler -> clientHandlerList.remove(handler));// 启动线程clientHandler.readToPrint();clientHandlerList.add(clientHandler);} catch (IOException e) {e.printStackTrace();System.out.println("客户端连接异常: " + e.getMessage());}}while (!done);System.out.println("服务器已关闭!");}void exit(){done = true;try {server.close();}catch (IOException e){e.printStackTrace();}}}
clientHandlerList作为已经建立了连接的客户端的集合,用于管理当前用户的信息。接收与发送都使用该集合。
Socket、流的退出与关闭
/*** 退出、关闭流*/public void exit(){readHandler.exit();writeHandler.exit();CloseUtils.close(socket);System.out.println("客户端已退出:" + socket.getInetAddress() + "\tP:" + socket.getPort());}/*** 发送消息* @param str*/public void send(String str){writeHandler.send(str);}/*** 接收消息*/public void readToPrint() {readHandler.exit();}/*** 接收、发送消息异常,自动关闭*/private void exitBySelf() {exit();closeNotiry.onSelfClosed(this);}/*** 关闭流*/public interface CloseNotiry{void onSelfClosed(ClientHandler handler);}
TCP 客户端收发并行重构
客户端 main函数重构
public static void main(String[] args) {// 定义10秒的搜索时间,如果超过10秒未搜索到,就认为服务器端没有开机ServerInfo info = UDPSearcher.searchServer(10000);System.out.println("Server:" + info);if( info != null){try {TCPClient.linkWith(info);}catch (IOException e){e.printStackTrace();}}}
客户端接收消息重构
static class ReadHandler extends Thread{private boolean done = false;private final InputStream inputStream;ReadHandler(InputStream inputStream){this.inputStream = inputStream;}@Overridepublic void run(){try {// 得到输入流,用于接收数据BufferedReader socketInput = new BufferedReader(new InputStreamReader(inputStream));do {// 客户端拿到一条数据String str = null;try {str = socketInput.readLine();}catch (SocketTimeoutException e){}if(str == null){System.out.println("连接已关闭,无法读取数据!");break;}// 打印到屏幕System.out.println(str);}while (!done);socketInput.close();}catch (IOException e){if(!done){System.out.println("连接异常断开:" + e.getMessage());}}finally {// 连接关闭CloseUtils.close(inputStream);}}void exit(){done = true;CloseUtils.close(inputStream);}}
创建ReadHandler用单独的线程去接收服务端的消息。连接关闭则exit() 关闭客户端。
客户端发送消息重构
private static void write(Socket client) throws IOException {// 构建键盘输入流InputStream in = System.in;BufferedReader input = new BufferedReader(new InputStreamReader(in));// 得到Socket输出流,并转换为打印流OutputStream outputStream = client.getOutputStream();PrintStream socketPrintStream = new PrintStream(outputStream);boolean flag = true;do {// 键盘读取一行String str = input.readLine();// 发送到服务器socketPrintStream.println(str);// 从服务器读取一行if("00bye00".equalsIgnoreCase(str)){break;}}while(flag);// 资源释放socketPrintStream.close();}
在linkWith() 中调用write() 发送方法,由 do-while 循环读取本地键盘输入信息进行发送操作。当满足 “00bye00” 时,关闭循环,关闭socket连接,结束该线程。
客户端 linkWith 主方法重构
public static void linkWith(ServerInfo info) throws IOException {Socket socket = new Socket();// 超时时间socket.setSoTimeout(3000);// 端口2000;超时时间300mssocket.connect(new InetSocketAddress(Inet4Address.getByName(info.getAddress()),info.getPort()));//System.out.println("已发起服务器连接,并进入后续流程~");System.out.println("客户端信息: " + socket.getLocalAddress() + "\tP:" + socket.getLocalPort());System.out.println("服务器信息:" + socket.getInetAddress() + "\tP:" + socket.getPort());try {ReadHandler readHandler = new ReadHandler(socket.getInputStream());readHandler.start();// 发送接收数据write(socket);}catch (Exception e){System.out.println("异常关闭");}// 释放资源socket.close();System.out.println("客户端已退出~");}
原有的逻辑里,是调用 todo() 方法,在todo() 方法里同时进行收发操作。现在是进行读写分离。
TCP 收发并行重构测试
服务端重构后执行日志

客户端重构后执行日志

源码下载
下载地址:https://gitee.com/qkongtao/socket_study/tree/master/src/main/java/cn/kt/socket/SocketDemo_L5_TCP_Channel
相关文章:
Socket网络编程(五)——TCP数据发送与接收并行
目录 主要实现需求TCP 服务端收发并行重构启动main方法重构重构分离收发消息的操作重构接收消息的操作重构发送消息TCPServer调用发送消息的逻辑监听客户端链接逻辑重构Socket、流的退出与关闭 TCP 客户端收发并行重构客户端 main函数重构客户端接收消息重构客户端发送消息重构…...
2024最新-ubuntu22.04安装最新版QT6.6~6.8教程
1. 在官网下载 online_installer: https://download.qt.io/archive/online_installers/4.7/qt-unified-linux-x64-4.7.0-online.run 或者直接镜像站下载: http://mirrors.ustc.edu.cn/qtproject/archive/online_installers/4.7/qt-unified-linux-x6…...
STM32------分析GPIO寄存器
一、初始LED原理图 共阴极led LED发光二极管,需要有电流通过才能点亮,当有电压差就会产生电流 二极管两端的电压差超过2.7v就会有电流通过 电阻的作用 由于公式IV/R 不加电阻容易造成瞬间电流无穷大 发光二极管工作电流为10-20MA 3.3v / 1kΩ 3.…...
数学实验-Matlab使用(1)
使用方法以及笔记均在文件中 class1_func1.m function f class1_func1(x) % f为输出,输出有多个时需要用中括号以矩阵的方式包起来 % x为输入f sin(x)class1_func2.m function [a,b,u,v] class1_func2(x,y)[a,b] eig(x)[u,v] eig(y)class1.m % 当语句后有…...
kafka文件存储机制和消费者
1.broker文件存储机制 去查看真正的存储文件: 在/opt/module/kafka/datas/ 路径下 kafka-run-class.sh kafka.tools.DumpLogSegments --files ./00000000000000000000.index 如果是6415那么这个会存储在563的log文件之中,因为介于6410和10090之间。 2.…...
《汇编语言》- 读书笔记 - 第15章-外中断
《汇编语言》- 读书笔记 - 第15章-外中断 15.1 接口芯片和端口15.2 外中断信息1. 可屏蔽中断(Maskable Interrupt)2. 不可屏蔽中断(Non-Maskable Interrupt)设计思想 15.3 PC 机键盘的处理过程1. 键盘输入2. 引发 9 号中断3. 执行…...
【Vue3】CSS 新特性
:slotted <template> <!-- App.vue--><Son ><div class"a">我要插入了</div></Son> </template><script setup lang"ts"> import Son from ./components/Son.vue </script><style></sty…...
四信水电站泄洪预警方案,精准提升防汛应急水平
2022年5月水利部办公厅发布《关于开展水库泄洪设施专项排查整改的紧急通知》,为坚决贯彻落实关于水库大坝安全的重要指示批示精神、关于保障水库泄洪通道通畅的批示要求,全力防范水库可能出现的重大险情,确保水库安全度汛。 2023年3月国家能源…...
k8s中容器的调度与创建:CRI,cgroup
container调度与创建 选自:K8s、CRI与container - packy的文章 - 知乎 https://zhuanlan.zhihu.com/p/102897620 Cgroup创建: cgexec -g cpu,memory:$UUID \ > unshare -uinpUrf --mount-proc \ > sh -c "/bin/hostname $UUID &…...
Unity安装与简单设置
安装网址:https://unity.cn 设置语言: 设置安装位置:否则C盘就会爆了 获取一个个人的资格证: 开始安装: 安装完毕。 添加模块:例如简体中文 新建项目: 布局2*3、单栏布局、 设置…...
数据库的介绍、分类、作用和特点
数据库是用来存储、管理和检索数据的集合系统。根据数据处理模型的不同,数据库可以分为多种类型,主要包括: 1、关系型数据库(RDBMS): 介绍:关系型数据库使用表格形式来存储数据,并通…...
【Unity】机器人末端执行器仿真
机械手臂的末端执行器使用多项式来计算转动角度可能有几个原因: 精确控制:机械臂的运动通常需要高度的精确性,特别是在精密工作或复杂运动轨迹的情况下。多项式,特别是高阶的,可以很好地近似复杂的非线性关系和运动轨迹…...
更换个人开发环境后,pycharm连接服务器报错Authentication failed
原因:服务器中更换个人开发环境后,密码变了。 解决:在pycharm中修改服务器开发环境密码即可。 1 找到Tools-Depolyment-Configuration 2 点击SSH Configuration后的省略号 3 修改这里面的Password即可...
E - Bad Juice
解题思路 由于最后返回一个01字符串表示所选人的状态要求人数最少,即字符串长度最少而要用最少的字符,找出则返回的字符为二进制下的编号这样利用了所有的01字符号人表示二进制下位的情况注意对于2的次方项,只需要有位,可以用位均…...
用HTML5的<canvas>元素实现刮刮乐游戏
用HTML5的<canvas>元素实现刮刮乐游戏 用HTML5的<canvas>元素实现刮刮乐,要求:将上面的“图层”的图像可用鼠标刮去,露出下面的“图层”的图像。 示例从简单到复杂。 简单示例 准备两张图像,我这…...
TypeScript + react 中 TypeScript 的加入后 , 有哪些优化项目
在使用 TypeScript 结合 React 进行开发时,TypeScript 提供了许多优化和增强代码质量的方式。以下是一些关键的优化操作和最佳实践: 强类型组件属性(Props)和状态(State): 使用接口或类型别名定义组件的 pr…...
Redis学习路径(构建体系)
学习路径 掌握数据类型(分析底层数据结构)和缓存的基本使用 (理论使用) 掌握 redis 实现高性能,高可靠、高可用技术 (理论)学习redis源代码底层实现 (底层实现) 先来一个引言,比较宏观的角度…...
【README 小技巧】 展示gitee中开源项目start
【README 小技巧】 展示gitee中开源项目start <a target"_blank" hrefhttps://gitee.com/wujiawei1207537021/wu-framework-parent><img srchttps://gitee.com/wujiawei1207537021/wu-framework-parent/badge/star.svg altGitee star/></a>...
tcping实用小工具
Tcping实用小工具命令详解 一、tcping介绍 tcping:tcping命令基于tcp协议监控,可以从较低级别的协议获得简单的,可能不可靠的数据报服务。 原则上,TCP应该能够在从容硬线连接到分组交换或电路交换网络的各种通信系统之上操作。 …...
【Web】Java反序列化之CC2——commons-collections4的新链之一
目录 关于commons-collections4 一个重要的思维模型 触发Transform的关键类:TransformingComparator 反序列化的入口:PriorityQueue Exp 关于commons-collections4 commons-collections4 是 Apache Commons 组件库中的一个项目,它是对旧…...
终极指南:如何用MediaPipe TouchDesigner插件打造惊艳的实时视觉交互
终极指南:如何用MediaPipe TouchDesigner插件打造惊艳的实时视觉交互 【免费下载链接】mediapipe-touchdesigner GPU Accelerated MediaPipe Plugin for TouchDesigner 项目地址: https://gitcode.com/gh_mirrors/me/mediapipe-touchdesigner 还在为TouchDes…...
Istio mTLS 与零信任网络:Sidecar 证书管理
# Istio mTLS 与零信任网络:Sidecar 证书管理深度解析> **标签**: #Istio #mTLS #零信任 #证书管理 #服务网格 > **阅读时间**: 约 15 分钟 > **难度**: ⭐⭐⭐⭐## 📖 引言在云原生时代,微服务架构的广泛应用带来了前所未有的安全挑战。传…...
3步掌握sndcpy:Android音频无线转发终极指南
3步掌握sndcpy:Android音频无线转发终极指南 【免费下载链接】sndcpy Android audio forwarding PoC (scrcpy, but for audio) 项目地址: https://gitcode.com/gh_mirrors/sn/sndcpy 在移动办公和内容创作日益普及的今天,你是否遇到过这样的困扰&…...
ChanlunX缠论插件:3步实现股票技术分析的终极可视化方案
ChanlunX缠论插件:3步实现股票技术分析的终极可视化方案 【免费下载链接】ChanlunX 缠中说禅炒股缠论可视化插件 项目地址: https://gitcode.com/gh_mirrors/ch/ChanlunX ChanlunX缠论可视化插件是专为通达信用户开发的智能缠论分析工具,通过自动…...
MFC中单选框与复选框控件的实战应用与优化技巧
1. MFC单选框与复选框控件基础入门 第一次接触MFC的单选框(Radio Button)和复选框(CheckBox)时,我完全被它们的组属性搞晕了。记得当时做了个问卷调查界面,结果所有单选框都能同时选中,简直是个灾难现场。后来才发现,原来MFC的单选…...
Android Input 系统深度解析【InputReader与InputDispatcher的协同与事件流】
1. Android输入系统核心架构解析 当你触摸手机屏幕时,系统如何精准识别你的操作?这背后是Android输入系统的高效运转。整个流程就像快递配送体系:InputReader是仓库分拣员,负责从Linux驱动节点(/dev/input)…...
前视声呐图像处理避坑指南:从像素坐标到真实距离/角度的转换原理与YOLO集成
前视声呐图像处理避坑指南:从像素坐标到真实距离/角度的转换原理与YOLO集成 水下机器人视觉系统的核心挑战之一,是如何准确解读前视声呐生成的二维图像。与光学相机不同,声呐图像中的每个像素点背后都隐藏着复杂的物理测量原理。许多工程师在…...
靠谱的法兰研发公司
在工业领域,法兰是连接管道系统的关键部件,其性能直接影响到整个系统的安全性和稳定性。因此,选择一家靠谱的法兰研发公司至关重要。本文将从多个维度对河北汇能管道制造有限公司(以下简称“河北汇能”)进行评测&#…...
哔哩下载姬DownKyi终极指南:3步轻松搞定B站高清视频下载
哔哩下载姬DownKyi终极指南:3步轻松搞定B站高清视频下载 【免费下载链接】downkyi 哔哩下载姬downkyi,哔哩哔哩网站视频下载工具,支持批量下载,支持8K、HDR、杜比视界,提供工具箱(音视频提取、去水印等&…...
大模型工程化到底缺什么?——SITS2026白皮书披露行业首套成熟度评估矩阵(含自测工具)
第一章:SITS2026发布:大模型工程化白皮书下载 2026奇点智能技术大会(https://ml-summit.org) SITS2026(Scalable Intelligent Training & Serving Summit)正式发布《大模型工程化白皮书(2026版)》&am…...
