Spring Boot 从Socket 到Netty网络编程(上):SOCKET 基本开发(BIO)与改进(NIO)
前言
无论是软件还是硬件的本质都是要解决IO问题(输入、输出),再说回网络编程本质上都是基于TCP/UP的开发,socket是在此基础上做的扩展与封装,而Netty又是对socket做的封装。本文旨在通过相关案例对socket进行探讨。
一、基本知识
交互方式
- 连接三次握手:1.客户-服务 (请求)2.服务-客户(同意)3.客户-服务(连接)
- 断开四次握手:1.客户-服务(请求断开)2.服务-客户(接受请求)3.服务-客户(断开) 4.客户-服务(断开完成)
Java类
ServerSocket 服务类
accept(() 开启连接
close() 停止服务
Socket 客户端类
getInputStream() 输入内存流(接收)
getOutputStream() 输出内存流(发布)
二、基于线程阻塞式socket(BIO)开发示例
实现
Server代码
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.PrintWriter;
import java.net.ServerSocket;
import java.net.Socket;public class SockerServer {public static void main(String[] args) throws IOException {ServerSocket serverSocket= new ServerSocket(1200);while (true){Socket socket = serverSocket.accept();System.out.println("有新的客户端连接了:"+socket.getInetAddress());new Thread(new ClientHandler(socket)).start();}}
}class ClientHandler implements Runnable {private Socket socket;public ClientHandler(Socket socket) {this.socket = socket;}@Overridepublic void run() {try {BufferedReader inStreamReader = new BufferedReader(new InputStreamReader(socket.getInputStream()));PrintWriter outStreamWriter = new PrintWriter(socket.getOutputStream(), true);outStreamWriter.println("请输入内容:");String message;while ((message = inStreamReader.readLine()) != null) {System.out.println("收到客户端消息: " + message);if ("bye".equalsIgnoreCase(message)) {outStreamWriter.println("服务器:连接已关闭,再见!");break; // 结束连接}// 回显客户端消息outStreamWriter.println("服务器回显:" + message);}} catch (IOException e) {throw new RuntimeException(e);}}
}
Client 代码
import java.io.BufferedReader;
import java.io.InputStreamReader;
import java.io.PrintWriter;
import java.net.Socket;public class SocketClient {public static void main(String[] args) throws Exception {Socket socket = new Socket("127.0.0.1",1200);BufferedReader in = new BufferedReader(new InputStreamReader(socket.getInputStream()));PrintWriter out = new PrintWriter(socket.getOutputStream(), true);BufferedReader console = new BufferedReader(new InputStreamReader(System.in));System.out.println("已连接到服务器!");System.out.println(in.readLine()); // 读取服务器欢迎消息String userInput;System.out.println("请输入消息(输入 exit 断开连接):");while ((userInput = console.readLine()) != null) {out.println(userInput); // 发送消息给服务器String serverResponse = in.readLine(); // 接收服务器响应System.out.println(serverResponse);if ("exit".equalsIgnoreCase(userInput)) {System.out.println("连接已关闭。");break;}}}
}
效果
缺点
虽然这个已经实现通信,由于它是基于线程控制通信的,换言之每个客户端连接后都会创建一个线程,客户端数量与线程增长成正比就意味着会吃更多的内存,这显然是不合理的。(如果你足够细心会有一些程序要隔一段时间需要重新启动一下否则会卡死,这或许是设计之初犯类似的错误),这就是BIO的致命缺点;
三、基于单线程socket(NIO)
前言
基于上面的问题,我们通过改造实现非隔断性Socket实现,而非多线程方式;这可以极大的提高交互效率与并发问题。
实现
服务端
import java.io.IOException;
import java.nio.ByteBuffer;
import java.nio.channels.SelectionKey;
import java.nio.channels.Selector;
import java.nio.channels.ServerSocketChannel;
import java.nio.channels.SocketChannel;
import java.util.Iterator;
import java.util.Set;public class NioServer {public static void main(String[] args) throws IOException {// 打开服务端Socket通道ServerSocketChannel serverSocketChannel = ServerSocketChannel.open();// 绑定端口1200serverSocketChannel.socket().bind(new java.net.InetSocketAddress(1200));// 设置为非阻塞模式serverSocketChannel.configureBlocking(false);// 创建Selector选择器Selector selector = Selector.open();// 将ServerSocketChannel注册到Selector,监听连接事件serverSocketChannel.register(selector, SelectionKey.OP_ACCEPT);System.out.println("系统运行中.......");while (true){// 阻塞直到有事件发生selector.select();// 获取所有发生的事件键Set<SelectionKey> selectionKeys = selector.selectedKeys();Iterator<SelectionKey> iterator = selectionKeys.iterator();while (iterator.hasNext()){SelectionKey key = iterator.next();if (key.isAcceptable()){ // 如果是客户端连接事件// 处理客户端连接ServerSocketChannel server = (ServerSocketChannel) key.channel(); // 获取触发事件的通道SocketChannel sc = server.accept(); // 接受客户端连接sc.configureBlocking(false); // 设置为非阻塞模式sc.register(selector, SelectionKey.OP_READ); // 注册读取事件System.out.println("有新的客户端连接了:"+sc.getRemoteAddress());}else if (key.isReadable()){ // 如果是客户端读取事件// 处理客户端读取请求SocketChannel sc = (SocketChannel) key.channel(); // 获取触发事件的通道ByteBuffer buffer = ByteBuffer.allocate(1024); // 创建缓冲区int len = sc.read(buffer); // 读取数据if (len > 0){ // 如果有数据String msg = new String(buffer.array(), 0, len); // 解析消息System.out.println("收到客户端"+sc.getRemoteAddress()+"的消息:"+msg);// 将收到的消息回传给客户端ByteBuffer outBuffer = ByteBuffer.wrap(msg.getBytes()); // 包装响应数据sc.write(outBuffer); // 发送响应}else if (len == -1){ // 如果客户端断开连接System.out.println("客户端"+sc.getRemoteAddress()+"断开了连接");sc.close(); // 关闭通道}}iterator.remove(); // 移除当前事件键}}}
}
客户端
import java.io.BufferedReader;
import java.io.InputStreamReader;
import java.io.PrintWriter;
import java.net.InetSocketAddress;
import java.nio.channels.SocketChannel;public class NioClient {public static void main(String[] args) throws Exception{// 打开SocketChannel并连接到服务器SocketChannel socketChannel = SocketChannel.open();socketChannel.connect(new InetSocketAddress("localhost", 1200));// 创建控制台输入流,用于读取用户输入BufferedReader console = new BufferedReader(new InputStreamReader(System.in));System.out.println("已连接到服务器!");// 初始化PrintWriter对象,用于向服务器发送数据PrintWriter writer = new PrintWriter(socketChannel.socket().getOutputStream(), true);String message;// 循环读取用户输入并发送给服务器while ((message = console.readLine()) != null) {if ("exit".equalsIgnoreCase(message)) {System.out.println("即将退出连接...");break;}// 向服务器发送消息writer.println(message);// 读取服务器返回的响应数据BufferedReader serverResponse = new BufferedReader(new InputStreamReader(socketChannel.socket().getInputStream()));String response;// 打印服务器返回的每一行数据while ((response = serverResponse.readLine()) != null) {System.out.println("服务器响应: " + response);}}// 关闭SocketChannel连接socketChannel.close();System.out.println("客户端已退出");}
}
效果
缺点
综上所述,NIO虽好,上面的代码仅做到了实现,但是进行性能调优、异常处理等等需要更写更多的代码;作为程序员目的是用好轮子而非造轮子,那么Netty这个网络通信工具以它的高性能、高并发、易配置的特点脱颖而出! 下篇我将进一步进行介绍。
下一篇《Spring Boot 从Socket 到Netty网络编程(下):Netty基本开发与改进【心跳、粘包与拆包、闲置连接】》
相关文章:

Spring Boot 从Socket 到Netty网络编程(上):SOCKET 基本开发(BIO)与改进(NIO)
前言 无论是软件还是硬件的本质都是要解决IO问题(输入、输出),再说回网络编程本质上都是基于TCP/UP的开发,socket是在此基础上做的扩展与封装,而Netty又是对socket做的封装。本文旨在通过相关案例对socket进行探讨。 一…...

79. Word Search
题目描述 79. Word Search 回溯 代码一,使用used数组 class Solution {vector<pair<int,int>> directions{{0,1},{0,-1},{1,0},{-1,0}};vector<vector<bool>> used; public:bool exist(vector<vector<char>>& board, st…...

结构性设计模式之Facade(外观)设计模式
结构性设计模式之Facade(外观)设计模式 前言: 外观模式:用自己的话理解就是用户看到是一个总体页面,比如xx报名系统页面。里面有历年真题模块、报名模块、教程模块、首页模块… 做了一个各个模块的合并,对…...

ICML 2025 Spotlight | 机器人界的「Sora」!让机器人实时进行未来预测和动作执行!
标题:Video Prediction Policy: A Generalist Robot Policy with Predictive Visual Representations 作者:Yucheng Hu, Yanjiang Guo, Pengchao Wang, Xiaoyu Chen, Yen-Jen Wang, Jianke Zhang, Koushil Sreenath, Chaochao Lu, Jianyu Chen 机构&am…...
CSP严格模式返回不存在的爬虫相关文件
文章目录 说明示例(返回404)示例(创建CSP例外) 说明 日期:2025年6月4日。 CSP严格模式是default-src none,但有些web应用中,在爬虫相关文件不存在的情况下,依旧返回了对应文件&…...
https(SSL)证书危机和可行的解决方案
证书危机 20250411,CAB 论坛服务器证书工作组( SCWG )投票通过一项重大提案《 SC-081v3: 引入缩短有效期和数据重复使用期的时间表》,最终决定:从 2026 年起 SSL/TLS 证书的最大有效期将从 398 天逐步缩短至 47 天,并…...
C#获取磁盘容量:代码实现与应用场景解析
C#获取磁盘容量:代码实现与应用场景解析 在软件开发过程中,尤其是涉及文件存储、数据备份等功能时,获取磁盘容量信息是常见的需求。通过获取磁盘的可用空间和总大小,程序可以更好地进行资源管理、预警提示等操作。在 C# 语言中&a…...
2359. 找到离给定两个节点最近的节点
2359. 找到离给定两个节点最近的节点 题目链接:2359. 找到离给定两个节点最近的节点 代码如下: class Solution { public:int closestMeetingNode(vector<int>& edges, int node1, int node2) {auto calc_dis [&](int x) {vector<in…...
前端导入Excel表格
前端如何在 Vue 3 中导入 Excel 文件(.xls 和 .xlsx)? 在日常开发中,我们经常需要处理 Excel 文件,比如导入数据表格、分析数据等。文章将在 Vue 3 中实现导入 .xls 和 .xlsx 格式的文件,并解析其中的数据…...

AI生态警报:MCP协议风险与应对指南(下)——MCP Host安全
AI生态警报:MCP协议风险与应对指南(上)——架构与供应链风险https://blog.csdn.net/WangsuSecurity/article/details/148335401?sharetypeblogdetail&sharerId148335401&sharereferPC&sharesourceWangsuSecurity&spm1011.24…...

基于VLC的Unity视频播放器(四)
上篇文章中提到的问题 播放某个m3u8地址时会嘎掉,想办法解决了一下,很粗暴的,先SetFormat,再Stop,最后再Play,能用…… if (player ! null && player.GetSize() 0) {player.GetSize((w, h) >…...

pixel刷入Android15 userdebug版本
最近入手一个pixel7,想着刷个userdebug版本,就不用模拟器调试开发了,结果按照网上的教程,每次刷机后都是卡在goole logo界面,卡了一天多我才找到问题所在,想着记录下,给自己做个备份。 1. 前期准备&#x…...
【Go-补充】ioReader + ioWriter + bufio
一、io.Reader 和 io.Writer 接口介绍 在 Go 语言中,io 包定义了两个最基础和最重要的接口:io.Reader 和 io.Writer。它们是 Go 语言中进行输入/输出操作的核心抽象,实现了极大的灵活性和可组合性。 io.Reader 接口 io.Reader 接口定义了一…...
leetcode 3403. 从盒子中找出字典序最大的字符串 I 中等
给你一个字符串 word 和一个整数 numFriends。 Alice 正在为她的 numFriends 位朋友组织一个游戏。游戏分为多个回合,在每一回合中: word 被分割成 numFriends 个 非空 字符串,且该分割方式与之前的任意回合所采用的都 不完全相同 。所有分…...

C# 一个解决方案放一个dll项目,一个dll测试项目 ,调试dll项目的源码
一个解决方案(sln)中放入2个项目(project,通常是一个文件夹),一个dll项目,一个dll测试项目 右键dll测试项目,设为启动项目。 在dll测试项目添加引用 1)右键测试项目 → 添加 → 引用 → 项目…...

【PmHub面试篇】PmHub 整合 TransmittableThreadLocal(TTL)缓存用户数据面试专题解析
你好,欢迎来到本次关于PmHub整合TransmittableThreadLocal (TTL)缓存用户数据的面试系列分享。在这篇文章中,我们将深入探讨这一技术领域的相关面试题预测。若想对相关内容有更透彻的理解,强烈推荐参考之前发布的博文:【PmHub后端…...

unity随机生成未知符号教程
目录 前言方法1方法2脚本后言示例代码 前言 在某些游戏中,有一些让人感到意味不明的未知符号,例如在游戏《巴别塔圣歌》中,就有这样一些能让人在初次就看不懂的未知符号。 或者在其他时候,这些未知符号如果跟粒子系统结合在一起的…...

基于RK3576+FPGA+AI工业控制器的工地防护检测装备解决方案
1.2.1 工地防护检测技术研究现状 在建筑施工的过程中,工人被要求暴露在危险的环境中作业 [2]。因此,防护装备 对于工人的安全与健康具有非常重要的意义[3]。工地工人必须佩戴适当的防护装备, 以降低意外伤害的风险。在过去的几十年里&#x…...

推荐一款PDF压缩的工具
今天一位小伙伴找来,问我有没有办法将PDF变小的办法。 详细了解了一下使用场景: 小伙伴要在某系统上传一个PDF文件,原文件是11.6MB,但是上传时系统做了限制,只能上传小于10MB的文件,如图: 我听…...

混沌映射(Chaotic Map)
一.定义 混沌映射是指一类具有混沌行为的离散时间非线性动力系统,通常由递推公式定义。其数学形式为 ,其中 f 是非线性函数,θ 为参数。它们以简单的数学规则生成复杂的、看似随机的轨迹,是非线性动力学和混沌理论的重要研究对象…...
MySQL对数据库用户的操作
注:‘%’:表示允许远程连接,‘localhost’ :限制本地登陆 – 根据用户名、权限查询用户 SELECT USER FROM mysql.user WHERE USER‘your_name’ AND HOST‘%’; – 彻底删除用户 DROP USER ‘appuser’‘%’; – 刷新使其生效 FL…...

《PyTorch Hub:解锁深度学习模型的百宝箱》
走进 PyTorch Hub 在当今的深度学习领域,模型的复用和共享已成为推动技术飞速发展的关键力量。随着深度学习在计算机视觉、自然语言处理、语音识别等众多领域取得突破性进展,研究人员和开发者们不断探索更高效、更强大的模型架构。然而,从头开始训练一个深度学习模型往往需要…...

数据结构 堆与优先级队列
文章目录 📕1. 堆(Heap)✏️1.1 堆的概念✏️1.2 堆的存储方式✏️1.3 堆的创建✏️1.4 堆的插入✏️1.5 堆的删除 📕2. 优先级队列(PriorityQueue)✏️2.1 堆与优先级队列的关系✏️2.2 优先级队列的构造方法✏️2.3 优先级队列的常用方法 3. Java对象的…...
Leetcode 3569. Maximize Count of Distinct Primes After Split
Leetcode 3569. Maximize Count of Distinct Primes After Split 1. 解题思路2. 代码实现 题目链接:3569. Maximize Count of Distinct Primes After Split 1. 解题思路 这一题的话思路倒是还好,显然,要找出所有distinct的质数的切分&…...

用好 ImageFX,解锁游戏素材生成新姿势:从入门到进阶
用好 ImageFX,解锁游戏素材生成新姿势:从入门到进阶 (备注)大陆ip无法访问到imagefx 地址:https://labs.google/fx/zh/tools/image-fx 对于独立游戏开发者和小型团队而言,美术资源往往是项目推进中的一大痛点。预算有限、专业美术人员缺乏…...
unix/linux,sudo,其基本属性、语法、操作、api
现在我们要深入到sudo的“微观结构”了——它的属性、语法、操作以及是否有传统意义上的“API”。这就像我们从宏观的宇宙现象深入到基本粒子的相互作用一样,充满了探索的乐趣! 一、 sudo 的基本属性 (Fundamental Attributes) 这些属性是sudo作为一款软件和系统工具的核心…...
文本内容变化引起布局尺寸变化 导致的 UI 适配问题
在使用 Flutter 开发应用时,配合 easy_localization 实现多语言切换是一个非常常见的做法。但正如你所说,在不同语言下文字长度差异较大(如英文和中文、阿拉伯语等)会导致界面布局错位、UI 不美观的问题。 这个问题本质上是 文本…...

01-Redis介绍与安装
01-Redis介绍与安装 SQL与NoSQL SQLNoSQL数据结构结构化非结构化数据关联关联的非关联的查询方式SQL查询非SQL事务特性ACIDBASE存储方式磁盘内存拓展性垂直水平使用场景1、数据结构固定2、相关业务对数据安全性、一致性要求较高1、数据结构不固定2、对安全性、一致性要求不高…...
十六、【前端强化篇】完善 TestCase 编辑器:支持 API 结构化定义与断言配置
【前端强化篇】完善 TestCase 编辑器:支持 API 结构化定义与断言配置 前言准备工作第一步:更新前端 `TestCase` 类型定义第二步:改造 `TestCaseEditView.vue` 表单第三步:修改后端代码中的TestCase模型和序列化器第四步:测试强化后的用例编辑器总结前言 在之前的后端文章…...
Kafka broker 写消息的过程
Producer → Kafka Broker → Replication → Consumer|Partition chosen (by key or round-robin)|Message appended to end of log (commit log)上面的流程是kafka 写操作的大体流程。 kafka 不会特意保留message 在内存中,而是直接写入了disk。 那么消费的时候&…...