Socket粘包问题终极解决方案—Netty版(2W字)! (2)

服务器端我们使用线程池来处理每个客户端的业务请求,实现代码如下:

/** * 服务器端 */ class MySocketServer { public static void main(String[] args) throws IOException { // 创建 Socket 服务器端 ServerSocket serverSocket = new ServerSocket(9093); // 获取客户端连接 Socket clientSocket = serverSocket.accept(); // 使用线程池处理更多的客户端 ThreadPoolExecutor threadPool = new ThreadPoolExecutor(100, 150, 100, TimeUnit.SECONDS, new LinkedBlockingQueue<>(1000)); threadPool.submit(() -> { // 客户端消息处理 processMessage(clientSocket); }); } /** * 客户端消息处理 * @param clientSocket */ private static void processMessage(Socket clientSocket) { // Socket 封装对象 SocketPacket socketPacket = new SocketPacket(); // 获取客户端发送的消息对象 try (InputStream inputStream = clientSocket.getInputStream()) { while (true) { // 获取消息头(也就是消息体的长度) int bodyLength = socketPacket.getHeader(inputStream); // 消息体 byte 数组 byte[] bodyByte = new byte[bodyLength]; // 每次实际读取字节数 int readCount = 0; // 消息体赋值下标 int bodyIndex = 0; // 循环接收消息头中定义的长度 while (bodyIndex <= (bodyLength - 1) && (readCount = inputStream.read(bodyByte, bodyIndex, bodyLength)) != -1) { bodyIndex += readCount; } bodyIndex = 0; // 成功接收到客户端的消息并打印 System.out.println("接收到客户端的信息:" + new String(bodyByte)); } } catch (IOException ioException) { System.out.println(ioException.getMessage()); } } }

以上程序的执行结果如下:

image.png


从上述结果可以看出,消息通讯正常,客户端和服务器端的交互中并没有出现粘包和半包的问题。

二、使用 Netty 实现高效通讯

以上的内容都是针对传统 Socket 编程的,但要实现更加高效的通讯和连接对象的复用就要使用 NIO(Non-Blocking IO,非阻塞 IO)或者 AIO(Asynchronous IO,异步非阻塞 IO)了。

传统的 Socket 编程是 BIO(Blocking IO,同步阻塞 IO),它和 NIO 和 AIO 的区别如下:

BIO 来自传统的 java.io 包,它是基于流模型实现的,交互的方式是同步、阻塞方式,也就是说在读入输入流或者输出流时,在读写动作完成之前,线程会一直阻塞在那里,它们之间的调用是可靠的线性顺序。它的优点就是代码比较简单、直观;缺点就是 IO 的效率和扩展性很低,容易成为应用性能瓶颈。

NIO 是 Java 1.4 引入的 java.nio 包,提供了 Channel、Selector、Buffer 等新的抽象,可以构建多路复用的、同步非阻塞 IO 程序,同时提供了更接近操作系统底层高性能的数据操作方式。

AIO 是 Java 1.7 之后引入的包,是 NIO 的升级版本,提供了异步非堵塞的 IO 操作方式,因此人们叫它 AIO(Asynchronous IO),异步 IO 是基于事件和回调机制实现的,也就是应用操作之后会直接返回,不会堵塞在那里,当后台处理完成,操作系统会通知相应的线程进行后续的操作。

PS:AIO 可以看作是 NIO 的升级,它也叫 NIO 2。

传统 Socket 的通讯流程:

image.png


NIO 的通讯流程:

image.png

使用 Netty 替代传统 NIO 编程

NIO 的设计思路虽然很好,但它的代码编写比较麻烦,比如 Buffer 的使用和 Selector 的编写等。并且在面对断线重连、包丢失和粘包等复杂问题时手动处理的成本都很大,因此我们通常会使用 Netty 框架来替代传统的 NIO。

Netty 是什么?

内容版权声明:除非注明,否则皆为本站原创文章。

转载注明出处:https://www.heiqu.com/zzfpwx.html