非阻塞套接字编程详解

阻塞与非阻塞套接字对比

传统阻塞式套接字编程使用ServerSocketSocket类时,关键方法如connect()accept()read()write()都会导致调用线程阻塞,直到操作完成。这种模式存在两个主要问题:

  1. 客户端线程在等待数据时会被完全阻塞
  2. 服务端需要为每个客户端连接创建独立线程,资源消耗大

核心类对比

阻塞式通信类非阻塞式通信类说明
ServerSocketServerSocketChannel底层仍使用ServerSocket
SocketSocketChannel底层仍使用Socket
InputStream/Output无直接对应类通过SocketChannel进行读写
无对应类Selector事件选择器核心组件
无对应类SelectionKey表示通道注册的事件类型

非阻塞机制原理

非阻塞套接字通过三个核心组件协同工作:

// 获取选择器实例
Selector selector = Selector.open();// 创建非阻塞服务端通道
ServerSocketChannel ssChannel = ServerSocketChannel.open();
ssChannel.configureBlocking(false);  // 必须设置为非阻塞模式
ssChannel.bind(new InetSocketAddress("localhost", 19000));// 注册ACCEPT事件
ssChannel.register(selector, SelectionKey.OP_ACCEPT);

事件类型与处理

选择器支持四种事件类型,对应SelectionKey中的常量:

  1. OP_CONNECT - 客户端连接就绪
  2. OP_ACCEPT - 服务端接受新连接
  3. OP_READ - 数据可读
  4. OP_WRITE - 数据可写

典型的事件处理循环如下:

while(true) {int readyCount = selector.select();  // 阻塞直到有事件发生if(readyCount <= 0) continue;Set readyKeys = selector.selectedKeys();Iterator iter = readyKeys.iterator();while(iter.hasNext()) {SelectionKey key = iter.next();iter.remove();if(key.isAcceptable()) {// 处理新连接ServerSocketChannel ssChannel = (ServerSocketChannel)key.channel();SocketChannel clientChannel = ssChannel.accept();clientChannel.configureBlocking(false);clientChannel.register(selector, SelectionKey.OP_READ);}else if(key.isReadable()) {// 读取数据SocketChannel channel = (SocketChannel)key.channel();ByteBuffer buffer = ByteBuffer.allocate(1024);channel.read(buffer);// ...处理数据...}}
}

性能优势体现

类比快餐店运营模式:

  • 传统阻塞模式:每个顾客(客户端)需要专属服务员(线程),资源利用率低
  • 非阻塞模式:前台(Selector)统一接待,厨房(工作线程)并行处理,实现:
    • 单线程处理多连接
    • 资源按需分配
    • 无空闲线程等待

客户端实现要点

客户端同样需要遵循非阻塞模式:

