Netty原理与基础

Netty是为了快速开发可维护的高性能、高可扩展、网络服务器和客户端程序而提供的异步事件驱动基础框架和工具。换句话说,Netty是一个Java NIO客户端/服务器框架。基于Netty,可以快速轻松地开发网络服务器和客户端的应用程序。与直接使用Java NIO相比,Netty给大家造出了一个非常优美的轮子,它可以大大简化了网络编程流程。例如,Netty极大地简化TCP、UDP套接字、HTTP Web服务程序的开发。

Netty的目标之一,是要使开发可以做到“快速和轻松”。除了做到“快速和轻松”的开发TCP/UDP等自定义协议的通信程序之外,Netty经过精心设计,还可以做到“快速和轻松”地开发应用层协议的程序,如FTP, SMTP, HTTP以及其他的传统应用层协议。

Netty的目标之二,是要做到高性能、高可扩展性。基于Java的NIO, Netty设计了一套优秀的Reactor反应器模式。后面会详细介绍Netty中反应器模式的实现。在基于Netty的反应器模式实现中的Channel(通道)、Handler(处理器)等基类,能快速扩展以覆盖不同协议、完成不同业务处理的大量应用类

1. 解密Netty中的Reactor反应器模式
设计模式是Java代码或者程序的重要组织方式,如果不了解设计模式,学习Java程序往往找不到头绪,上下求索而不得其法。故而,在学习Netty组件之前,我们必须了解Netty中的反应器模式是如何实现的。现在,现回顾一下Java NIO中IO事件的处理流程和反应器模式的基础内容。

  1. 回顾Reactor反应器模式中IO事件的处理流程

一个IO事件从操作系统底层产生后,在Reactor反应器模式中的处理流程如图6-1所示。
在这里插入图片描述
整个流程大致分为4步,具体如下:

第1步:通道注册。IO源于通道(Channel)。IO是和通道(对应于底层连接而言)强相关的。一个IO事件,一定属于某个通道。但是,如果要查询通道的事件,首先要将通道注册到选择器。只需通道提前注册到Selector选择器即可,IO事件会被选择器查询到。

第2步:查询选择。在反应器模式中,一个反应器(或者SubReactor子反应器)会负责一个线程;不断地轮询,查询选择器中的IO事件(选择键)。

第3步:事件分发。如果查询到IO事件,则分发给与IO事件有绑定关系的Handler业务处理器。

第4步:完成真正的IO操作和业务处理,这一步由Handler业务处理器负责。

以上4步,就是整个反应器模式的IO处理器流程。其中,第1步和第2步,其实是Java NIO的功能,反应器模式仅仅是利用了Java NIO的优势而已。题外话:上面的流程比较重要,是学习Netty的基础。如果这里看不懂,作为铺垫,请先回到反应器模式的详细介绍部分,回头再学习一下反应器模式。

2 Netty中的Channel通道组件

Channel通道组件是Netty中非常重要的组件,为什么首先要说的是Channel通道组件呢?原因是:反应器模式和通道紧密相关,反应器的查询和分发的IO事件都来自于Channel通道组件。

Netty中不直接使用Java NIO的Channel通道组件,对Channel通道组件进行了自己的封装。在Netty中,有一系列的Channel通道组件,为了支持多种通信协议,换句话说,对于每一种通信连接协议,Netty都实现了自己的通道。

另外一点就是,除了Java的NIO, Netty还能处理Java的面向流的OIO(Old-IO,即传统的阻塞式IO)。

总结起来,Netty中的每一种协议的通道,都有NIO(异步IO)和OIO(阻塞式IO)两个版本。

对应于不同的协议,Netty中常见的通道类型如下:·

