文档章节

netty-Server读取客户端写入

 大胖和二胖
发布于 2016/07/14 11:30
字数 833
阅读 79
收藏 0

之前的文章当中,我们研究了在客户端发起连接的时候,netty执行了怎样的动作。今天我们来研究一下,客户端发送数据过来的时候,netty具体是怎么执行读取任务的。

仍然是从NioEventLoop开始,还是NioEventLoop.processSelectedKey这个方法,

private static void processSelectedKey(SelectionKey k, AbstractNioChannel ch) {
        final NioUnsafe unsafe = ch.unsafe();
        if (!k.isValid()) {
            // close the channel if the key is not valid anymore
            unsafe.close(unsafe.voidPromise());
            return;
        }

        try {
            int readyOps = k.readyOps();
            // Also check for readOps of 0 to workaround possible JDK bug which may otherwise lead
            // to a spin loop
            if ((readyOps & (SelectionKey.OP_READ | SelectionKey.OP_ACCEPT)) != 0 || readyOps == 0) {
                unsafe.read();

                if (!ch.isOpen()) {
                    // Connection already closed - no need to handle write.
                    return;
                }
            }
            if ((readyOps & SelectionKey.OP_WRITE) != 0) {
                // Call forceFlush which will also take care of clear the OP_WRITE once there is nothing left to write
                ch.unsafe().forceFlush();
            }
            if ((readyOps & SelectionKey.OP_CONNECT) != 0) {
                // remove OP_CONNECT as otherwise Selector.select(..) will always return without blocking
                // See https://github.com/netty/netty/issues/924
                int ops = k.interestOps();
                ops &= ~SelectionKey.OP_CONNECT;
                k.interestOps(ops);

                unsafe.finishConnect();
            }
        } catch (CancelledKeyException e) {
            unsafe.close(unsafe.voidPromise());
        }
    }

红色字体部分,unsafe.read(),进入到AbstractNioByteChannel$NioByteUnsafe,要从io当中读取数据,首先需要分配一个ByteBuf,这里涉及到了2个问题,1、从哪里分配内存,2、每次具体分配多大的内存。首先是第一个问题,首先大家需要了解(allocate和allocateDirect2中方式的不同),具体的实现在 allocator.ioBuffer 当中,会根据平台不同,来决定能否直接分配系统内存。第二个问题,大家可以再看一下 allocHandle.record 方法,这个当中会根据当前从io当中读取的数据量,来决定后续分配内存的大小。然后就是从socket当中读取数据,写入bytebuf,并触发一系列的handler。

public void read() {
            final ChannelConfig config = config();
            final ChannelPipeline pipeline = pipeline();
            final ByteBufAllocator allocator = config.getAllocator();
            final int maxMessagesPerRead = config.getMaxMessagesPerRead();
            RecvByteBufAllocator.Handle allocHandle = this.allocHandle;
            if (allocHandle == null) {
                this.allocHandle = allocHandle = config.getRecvByteBufAllocator().newHandle();
            }
            if (!config.isAutoRead()) {
                removeReadOp();
            }

            ByteBuf byteBuf = null;
            int messages = 0;
            boolean close = false;
            try {
                int byteBufCapacity = allocHandle.guess();
                int totalReadAmount = 0;
                do {
                    byteBuf = allocator.ioBuffer(byteBufCapacity);
                    int writable = byteBuf.writableBytes();
                    int localReadAmount = doReadBytes(byteBuf);
                    if (localReadAmount <= 0) {
                        // not was read release the buffer
                        byteBuf.release();
                        close = localReadAmount < 0;
                        break;
                    }

                    pipeline.fireChannelRead(byteBuf);
                    byteBuf = null;

                    if (totalReadAmount >= Integer.MAX_VALUE - localReadAmount) {
                        // Avoid overflow.
                        totalReadAmount = Integer.MAX_VALUE;
                        break;
                    }

                    totalReadAmount += localReadAmount;
                    if (localReadAmount < writable) {
                        // Read less than what the buffer can hold,
                        // which might mean we drained the recv buffer completely.
                        break;
                    }
                } while (++ messages < maxMessagesPerRead);

                pipeline.fireChannelReadComplete();
                allocHandle.record(totalReadAmount);

                if (close) {
                    closeOnRead(pipeline);
                    close = false;
                }
            } catch (Throwable t) {
                handleReadException(pipeline, byteBuf, t, close);
            }
        }

到此为止,netty server的read流程基本已经清楚了。

总结:

