Java|从操作系统层面分析 Java IO 演进之路( 三 )
clientBytes = new byte[buffer.limit()
; //从缓冲区 读取到内存中 buffer.get(clientBytes); System.out.println(socketChannel.socket().getPort() + \":\" + new String(clientBytes)); //清空缓冲区 buffer.clear();2 发生的系统调用
启动
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)fcntl(4 F_SETFL O_RDWR|O_NONBLOCK) = 0epoll_create(256) = 7epoll_ctl(7 EPOLL_CTL_ADD 5 {EPOLLIN {u32=5 u64=4324783852322029573) = 0epoll_ctl(7 EPOLL_CTL_ADD 4 {EPOLLIN {u32=4 u64=158913789956) = 0epoll_wait(7 关于对epoll_create(对应着Java的 Selector selector = Selector.open()) 的说明 , 本质上是在内存的操作系统保留区 , 创建一个epoll数据结构 。 用于后面当有client连接时 , 向该epoll区中添加监听 。
有连接
epoll_wait(7[{EPOLLIN {u32=4 u64=158913789956
8192 -1) = 1accept(4 {sa_family=AF_INET sin_port=htons(29597) sin_addr=inet_addr(\"42.120.74.252\") [16
) = 8fcntl(8 F_SETFL O_RDWR|O_NONBLOCK) = 0epoll_ctl(7 EPOLL_CTL_ADD 8 {EPOLLIN {u32=8 u64=3212844375897800712) = 0 关于epoll_ctl (对应着Java的 client.register(selector SelectionKey.OP_READ) ) 。 其中 EPOLLIN 恰好对应着Java的 SelectionKey.OP_READ 即监听数据到达读取事件 。
客户端发送数据
epoll_wait(7[{EPOLLIN {u32=8 u64=3212844375897800712
8192 -1) = 1read(8 \"hellomultiplex\\" 4096) = 16epoll_wait(7 note:epoll_wait第四个参数-1表示block 。
poll 和 epoll 对比
根据“1.BIO”中的poll函数调用和epoll函数对比如下:
poll和epoll本质上都是同步IO ,区别于BIO的是 多路复用充分降低了 system call , 而epoll更进一步 , 再次降低了system call的时间复杂度 。
3 优缺点
优点
线程数同样很少 , 甚至可以把acceptor线程和worker线程使用同一个 。 时间复杂度低 , Java实现的Selector(在Linux OS下使用的epoll函数)支持多个clientChannel事件的一次性获取 , 且时间复杂度维持在O(1) 。 CPU使用低:得益于Selector , 我们不用向 “2.NIO”中需要自己一个个ClientChannel手动去检查事件 , 因此使得CPU使用率大大降低 。 缺点
数据处理麻烦:目前socketChannel.read 读取数据完全是基于字节的 , 当我们需要需要作为HTTP服务网关时 , 对于HTTP协议的处理完全需要自己解析 , 这是个庞大、烦杂、容易出错的工作 。 性能现有socket数据的读取(socketChannel.read(buffer))全部通过一个buffer 缓冲区来接受 , 一旦连接多起来 , 这无疑是一个单线程读取 , 性能无疑是个问题 。 那么此时buffer我们每次读取都重新new出来呢?如果每次都new出来 , 这样的内存碎片对于GC无疑是一场灾难 。 如何平衡地协调好buffer的共享 , 既保证性能 , 又保证线程安全 , 这是个难题 。 四 Netty 1 研究的目标源码(netty提供的入门example)
TelnetServer
package telnet;import io.netty.bootstrap.ServerBootstrap;import io.netty.channel.EventLoopGroup;import io.netty.channel.nio.NioEventLoopGroup;import io.netty.channel.socket.nio.NioServerSocketChannel;import io.netty.handler.logging.LogLevel;import io.netty.handler.logging.LoggingHandler;import io.netty.handler.ssl.SslContext;import io.netty.handler.ssl.SslContextBuilder;import io.netty.handler.ssl.util.SelfSignedCertificate;/** * Simplistic telnet server. */public final class TelnetServer { static final boolean SSL = System.getProperty(\"ssl\") != null; static final int PORT = Integer.parseInt(System.getProperty(\"port\" SSL? \"8992\" : \"8023\")); public static void main(String[
args) throws Exception { // Configure SSL. final SslContext sslCtx; if (SSL) { SelfSignedCertificate ssc = new SelfSignedCertificate(); sslCtx = SslContextBuilder.forServer(ssc.certificate() ssc.privateKey()).build();else { sslCtx = null;EventLoopGroup bossGroup = new NioEventLoopGroup(1); EventLoopGroup workerGroup = new NioEventLoopGroup(); try { ServerBootstrap b = new ServerBootstrap(); b.group(bossGroup workerGroup) .channel(NioServerSocketChannel.class) .handler(new LoggingHandler(LogLevel.INFO)) .childHandler(new TelnetServerInitializer(sslCtx)); b.bind(PORT).sync().channel().closeFuture().sync();finally { bossGroup.shutdownGracefully(); workerGroup.shutdownGracefully();TelnetServerHandler
- 章子怡扎减龄丸子头,短裙露美腿不像生过娃,从影20年笑容依旧甜
- 三星|从“不想买”到“抢着买”,骁龙8+无线充电+IP68防水,下跌600元
- 男子带家传木盒来鉴宝,盒子从没打开过,专家:稳住,两个好消息
- 世界上最孤独的“手”,他握住一棵树长达50年,却从来没有松过手
- 互联互通|哪些无人机适合新手使用?从普宙O2到大疆Mini 3 Pro,一文读懂
- 路由器|WiFi信号增强从简到易,软硬兼施后效果明显
- 关于时间的12件事,比如为什么从三月份起要把你的钟表拨快些?
- Java|干翻小米,红米又出超大杯旗舰,120W+2K直屏+双旗舰芯
- 百度地图|Java程序员应知应会之Maven和Gradle的区别
- javascript|穿戴设备将迎变革,OPPO又增新专利,能提升模式切换效率!