通道类型说明是否阻塞
NioSocketChannel异步非阻塞TCP Socket传输通道异步非阻塞
NioServerSocketChannel异步非阻塞TCP Socket服务器端监听通道异步非阻塞
NioDatagramChannel异步非阻塞的UDP传输通道异步非阻塞
NioSctpChannel异步非阻塞Sctp传输通道异步非阻塞
NioSctpServerChannel异步非阻塞Sctp服务器端监听通道异步非阻塞
OioSocketChannel同步阻塞式TCP Socket传输通道同步阻塞式
OioServerSocketChannel同步阻塞式TCP Socket服务器端监听通道同步阻塞式
OioDatagramChannel同步阻塞式UDP传输通道同步阻塞式
OioSctpChannel同步阻塞式Sctp传输通道同步阻塞式
OioSctpServerChannel同步阻塞式Sctp服务器端监听通道同步阻塞式

一般来说,服务器端编程用到最多的通信协议还是TCP协议。对应的传输通道类型为NioSocketChannel类,服务器监听类为NioServerSocketChannel。在主要使用的方法上,其他的通道类型和这个NioSocketChannel类在原理上基本是相通的,因此,本书的很多案例都以NioSocketChannel通道为主。在Netty的NioSocketChannel内部封装了一个Java NIO的SelectableChannel成员。通过这个内部的Java NIO通道,Netty的NioSocketChannel通道上的IO操作,最终会落地到Java NIO的SelectableChannel底层通道。NioSocketChannel的继承关系图,如图6-2所示。
在这里插入图片描述
3 Netty中的Reactor反应器

在反应器模式中,一个反应器(或者SubReactor子反应器)会负责一个事件处理线程,不断地轮询,通过Selector选择器不断查询注册过的IO事件(选择键)。如果查询到IO事件,则分发给Handler业务处理器。

Netty中的反应器有多个实现类,与Channel通道类有关系。对应于NioSocketChannel通道,Netty的反应器类为:NioEventLoop。

NioEventLoop类绑定了两个重要的Java成员属性:一个是Thread线程类的成员,一个是Java NIO选择器的成员属性。NioEventLoop的继承关系和主要的成员属性,如下图6-3所示。

在这里插入图片描述
通过这个关系图,可以看出:NioEventLoop和前面章节讲到反应器,在思路上是一致的:一个NioEventLoop拥有一个Thread线程,负责一个Java NIO Selector选择器的IO事件轮询。

在Netty中,EventLoop反应器和Netty Channel通道,关系如何呢?理论上来说,一个EventLoopNetty反应器和NettyChannel通道是一对多的关系:一个反应器可以注册成千上万的通道。

在这里插入图片描述2.4 Netty中的Handler处理器

在前面的章节,解读Java NIO的IO事件类型时讲到,可供选择器监控的通道IO事件类型包括以下4种:

  • 可读:SelectionKey.OP_READ
  • 可写:SelectionKey.OP_WRITE
  • 连接:SelectionKey.OP_CONNECT
  • 接收:SelectionKey.OP_ACCEPT

在Netty中,EventLoop反应器内部有一个Java NIO选择器成员 执行以上事件的查询,然后进行对应的事件分发。事件分发(Dispatch)的目标就是Netty自己的Handler处理器。

Netty的Handler处理器分为两大类:第一类是ChannelInboundHandler通道入站处理器;第二类是ChannelOutboundHandler通道出站处理器。二者都继承了ChannelHandler处理器接口。Netty中的Handler处理器的接口与继承关系,如图6-5所示。

在这里插入图片描述Netty中的入站处理,不仅仅是OP_READ输入事件的处理,还是从通道底层触发,由Netty通过层层传递,调用ChannelInboundHandler通道入站处理器进行的某个处理。以底层的Java NIO中的OP_READ输入事件为例:在通道中发生了OP_READ事件后,会被EventLoop查询到,然后分发给ChannelInboundHandler通道入站处理器,调用它的入站处理的方法read。在ChannelInboundHandler通道入站处理器内部的read方法可以从通道中读取数据。

Netty中的入站处理,触发的方向为:从通道到ChannelInboundHandler通道入站处理器。