1、在分配bytebuf的时候,会根据平台的不同,来判断能否直接分配系统内存(对应的是java的heap内存),这里我们了解到了一个概念,unsafe,这是jdk提供的一个用于执行低级别、不安全操作的方法集合。那么这个unsafe和netty当中的unsafe有没有什么关联呢?通过分析代码,我们发现,并没有直接的联系,可能只是一种名称和概念上的借用,在netty当中,把对系统io的操作,视做不安全的,并把这一系列不安全的操作进行了分装。我们在netty当中找到了这个文件,io.netty.channel.Channel,这个文件当中同时定义了2个interface, Channel和Unsafe,也就是说,从根子上来讲,unsafe和channel就是一同出现的。

2、在之前的一系列文章当中,我们从代码级别,分析了从netty server启动,到接收客户端数据的一系列动作。到目前为止,netty具体是怎么工作的,我们基本已经清楚了。

© 著作权归作者所有

粉丝 23
博文 69
码字总数 50842
作品 0
沈阳
架构师
私信 提问
HttpClient4基于Shadowsocks-netty的Socks代理

前言 最近想批量下载一些国外网站的视频,之前写过一个代理程序shadowsocks-netty,打算直接 用它来当作客户端代理程序,而HttpClient4也支持Socks代理;所有准备用HttpClient4来访问国外网站...

ksfzhaohui
2017/10/18
460
0
Twitter的RPC框架Finagle简介

Finagle是Twitter基于Netty开发的支持容错的、协议无关的RPC框架,该框架支撑了Twitter的核心服务。来自Twitter的软件工程师Jeff Smick撰文详细描述了该框架的工作原理和使用方式。 在Jeff ...

tqyin
2016/09/30
111
0
netty 学习 (4)混合使用coder和handler

该例子模拟一个Server和Client,两者之间通过http协议进行通讯,在Server内部通过一个自定义的StringDecoder把httprequest转换成String。Server端处理完成后,通过StringEncoder把String转换...

yaokangjun
2014/06/05
6.3K
0
大众点评Cat源码阅读(七)——客户端选择server的机制

一、概要思路 客户端跟服务端连接建立,分两步: 初始ChannelMananger的时候 ; ChannelManager异步线程,每隔10秒做一次检查。 1.1 初始ChannelMananger的时候 实例化ChannelManager的时候,...

liangxiao
2017/12/21
32
0
Netty 粘包/拆包应用案例及解决方案分析

熟悉TCP变成的可以知道,无论是客户端还是服务端,但我们读取或者发送消息的时候,都需要考虑TCP底层粘包/拆包机制,下面我们先看一下TCP 粘包/拆包和基础知识,然后模拟一个没有考虑TCP粘包...

Java/Python
2018/08/21
0
0

没有更多内容

加载失败,请刷新页面

加载更多

java通过ServerSocket与Socket实现通信

首先说一下ServerSocket与Socket. 1.ServerSocket ServerSocket是用来监听客户端Socket连接的类,如果没有连接会一直处于等待状态. ServetSocket有三个构造方法: (1) ServerSocket(int port);...

Blueeeeeee
今天
6
0
用 Sphinx 搭建博客时,如何自定义插件?

之前有不少同学看过我的个人博客(http://python-online.cn),也根据我写的教程完成了自己个人站点的搭建。 点此:使用 Python 30分钟 教你快速搭建一个博客 为防有的同学不清楚 Sphinx ,这...

王炳明
昨天
5
0
黑客之道-40本书籍助你快速入门黑客技术免费下载

场景 黑客是一个中文词语,皆源自英文hacker,随着灰鸽子的出现,灰鸽子成为了很多假借黑客名义控制他人电脑的黑客技术,于是出现了“骇客”与"黑客"分家。2012年电影频道节目中心出品的电影...

badaoliumang
昨天
15
0
很遗憾,没有一篇文章能讲清楚线程的生命周期!

(手机横屏看源码更方便) 注:java源码分析部分如无特殊说明均基于 java8 版本。 简介 大家都知道线程是有生命周期,但是彤哥可以认真负责地告诉你网上几乎没有一篇文章讲得是完全正确的。 ...

彤哥读源码
昨天
16
0
jquery--DOM操作基础

本文转载于:专业的前端网站➭jquery--DOM操作基础 元素的访问 元素属性操作 获取:attr(name);$("#my").attr("src"); 设置:attr(name,value);$("#myImg").attr("src","images/1.jpg"); ......

前端老手
昨天
7
0

没有更多内容

加载失败,请刷新页面

加载更多

返回顶部
顶部