基于NIO建立长连接

embedded/2025/2/5 21:59:25/

引言

NIO非阻塞IO无论长连接和短连接都有很好的适配性,本篇文章将实战NIO实现长链接的能力

NIO是什么

NIO(New I/O)是 Java 中的一种非阻塞 I/O 模型,其底层实现主要涉及以下几个关键方面:

  • Selector(选择器)

    • 原理:Selector 是 NIO 实现非阻塞 I/O 的核心组件。它基于操作系统的底层 I/O 多路复用机制,如 Linux 中的 epoll、Windows 中的 IOCP 等。Selector 可以同时监控多个 Channel 的 I/O 事件,比如连接建立、数据可读、数据可写等。通过 Selector,线程可以在一个或多个 Channel 上等待 I/O 事件的发生,而不需要为每个 Channel 都创建一个单独的线程来阻塞等待 I/O 操作完成,从而大大提高了 I/O 的效率和系统的并发处理能力。
    • 实现方式:在 Java 中,Selector 的实现是通过操作系统提供的底层 I/O 多路复用接口来完成的。例如在 Linux 系统上,Java 的 Selector 底层会调用 epoll 相关的系统函数来注册和监听 Channel 上的 I/O 事件。当有 I/O 事件发生时,操作系统会将这些事件通知给 Selector,Selector 再将这些事件分发给相应的 Channel 进行处理。
  • Channel(通道)

    • 原理:Channel 是 NIO 中用于与 I/O 设备进行交互的通道,它类似于传统 I/O 中的流,但具有非阻塞和双向等特性。Channel 可以看作是对底层操作系统 I/O 通道的一种抽象封装,它提供了一种更高效、更灵活的方式来进行数据的读写操作。每个 Channel 都可以注册到一个 Selector 上,以便 Selector 对其 I/O 事件进行监控。
    • 实现方式:在底层,不同类型的 Channel 有不同的实现。例如,SocketChannel 用于网络 I/O,它的底层实现是基于操作系统的网络套接字接口。在 Java 中,通过 JNI(Java Native Interface)等技术调用操作系统的网络相关函数来实现 SocketChannel 的功能,如创建套接字、连接服务器、发送和接收数据等。而 FileChannel 用于文件 I/O,它的底层实现则是通过调用操作系统的文件操作相关系统函数来完成文件的读写、定位等操作。
  • Buffer(缓冲区)

    • 原理:Buffer 是 NIO 中用于存储数据的缓冲区,它是一个线性的字节数组或其他基本数据类型的数组。在 NIO 中,数据的读写操作都是通过 Buffer 来进行的。当从 Channel 读取数据时,数据会被读取到 Buffer 中;当向 Channel 写入数据时,数据也是从 Buffer 中获取的。Buffer 提供了一系列的方法来管理和操作缓冲区中的数据,如读取、写入、翻转、复位等操作,方便了数据的处理和传输。
    • 实现方式:在 Java 中,Buffer 是一个抽象类,具体的实现有 ByteBuffer、CharBuffer、IntBuffer 等多种类型,分别用于存储不同类型的数据。这些具体的 Buffer 实现类通常是基于 Java 的数组来实现的,通过维护一些指针和状态变量来管理缓冲区中的数据读写位置和操作状态。例如,ByteBuffer 可以通过调用操作系统的内存分配函数来分配一块连续的内存空间作为缓冲区,然后通过 Java 的直接内存访问技术来对这块内存进行读写操作,从而提高数据的读写效率。

长连接与短连接的概念

  • 短连接:客户端和服务器进行一次数据交互就断开连接,每次请求都需要重新建立连接,完成数据传输后关闭连接。
  • 长连接:客户端和服务器建立连接后,在一段时间内保持连接状态,多次数据交互可以复用这个连接,直到双方主动关闭或者因为网络异常等原因断开。

NIO 支持长连接的原因

  • 非阻塞特性:NIO 的核心在于非阻塞 I/O 模型,利用 Selector 可以同时监控多个 Channel 的 I/O 事件。对于长连接场景,服务器可以使用一个线程通过 Selector 管理大量的长连接 Channel,当某个连接有数据可读或可写时才进行相应的处理,而不是像传统阻塞 I/O 那样为每个连接都分配一个线程并阻塞等待,这样大大提高了资源利用率,适合长连接这种长时间保持连接的场景。
  • 资源复用:在长连接中,建立连接是一个相对开销较大的操作。NIO 允许在已建立的连接上多次进行数据读写操作,复用已有的连接资源,避免了频繁建立和销毁连接带来的性能损耗。例如,在一个基于 NIO 的即时通讯服务器中,客户端和服务器建立长连接后,可以不断地通过这个连接发送和接收消息。

实战(Nio支持长连接服务器)