Netty中的出站处理,本来就包括Java NIO的OP_WRITE可写事件。注意,OP_WRITE可写事件是Java NIO的底层概念,它和Netty的出站处理的概念不是一个维度,Netty的出站处理是应用层维度的。那么,Netty中的出站处理,具体指的是什么呢?指的是从ChanneOutboundHandler通道出站处理器到通道的某次IO操作,例如,在应用程序完成业务处理后,可以通过ChanneOutboundHandler通道出站处理器将处理的结果写入底层通道。它的最常用的一个方法就是write()方法,把数据写入到通道。

这两个业务处理接口都有各自的默认实现:
ChannelInboundHandler的默认实现为ChannelInboundHandlerAdapter,叫作通道入站处理适配器。ChanneOutboundHandler的默认实现为ChanneloutBoundHandlerAdapter,叫作通道出站处理适配器。

这两个默认的通道处理适配器,分别实现了入站操作和出站操作的基本功能。如果要实现自己的业务处理器,不需要从零开始去实现处理器的接口,只需要继承通道处理适配器即可。

5 Netty的流水线(Pipeline)

来梳理一下Netty的反应器模式中各个组件之间的关系:

(1)反应器(或者SubReactor子反应器)和通道之间是一对多的关系:一个反应器可以查询很多个通道的IO事件。
(2)通道和Handler处理器实例之间,是多对多的关系:一个通道的IO事件被多个的Handler实例处理;一个Handler处理器实例也能绑定到很多的通道,处理多个通道的IO事件。

问题是:通道和Handler处理器实例之间的绑定关系,Netty是如何组织的呢?Netty设计了一个特殊的组件,叫作ChannelPipeline(通道流水线),它像一条管道,将绑定到一个通道的多个Handler处理器实例,串在一起,形成一条流水线。ChannelPipeline(通道流水线)的默认实现,实际上被设计成一个双向链表。所有的Handler处理器实例被包装成了双向链表的节点,被加入到了ChannelPipeline(通道流水线)中。

重点申明:一个Netty通道拥有一条Handler处理器流水线,成员的名称叫作pipeline。

问题来了:这里为什么将pipeline翻译成流水线,而不是翻译成为管道呢?这是有原因的。具体来说,与流水线内部的Handler处理器之间处理IO事件的先后次序有关。

以入站处理为例。每一个来自通道的IO事件,都会进入一次ChannelPipeline通道流水线。在进入第一个Handler处理器后,这个IO事件将按照既定的从前往后次序,在流水线上不断地向后流动,流向下一个Handler处理器。

在向后流动的过程中,会出现3种情况:
(1)如果后面还有其他Handler入站处理器,那么IO事件可以交给下一个Handler处理器,向后流动。(2)如果后面没有其他的入站处理器,这就意味着这个IO事件在此次流水线中的处理结束了。
(3)如果在流水线中间需要终止流动,可以选择不将IO事件交给下一个Handler处理器,流水线的执行也被终止了。

为什么说Handler的处理是按照既定的次序,而不是从前到后的次序呢?Netty是这样规定的:入站处理器Handler的执行次序,是从前到后;出站处理器Handler的执行次序,是从后到前。总之,IO事件在流水线上的执行次序,与IO事件的类型是有关系的,如图6-6所示。
在这里插入图片描述除了流动的方向与IO操作的类型有关之外,流动过程中经过的处理器节点的类型,也是与IO操作的类型有关。入站的IO操作只会且只能从Inbound入站处理器类型的Handler流过;出站的IO操作只会且只能从Outbound出站处理器类型的Handler流过。

总之,流水线是通道的“大管家”,为通道管理好了它的一大堆Handler“小弟”。

了解完了流水线之后,大家应该对Netty中的通道、EventLoop反应器、Handler处理器,以及三者之间的协作关系,有了一个清晰的认知和了解。至此,大家基本可以动手开发简单的Netty程序了。不过,为了方便开发者,Netty提供了一个类把上面的三个组件快速组装起来,这个系列的类叫作Bootstrap启动器。严格来说,不止一个类名字为Bootstrap,例如在服务器端的启动类叫作ServerBootstrap类。

Bootstrap启动器类

