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

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基本开发与改进【心跳、粘包与拆包、闲置连接】》

相关文章:

  • OpenCV C++ 学习笔记(六):绘制文本、几何绘图、查找/绘制轮廓
  • EasyRTC嵌入式音视频通信SDK音视频功能驱动视频业务多场景应用
  • 数据标注与大模型的双向赋能:效率与性能的跃升
  • RPG21.创建敌人的AttributeSet,创建角色的GameplayEffect
  • 【Rust 高级trait】Rust trait的一些高级用法解密
  • 【算法深练】分组循环:“分”出条理,化繁为简
  • Paraformer分角色语音识别-中文-通用 FunASR
  • 数控矫平机:深潜技术内核与智造前沿
  • Python try-except-else 语句详解
  • 夏普比率(Sharpe ratio)​
  • [C++入门]简化的艺术---对模版的初步探索
  • django ssh登录 并执行命令
  • React从基础入门到高级实战:React 高级主题 - 测试进阶:从单元测试到端到端测试的全面指南
  • k8s集群安装坑点汇总
  • SQL-为什么缺少 COUNT(*) 会导致总行数返回1
  • 07 APP 自动化- appium+pytest+allure框架封装
  • OA工程自动化办公系统 – 免费Java源码
  • Selenium自动化测试工具安装和使用(PyCharm)
  • Java高级 | 【实验四】Springboot 获取前端数据与返回Json数据
  • 设计模式之单例模式(二): 心得体会
  • 外贸b2b网站建设公司/网上销售方法
  • 如何让网站不被收录/好口碑的关键词优化
  • wordpress 登录后可查看/杭州最好的seo公司
  • 寻求南宁网站建设人员/工业设计公司
  • 做网站销售的/市场营销策划案例经典大全
  • 建立微信公众号步骤/西安seo网络推广