Java|从操作系统层面分析 Java IO 演进之路( 二 )
clientBytes = new byte[buffer.limit()
; //从缓冲区 读取到内存中 buffer.get(clientBytes); System.out.println(c.socket().getPort() + \":\" + new String(clientBytes)); //清空缓冲区 buffer.clear();).start();public static void main(String[
args) throws IOException { //new socket开启监听 ServerSocketChannel socketChannel = ServerSocketChannel.open(); socketChannel.bind(new InetSocketAddress(9090)); //设置阻塞接受客户端连接 socketChannel.configureBlocking(true); //开始client处理线程 startClientChannelHandleThread(); while (true) { //接受客户端连接; 非阻塞 , 无客户端返回null(操作系统返回-1) SocketChannel client = socketChannel.accept(); if (client == null) { //System.out.println(\"no client\");else { //设置读非阻塞 client.configureBlocking(false); int port = client.socket().getPort(); System.out.println(\"client port :\" + port); clients.add(client);1 发生的系统调用
主线程
socket(AF_INET SOCK_STREAM IPPROTO_IP) = 4bind(4 {sa_family=AF_INET sin_port=htons(9090) sin_addr=inet_addr(\"0.0.0.0\") 16) = 0listen(4 50) = 0fcntl(4 F_SETFL O_RDWR|O_NONBLOCK) = 0accept(4 0x7fe26414e680 0x7fe26c376710) = -1 EAGAIN (Resource temporarily unavailable) 有连接后 , 子线程
read(6 0x7f3f415b1c50 4096) = -1 EAGAIN (Resource temporarily unavailable)read(6 0x7f3f415b1c50 4096) = -1 EAGAIN (Resource temporarily unavailable)... 资源使用情况:
2 优缺点
优点
线程数大大减少 。
缺点
需要程序自己扫描 每个连接read , 需要 O(n)时间复杂度系统调用 (此时可能只有一个连接发送了数据) , 高频系统调用(导致CPU 用户态内核态切换)高 。 导致CPU消耗很高 。
三 多路复用器(select、poll、epoll) 改进:不需要用户扫描所有连接 , 由kernel 给出哪些连接有数据 , 然后应用从有数据的连接读取数据 。
1 epoll
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.LinkedList;import java.util.Set;/** * 多路复用socket * * @author xiangyong.ding * @version $Id: MultiplexingSocket.java v 0.1 2020年08月09日 12:19 xiangyong.ding Exp $ */public class MultiplexingSocket { static ByteBuffer buffer = ByteBuffer.allocateDirect(4096); public static void main(String[
args) throws Exception { LinkedListSocketChannelclients = new LinkedList(); //1.启动server //new socket开启监听 ServerSocketChannel socketChannel = ServerSocketChannel.open(); socketChannel.bind(new InetSocketAddress(9090)); //设置非阻塞 , 接受客户端 socketChannel.configureBlocking(false); //多路复用器(JDK包装的代理 , select /poll/epoll/kqueue) Selector selector = Selector.open(); //java自动代理 , 默认为epoll //Selector selector = PollSelectorProvider.provider().openSelector();//指定为poll //将服务端socket 注册到 多路复用器 socketChannel.register(selector SelectionKey.OP_ACCEPT); //2. 轮训多路复用器 // 先询问有没有连接如果有则返回数量以及对应的对象(fd) while (selector.select()0) { System.out.println(); SetSelectionKeyselectionKeys = selector.selectedKeys(); IteratorSelectionKeyiter = selectionKeys.iterator(); while (iter.hasNext()) { SelectionKey key = iter.next(); iter.remove(); //2.1 处理新的连接 if (key.isAcceptable()) { //接受客户端连接; 非阻塞 , 无客户端返回null(操作系统返回-1) SocketChannel client = socketChannel.accept(); //设置读非阻塞 client.configureBlocking(false); //同样 , 把client也注册到selector client.register(selector SelectionKey.OP_READ); System.out.println(\"new client : \" + client.getRemoteAddress());//2.2 处理读取数据 else if (key.isReadable()) { readDataFromSocket(key);protected static void readDataFromSocket(SelectionKey key) throws Exception { SocketChannel socketChannel = (SocketChannel) key.channel(); // 非阻塞0 表示读取到的字节数量 0或-1表示未读取到或读取异常 // 请注意:这个例子降低复杂度 , 不考虑报文大于buffer size的情况 int num = socketChannel.read(buffer); if (num0) { buffer.flip(); byte[
- 章子怡扎减龄丸子头,短裙露美腿不像生过娃,从影20年笑容依旧甜
- 三星|从“不想买”到“抢着买”,骁龙8+无线充电+IP68防水,下跌600元
- 男子带家传木盒来鉴宝,盒子从没打开过,专家:稳住,两个好消息
- 世界上最孤独的“手”,他握住一棵树长达50年,却从来没有松过手
- 互联互通|哪些无人机适合新手使用?从普宙O2到大疆Mini 3 Pro,一文读懂
- 路由器|WiFi信号增强从简到易,软硬兼施后效果明显
- 关于时间的12件事,比如为什么从三月份起要把你的钟表拨快些?
- Java|干翻小米,红米又出超大杯旗舰,120W+2K直屏+双旗舰芯
- 百度地图|Java程序员应知应会之Maven和Gradle的区别
- javascript|穿戴设备将迎变革,OPPO又增新专利,能提升模式切换效率!