Bootstrap类是Netty提供的一个便利的工厂类,可以通过它来完成Netty的客户端或服务器端的Netty组件的组装,以及Netty程序的初始化。当然,Netty的官方解释是,完全可以不用这个Bootstrap启动器。但是,一点点去手动创建通道、完成各种设置和启动、并且注册到EventLoop,这个过程会非常麻烦。通常情况下,还是使用这个便利的Bootstrap工具类会效率更高。

在Netty中,有两个启动器类,分别用在服务器和客户端。如图6-7所示。
在这里插入图片描述
这两个启动器仅仅是使用的地方不同,它们大致的配置和使用方法都是相同的。下面以ServerBootstrap服务器启动类作为重点的介绍对象。

在介绍ServerBootstrap的服务器启动流程之前,首先介绍一下涉及到的两个基础概念:父子通道、EventLoopGroup线程组(事件循环线程组)。

3.1 父子通道

在Netty中,每一个NioSocketChannel通道所封装的是Java NIO通道,再往下就对应到了操作系统底层的socket描述符。理论上来说,操作系统底层的socket描述符分为两类:

  • 连接监听类型。连接监听类型的socket描述符,放在服务器端,它负责接收客户端的套接字连接;在服务器端,一个“连接监听类型”的socket描述符可以接受(Accept)成千上万的传输类的socket描述符。
  • 传输数据类型。数据传输类的socket描述符负责传输数据。同一条TCP的Socket传输链路,在服务器和客户端,都分别会有一个与之相对应的数据传输类型的socket描述符。

在Netty中,异步非阻塞的服务器端监听通道NioServerSocketChannel,封装在Linux底层的描述符,是“连接监听类型”socket描述符;而NioSocketChannel异步非阻塞TCP Socket传输通道,封装在底层Linux的描述符,是“数据传输类型”的socket描述符。

在Netty中,将有接收关系的NioServerSocketChannel和NioSocketChannel,叫作父子通道。其中,NioServerSocketChannel负责服务器连接监听和接收,也叫父通道(Parent Channel)。对应于每一个接收到的NioSocketChannel传输类通道,也叫子通道(Child Channel)。

2 EventLoopGroup线程组

Netty中的Reactor反应器模式,肯定不是单线程版本的反应器模式,而是多线程版本的反应器模式。Netty的多线程版本的反应器模式是如何实现的呢?

在Netty中,一个EventLoop相当于一个子反应器(SubReactor)。大家已经知道,一个NioEventLoop子反应器拥有了一个线程,同时拥有一个Java NIO选择器。Netty如何组织外层的反应器呢?答案是使用EventLoopGroup线程组。多个EventLoop线程组成一个EventLoopGroup线程组。

反过来说,Netty的EventLoopGroup线程组就是一个多线程版本的反应器。而其中的单个EventLoop线程对应于一个子反应器(SubReactor)。

Netty的程序开发不会直接使用单个EventLoop线程,而是使用EventLoopGroup线程组。EventLoopGroup的构造函数有一个参数,用于指定内部的线程数。在构造器初始化时,会按照传入的线程数量,在内部构造多个Thread线程和多个EventLoop子反应器(一个线程对应一个EventLoop子反应器),进行多线程的IO事件查询和分发。

如果使用EventLoopGroup的无参数的构造函数,没有传入线程数或者传入的线程数为0,那么EventLoopGroup内部的线程数到底是多少呢?默认的EventLoopGroup内部线程数为最大可用的CPU处理器数量的2倍。假设电脑使用的是4核的CPU,那么在内部会启动8个EventLoop线程,相当8个子反应器(SubReactor)实例。

从前文可知,为了及时接受(Accept)到新连接,在服务器端,一般有两个独立的反应器,一个反应器负责新连接的监听和接受,另一个反应器负责IO事件处理。对应到Netty服务器程序中,则是设置两个EventLoopGroup线程组,一个EventLoopGroup负责新连接的监听和接受,一个EventLoopGroup负责IO事件处理。

那么,两个反应器如何分工呢?负责新连接的监听和接受的EventLoopGroup线程组,查询父通道的IO事件,有点像负责招工的包工头,因此,可以形象地称为“包工头”(Boss)线程组。另一个EventLoopGroup线程组负责查询所有子通道的IO事件,并且执行Handler处理器中的业务处理——例如数据的输入和输出(有点儿像搬砖),这个线程组可以形象地称为“工人”(Worker)线程组。