import java.io.IOException;
import java.net.InetSocketAddress;
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 NioLongConnectionServer {private static final int PORT = 8888;public static void main(String[] args) {try (Selector selector = Selector.open();ServerSocketChannel serverSocketChannel = ServerSocketChannel.open()) {serverSocketChannel.bind(new InetSocketAddress(PORT));serverSocketChannel.configureBlocking(false);serverSocketChannel.register(selector, SelectionKey.OP_ACCEPT);System.out.println("Server started, listening on port: " + PORT);while (true) {int readyChannels = selector.select();if (readyChannels == 0) continue;Set<SelectionKey> selectedKeys = selector.selectedKeys();Iterator<SelectionKey> keyIterator = selectedKeys.iterator();while (keyIterator.hasNext()) {SelectionKey key = keyIterator.next();if (key.isAcceptable()) {ServerSocketChannel serverChannel = (ServerSocketChannel) key.channel();SocketChannel socketChannel = serverChannel.accept();socketChannel.configureBlocking(false);socketChannel.register(selector, SelectionKey.OP_READ);System.out.println("New connection accepted: " + socketChannel.getRemoteAddress());} else if (key.isReadable()) {SocketChannel socketChannel = (SocketChannel) key.channel();ByteBuffer buffer = ByteBuffer.allocate(1024);int bytesRead = socketChannel.read(buffer);if (bytesRead > 0) {buffer.flip();byte[] data = new byte[buffer.remaining()];buffer.get(data);String message = new String(data);System.out.println("Received message: " + message);} else if (bytesRead == -1) {// 客户端关闭连接socketChannel.close();System.out.println("Connection closed: " + socketChannel.getRemoteAddress());}}keyIterator.remove();}}} catch (IOException e) {e.printStackTrace();}}
}


http://www.ppmy.cn/embedded/159856.html

相关文章

4、PyTorch 第一个神经网络,手写神经网络的基本部分组成

假设有一个二维数据集&#xff0c;目标是根据点的位置将它们分类到两个类别中&#xff08;例如&#xff0c;红色和蓝色点&#xff09;。 以下实例展示了如何使用神经网络完成简单的二分类任务&#xff0c;为更复杂的任务奠定了基础&#xff0c;通过 PyTorch 的模块化接口&#…

如何使用Python调用大语言模型的API接口?

以下是使用 Python 调用几种常见大语言模型 API 接口的详细步骤和示例代码&#xff1a; 1. 调用 OpenAI 的 GPT 模型 API OpenAI 提供了强大的 GPT 系列模型&#xff0c;使用其 API 需要先注册 OpenAI 账号并获取 API 密钥。 步骤&#xff1a; 安装openai库&#xff1a;pip…

【C语言篇】“三子棋”

一、游戏介绍 三子棋&#xff0c;英文名为 Tic - Tac - Toe&#xff0c;是一款简单而经典的棋类游戏。游戏在一个 33 的棋盘上进行&#xff0c;两名玩家轮流在棋盘的空位上放置自己的棋子&#xff08;通常用 * 和 # 表示&#xff09;&#xff0c;率先在横、竖或斜方向上连成三个…

计算机视觉:解锁智能时代的钥匙与实战案例

计算机视觉&#xff1a;解锁智能时代的钥匙与实战案例 在人工智能的浩瀚星空中&#xff0c;计算机视觉无疑是最为璀璨的星辰之一。它不仅让机器拥有了“看”的能力&#xff0c;更是推动了自动驾驶、安防监控、医疗影像分析、智能制造等多个领域的革新。本文将深入探讨计算机视…

跨域问题和解决方案

跨域问题及解决方案 同源策略及跨域问题 同源策略是一套浏览器安全机制&#xff0c;当一个源的文档和脚本&#xff0c;与另一个源的资源进行通信时&#xff0c;同源策略就会对这个通信做出不同程度的限制。 简单来说&#xff0c;同源策略对 同源资源 放行&#xff0c;对 异源…

React中useState()钩子和函数式组件底层渲染流程详解

useState()钩子底层渲染流程 React中useState的底层渲染机理。首先&#xff0c;我知道useState是React Hooks的一部分&#xff0c;用于在函数组件中添加状态。但底层是如何工作的呢&#xff1f;可能涉及到React的调度器、Fiber架构以及闭包等概念。 首先&#xff0c;React使用F…

pytorch图神经网络处理图结构数据

人工智能例子汇总&#xff1a;AI常见的算法和例子-CSDN博客 图神经网络&#xff08;Graph Neural Networks&#xff0c;GNNs&#xff09;是一类能够处理图结构数据的深度学习模型。图结构数据由节点&#xff08;vertices&#xff09;和边&#xff08;edges&#xff09;组成&a…

Python从0到100(八十六):神经网络-ShuffleNet通道混合轻量级网络的深入介绍

前言&#xff1a; 零基础学Python&#xff1a;Python从0到100最新最全教程。 想做这件事情很久了&#xff0c;这次我更新了自己所写过的所有博客&#xff0c;汇集成了Python从0到100&#xff0c;共一百节课&#xff0c;帮助大家一个月时间里从零基础到学习Python基础语法、Pyth…