SocketChannel clientChannel = SocketChannel.open();
clientChannel.configureBlocking(false);
clientChannel.connect(new InetSocketAddress("localhost", 19000));// 注册连接、读写事件
clientChannel.register(selector, SelectionKey.OP_CONNECT | SelectionKey.OP_READ | SelectionKey.OP_WRITE);// 处理连接完成事件
if(key.isConnectable()) {while(clientChannel.isConnectionPending()) {clientChannel.finishConnect();  // 完成非阻塞连接}
}

注意事项

  1. 缓冲区管理:必须配合ByteBuffer进行数据读写
  2. 字符编码:需显式处理字符集编解码
  3. 事件去重:处理完SelectionKey后需从ready集合移除
  4. 资源释放:异常时需调用key.cancel()取消注册

这种模式虽然提高了吞吐量,但也带来了编程复杂度,适合高并发但单连接数据处理量不大的场景。

核心组件与工作原理

Selector调度机制

Selector作为非阻塞I/O的核心调度中心,通过select()方法监控所有注册通道的I/O事件状态。当至少一个通道准备好进行注册的操作时,select()会返回就绪通道的数量,典型的事件处理循环结构如下:

while (true) {int readyChannels = selector.select(); // 阻塞直到有事件就绪if (readyChannels <= 0) continue;Set readyKeys = selector.selectedKeys();Iterator keyIterator = readyKeys.iterator();while (keyIterator.hasNext()) {SelectionKey key = keyIterator.next();keyIterator.remove(); // 必须显式移除已处理的keyif (key.isAcceptable()) {handleAccept(key);} else if (key.isReadable()) {handleRead(key);}}
}

四种核心操作类型

通道可注册的事件类型通过SelectionKey常量定义:

操作类型适用场景检测方法
OP_ACCEPT服务端接受新连接isAcceptable()
OP_CONNECT客户端建立连接isConnectable()
OP_READ通道数据可读isReadable()
OP_WRITE通道可写入数据isWritable()

组合注册示例:

// 客户端通道注册连接、读写事件
channel.register(selector, SelectionKey.OP_CONNECT | SelectionKey.OP_READ |SelectionKey.OP_WRITE);

SelectionKey工作机制

每个注册通道对应一个SelectionKey,包含三个重要属性:

  1. interest集合:通道关注的事件类型
  2. ready集合:当前就绪的事件类型
  3. 附加对象:可通过attach()绑定业务对象

关键方法:

// 获取关联通道
SelectableChannel channel = key.channel(

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.pswp.cn/news/907712.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

电子电路:初步认识CMOS技术

CMOS&#xff08;Complementary Metal-Oxide-Semiconductor&#xff0c;互补金属氧化物半导体&#xff09;是一种半导体技术&#xff0c;广泛应用于集成电路&#xff08;IC&#xff09;的设计和制造中。以下是关于CMOS的详细说明&#xff1a; 1. 基本概念 技术原理&#xff1a…

【11408学习记录】考研英语写作提分秘籍:2013真题邀请信精讲+万能模板套用技巧

邀请信 英语写作2013年考研英语&#xff08;一&#xff09;真题小作文题目分析写作思路第一段&#xff1a;第二段&#xff1a;锦囊妙句1&#xff1a;锦囊妙句2&#xff1a;锦囊妙句3&#xff1a;锦囊妙句5&#xff1a;锦囊妙句6&#xff1a;锦囊妙句9&#xff1a;锦囊妙句14&am…

Java 注解与反射(超详细!!!)

Java 注解与反射&#xff08;超详细&#xff01;&#xff01;&#xff01;&#xff09; 文章目录 Java 注解与反射&#xff08;超详细&#xff01;&#xff01;&#xff01;&#xff09;1.注解1.1内置注解1.1.1 SuppressWarnings注解用法 1.2 元注解1.3自定义注解 2.反射2.1 反…

用nz-tabel写一个合并表格

用nz-tabel写一个合并表格 <nz-table #basicTable [nzData]"tableSearchStatus.dataList" nzBordered><thead><tr><th>班级</th><th>姓名</th><th>年龄</th><th>电话</th></tr></thead&…

第6章 放大电路的反馈

本章基本要求 会判&#xff1a;判断电路中有无反馈及反馈的性质 会算&#xff1a;估算深度负反馈条件下的放大倍数 会引&#xff1a;根据需求引入合适的反馈 会判振消振&#xff1a;判断电路是否能稳定工作&#xff0c;会消除自激振荡。 6.1 反馈的概念及判断 一、反馈的…

ansible template 文件中如果包含{{}} 等非ansible 变量处理

在 Ansible 模板中&#xff0c;如果你的 Python 脚本里有大量 {}、f""、或者其他 Jinja 会误解析的语法&#xff0c;就需要用 {% raw %}…{% endraw %} 把它们包起来&#xff0c;只在需要替换变量的那一行单独“放行”。例如&#xff1a; {% raw %} #!/usr/bin/env …

STM32G4 电机外设篇(一) GPIO+UART

目录 一、STM32G4 电机外设篇&#xff08;一&#xff09; GPIOUART1 GPIO1.1 STM32CUBEMX 配置以及Keil代码1.2 代码和实验现象 2 UART2.1 STM32CUBEMX 配置以及Keil代码2.2 代码和实验现象 附学习参考网址欢迎大家有问题评论交流 (* ^ ω ^) 一、STM32G4 电机外设篇&#xff0…

Kotlin 中集合遍历有哪几种方式?

1 for-in 循环&#xff08;最常用&#xff09; val list listOf("A", "B", "C") for (item in list) {print("$item ") }// A B C 2 forEach 高阶函数 val list listOf("A", "B", "C") list.forEac…

尚硅谷redis7 99 springboot整合redis之连接集群

6381宕机&#xff0c;手动shutdown后在redis中&#xff0c;634自动上位变成master结点。 但是在springboot中却没有动态感知道redisCluster的最新集群消息&#xff0c;所以找不到我们要检索的数据。原因是&#xff1a;SpringBoot 2.X版本,Redis默认的连接池采用 Lettuce&#…

AI 的早期萌芽?用 Swift 演绎约翰·康威的「生命游戏」

文章目录 摘要描述题解答案题解代码分析示例测试及结果时间复杂度空间复杂度总结 摘要 你有没有想过&#xff0c;能不能通过简单的规则模拟出生与死亡&#xff1f;「生命游戏」正是这样一种充满魅力的数学模拟系统。这篇文章我们来聊聊它的规则到底有多神奇&#xff0c;并用 S…

web ui自动化工具playwright

playwright是微软开源的一款web ui自动化工具&#xff0c;该工具有很多亮点&#xff0c;解决以前困扰web UI自动化测试的很多难点。这篇博客将介绍playwright主要特点。 playwright支持录制减少了编写成本 如果要使用playwright的录制功能&#xff0c;有两种途径&#xff0c;途…

移动安全Android——客户端静态安全

一、反编译保护 测试工具 Jadx GitHub - skylot/jadx: Dex to Java decompiler PKID [下载]PKID-APP查壳工具-Android安全-看雪-安全社区|安全招聘|kanxue.com 测试流程 &#xff08;1&#xff09;通过Jadx对客户端APK文件进行反编译&#xff0c;观察是否进行代码混淆 &…

04-redis-分布式锁-edisson

1 基本概念 百度百科&#xff1a;控制分布式系统之间同步访问共享资源方式。 在分布式系统中&#xff0c;常常需要协调他们的动作。如果不同的系统或是同一个系统的不同主机之间共享了一个或一组资源&#xff0c;那么访问这些资源的时候&#xff0c;往往需要互斥来防止…

cf每日刷题

目录 String&#xff08;800&#xff09; Skibidus and Amogu&#xff08;800&#xff09; Apples in Boxes&#xff08;1100&#xff09; String&#xff08;800&#xff09; https://codeforces.com/problemset/problem/2062/A #include <iostream> #include <…

AWS WebRTC:获取ICE服务地址(part 1)

建立WebRTC连接的第二步是获取ICE服务地址。 ICE全称&#xff1a;Interactive Connectivity Establishment&#xff0c;建立互动连接。 ICE 服务地址&#xff0c;主要是 TURN 和 STUN 服务器的地址&#xff0c;用于 WebRTC 在 NAT 网络环境中协商建立连接。 上代码&#xff…

Python兴趣匹配算法:从理论到实战的进阶指南

目录 一、兴趣匹配算法的技术栈解析 1. 基础特征匹配阶段 2. 向量空间模型阶段 3. 深度学习阶段 二、工程化实践关键技术 1. 特征工程体系 2. 相似度计算优化 三、典型应用场景实现 1. 社交好友推荐系统 2. 电商商品推荐系统 四、性能优化与挑战应对 1. 计算性能优…

【C语言】讲解 程序分配的区域(新手)

目录 代码区 数据区 堆区 栈区 常量区 重点比较一下堆区与 栈区 总结&#xff1a; 前言&#xff1a; C语言程序的内存分配区域是理解其运行机制的重要部分。根据提供的多条证据&#xff0c;我们可以总结出C语言程序在运行时主要涉及以下五个关键内存区域&#xff1a; 代…

Go语言之接口与多态 -《Go语言实战指南》

接口是 Go 语言实现 多态 的核心机制。本章将帮助你理解接口的设计哲学、动态行为&#xff0c;以及它如何让 Go 实现面向接口编程的能力。 一、什么是接口&#xff1f; 接口是一组方法签名的集合&#xff0c;任何类型只要实现了接口中声明的所有方法&#xff0c;就被视为实现了…

JSR 303(即 Bean Validation)是一个通过​​注解在 Java Bean 上定义和执行验证规则​​的规范

&#x1f6e0;️ 一、JSR 303是什么&#xff1f; JSR 303&#xff08;Java Specification Requests 303&#xff09;是Java EE 6的子规范&#xff0c;全称​​Bean Validation​​。它通过注解方式对JavaBean的属性值进行标准化校验&#xff0c;例如检查非空、长度、格式等规则…

【图像处理入门】3. 几何变换基础:从平移旋转到插值魔法

摘要 掌握图像的几何变换相当于学会「图像的空间魔法」。本文将带你理解平移/旋转/缩放的数学原理&#xff0c;掌握OpenCV中warpAffine和getAffineTransform的核心用法&#xff0c;对比最近邻、双线性等插值算法的优劣。通过图像翻转、镜像、透视变换实战&#xff0c;学会用变…