至此,已经介绍完了两个基础概念:父子通道、EventLoopGroup线程组。下一节将介绍ServerBootstrap的启动流程。

3.3 Bootstrap的启动流程

Bootstrap的启动流程,也就是Netty组件的组装、配置,以及Netty服务器或者客户端的启动流程。在本节中对启动流程进行了梳理,大致分成了8个步骤。本书仅仅演示的是服务器端启动器的使用,用到的启动器类为ServerBootstrap。正式使用前,首先创建一个服务器端的启动器实例。

        //创建一个服务器端的启动器
        ServerBootstrap b = new ServerBootstrap();

接下来,结合前面的NettyDiscardServer服务器的程序代码,给大家详细介绍一下Bootstrap启动流程中精彩的8个步骤。

第1步:创建反应器线程组,并赋值给ServerBootstrap启动器实例

            //创建反应器线程组
            //boss线程组
            EventLoopGroupbossLoopGroup = new NioEventLoopGroup(1);
            //worker线程组
            EventLoopGroupworkerLoopGroup = new NioEventLoopGroup();
            //...
            //1 设置反应器线程组
            b.group(bossLoopGroup, workerLoopGroup);

在设置反应器线程组之前,创建了两个NioEventLoopGroup线程组,一个负责处理连接监听IO事件,名为bossLoopGroup;另一个负责数据IO事件和Handler业务处理,名为workerLoopGroup。

在线程组创建完成后,就可以配置给启动器实例,调用的方法是b.group(bossGroup, workerGroup),它一次性地给启动器配置了两大线程组。

不一定非得配置两个线程组,可以仅配置一个EventLoopGroup反应器线程组。具体的配置方法是调用b.group( workerGroup)。在这种模式下,连接监听IO事件和数据传输IO事件可能被挤在了同一个线程中处理。这样会带来一个风险:新连接的接受被更加耗时的数据传输或者业务处理所阻塞。

在服务器端,建议设置成两个线程组的工作模式。

第2步:设置通道的IO类型

Netty不止支持Java NIO,也支持阻塞式的OIO(也叫BIO, Block-IO,即阻塞式IO)。下面配置的是Java NIO类型的通道类型,方法如下:

                  //2 设置nio类型的通道
                  b.channel(NioServerSocketChannel.class);

如果确实需要指定Bootstrap的IO模型为BIO,那么这里配置上Netty的OioServerSocketChannel.class类即可。由于NIO的优势巨大,通常不会在Netty中使用BIO。

第3步:设置监听端口

                  //3 设置监听端口
                  b.localAddress(new InetSocketAddress(port));

这是最为简单的一步操作,主要是设置服务器的监听地址。

第4步:设置传输通道的配置选项

                  //4 设置通道的参数
                  b.option(ChannelOption.SO_KEEPALIVE, true);
                  b.option(ChannelOption.ALLOCATOR, PooledByteBufAllocator.DEFAULT);

这里用到了Bootstrap的option() 选项设置方法。对于服务器的Bootstrap而言,这个方法的作用是:给父通道(Parent Channel)接收连接通道设置一些选项。

如果要给子通道(Child Channel)设置一些通道选项,则需要用另外一个childOption()设置方法。

可以设置哪些通道选项(ChannelOption)呢?在上面的代码中,设置了一个底层TCP相关的选项ChannelOption.SO_KEEPALIVE。该选项表示:是否开启TCP底层心跳机制,true为开启,false为关闭。

其他的通道设置选项,参见下一小节。

第5步:装配子通道的Pipeline流水线

上一节介绍到,每一个通道的子通道,都用一条ChannelPipeline流水线。它的内部有一个双向的链表。装配流水线的方式是:将业务处理器ChannelHandler实例加入双向链表中。

装配子通道的Handler流水线调用childHandler()方法,传递一个ChannelInitializer通道初始化类的实例。在父通道成功接收一个连接,并创建成功一个子通道后,就会初始化子通道,这里配置的ChannelInitializer实例就会被调用。

在ChannelInitializer通道初始化类的实例中,有一个initChannel初始化方法,在子通道创建后会被执行到,向子通道流水线增加业务处理器。

        //5 装配子通道流水线
        	       b.childHandler(new ChannelInitializer<SocketChannel>() {
            //有连接到达时会创建一个通道的子通道,并初始化
                protected void initChannel(SocketChannelch) throws Exception {
              // 流水线管理子通道中的Handler业务处理器
                // 向子通道流水线添加一个Handler业务处理器
                ch.pipeline().addLast(new NettyDiscardHandler());
            }
        });

为什么仅装配子通道的流水线,而不需要装配父通道的流水线呢?

原因是:父通道也就是NioServerSocketChannel连接接受通道,它的内部业务处理是固定的:接受新连接后,创建子通道,然后初始化子通道,所以不需要特别的配置。如果需要完成特殊的业务处理,可以使用ServerBootstrap的handler(ChannelHandler handler)方法,为父通道设置ChannelInitializer初始化器。

说明一下,ChannelInitializer处理器器有一个泛型参数SocketChannel,它代表需要初始化的通道类型,这个类型需要和前面的启动器中设置的通道类型,一一对应起来。

第6步:开始绑定服务器新连接的监听端口

        // 6 开始绑定端口,通过调用sync同步方法阻塞直到绑定成功
        ChannelFuturechannelFuture = b.bind().sync();
        Logger.info(" 服务器启动成功,监听端口: " +
        channelFuture.channel().localAddress());

这个也很简单。b.bind()方法的功能:返回一个端口绑定Netty的异步任务channelFuture。在这里,并没有给channelFuture异步任务增加回调监听器,而是阻塞channelFuture异步任务,直到端口绑定任务执行完成。

在Netty中,所有的IO操作都是异步执行的,这就意味着任何一个IO操作会立刻返回,在返回的时候,异步任务还没有真正执行。什么时候执行完成呢?Netty中的IO操作,都会返回异步任务实例(如ChannelFuture实例),通过自我阻塞一直到ChannelFuture异步任务执行完成,或者为ChannelFuture增加事件监听器的两种方式,以获得Netty中的IO操作的真正结果。上面使用了第一种。

至此,服务器正式启动。

第7步:自我阻塞,直到通道关闭

        // 7 等待通道关闭
        // 自我阻塞,直到通道关闭的异步任务结束
        ChannelFuturecloseFuture = channelFuture.channel().closeFuture();
        closeFuture.sync();

如果要阻塞当前线程直到通道关闭,可以使用通道的closeFuture()方法,以获取通道关闭的异步任务。当通道被关闭时,closeFuture实例的sync()方法会返回。

第8步:关闭EventLoopGroup

        // 8关闭EventLoopGroup,
        // 释放掉所有资源,包括创建的反应器线程
        workerLoopGroup.shutdownGracefully();
        bossLoopGroup.shutdownGracefully();

关闭Reactor反应器线程组,同时会关闭内部的subReactor子反应器线程,也会关闭内部的Selector选择器、内部的轮询线程以及负责查询的所有的子通道。在子通道关闭后,会释放掉底层的资源,如TCP Socket文件描述符等。

3.4 ChannelOption通道选项

无论是对于NioServerSocketChannel父通道类型,还是对于NioSocketChannel子通道类型,都可以设置一系列的ChannelOption选项。在ChannelOption类中,定义了一大票通道选项。下面介绍一些常见的选项。

1.SO_RCVBUF, SO_SNDBUF

此为TCP参数。每个TCP socket(套接字)在内核中都有一个发送缓冲区和一个接收缓冲区,这两个选项就是用来设置TCP连接的这两个缓冲区大小的。TCP的全双工的工作模式以及TCP的滑动窗口便是依赖于这两个独立的缓冲区及其填充的状态。

2.TCP_NODELAY

此为TCP参数。表示立即发送数据,默认值为True(Netty默认为True,而操作系统默认为False)。该值用于设置Nagle算法的启用,该算法将小的碎片数据连接成更大的报文(或数据包)来最小化所发送报文的数量,如果需要发送一些较小的报文,则需要禁用该算法。Netty默认禁用该算法,从而最小化报文传输的延时。

说明一下:这个参数的值,与是否开启Nagle算法是相反的,设置为true表示关闭,设置为false表示开启。通俗地讲,如果要求高实时性,有数据发送时就立刻发送,就设置为true,如果需要减少发送次数和减少网络交互次数,就设置为false。

3.SO_KEEPALIVE

此为TCP参数。表示底层TCP协议的心跳机制。true为连接保持心跳,默认值为false。启用该功能时,TCP会主动探测空闲连接的有效性。可以将此功能视为TCP的心跳机制,需要注意的是:默认的心跳间隔是7200s即2小时。Netty默认关闭该功能。

4.SO_REUSEADDR

此为TCP参数。设置为true时表示地址复用,默认值为false。有四种情况需要用到这个参数设置:

  • 当有一个有相同本地地址和端口的socket1处于TIME_WAIT状态时,而我们希望启动的程序的socket2要占用该地址和端口。例如在重启服务且保持先前端口时。
  • 有多块网卡或用IP Alias技术的机器在同一端口启动多个进程,但每个进程绑定的本地IP地址不能相同。
  • 单个进程绑定相同的端口到多个socket(套接字)上,但每个socket绑定的IP地址不同。
  • 完全相同的地址和端口的重复绑定。但这只用于UDP的多播,不用于TCP。

5.SO_LINGER

此为TCP参数。表示关闭socket的延迟时间,默认值为-1,表示禁用该功能。-1表示socket.close()方法立即返回,但操作系统底层会将发送缓冲区全部发送到对端。0表示socket.close()方法立即返回,操作系统放弃发送缓冲区的数据,直接向对端发送RST包,对端收到复位错误。非0整数值表示调用socket.close()方法的线程被阻塞,直到延迟时间到来、发送缓冲区中的数据发送完毕,若超时,则对端会收到复位错误。

6.SO_BACKLOG

此为TCP参数。表示服务器端接收连接的队列长度,如果队列已满,客户端连接将被拒绝。默认值,在Windows中为200,其他操作系统为128。如果连接建立频繁,服务器处理新连接较慢,可以适当调大这个参数.

7.SO_BROADCAST此为TCP参数。表示设置广播模式。

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
Netty是一个高性能的、异步的、事件驱动的网络应用程序框架。它使用NIO(Java非阻塞IO)技术实现高效的网络通讯,因此在高并发、大数据量、网络负载大的场景下表现优异。《Netty原理解析与开发实战》一书详细地介绍了Netty框架的原理和使用方法。 该书主要分为两部分。第一部分介绍了Netty基础知识,先是NIO的介绍,然后再分别介绍了Netty的核心组件、线程模型、数据编解码、Pipeline和ChannelHandler等知识点。第二部分则是Netty的实际开发实践,包括服务端和客户端的开发、消息的发送和接收、心跳检测、HTTP协议的实现等。 具体来说,本书的核心内容包括:第一、介绍了Netty的核心架构——Reactor模型、多线程模型、EventLoop机制;第二、详细讲解了Netty中IO线程、TaskQueue、BossGroup、WorkerGroup等核心组件的作用;第三、深入探讨了Netty中的传输协议——TCP协议的实现、消息的编解码等;第四、详细介绍了Pipeline的实现机制以及ChannelHandle的分类和使用;第五、丰富的实践案例为读者提供了丰富的实战经验,包括Netty在HTTP、UDP等协议中的应用、心跳检测、应用层协议使用等。 近年来,随着大数据、物联网、云计算等技术的发展,对高性能、高可靠性的网络通信需求越来越迫切。本书的出版,将Netty框架的应用知识和实践经验系统地呈现在读者面前,对需要进行网络开发的开发工程师具有重要的参考价值。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

yitian_hm

您的支持是我最大鼓励

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值