编写一个应答服务器

编写一个应答服务器

写一个Netty服务器主要由两部分组成:

  • 配置服务器功能,如线程、端口
  • 实现服务器处理程序,它包含业务逻辑,决定当有一个请求连接或接收数据时该做什么

启动服务器

通过创建ServerBootstrap对象来启动服务器,然后配置这个对象的相关选项,如端口、线程模式、事件循环,并且添加逻辑处理程序用来处理业务逻辑

package netty.example;

import io.netty.bootstrap.ServerBootstrap;
import io.netty.channel.Channel;
import io.netty.channel.ChannelFuture;
import io.netty.channel.ChannelInitializer;
import io.netty.channel.EventLoopGroup;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.nio.NioServerSocketChannel; public class EchoServer { private final int port; public EchoServer(int port) {
this.port = port;
}
public void start() throws Exception {
EventLoopGroup group = new NioEventLoopGroup();
try {
//create ServerBootstrap instance
erverBootstrap b = new ServerBootstrap();
//Specifies NIO transport, local socket address
//Adds handler to channel pipeline
b.group(group).channel(NioServerSocketChannel.class).localAddress(port)
.childHandler(new ChannelInitializer<Channel>() {
@Override
protected void initChannel(Channel ch) throws Exception {
ch.pipeline().addLast(new EchoServerHandler());
}
});
//Binds server, waits for server to close, and releases resources
ChannelFuture f = b.bind().sync();
System.out.println(EchoServer.class.getName() + "started and listen on “" + f.channel().localAddress());
f.channel().closeFuture().sync();
} finally {
group.shutdownGracefully().sync();
}
} public static void main(String[] args) throws Exception {
new EchoServer(65535).start();
}
  • HTTP消息聚合

    处理HTTP时可能接收HTTP消息片段,Netty需要缓冲直到接收完整个消息。要完成的处理HTTP消息,并且内存开销也不会很大,Netty为此提供了HttpObjectAggregator。通过HttpObjectAggregator,Netty可以聚合HTTP消息,使用FullHttpResponse和FullHttpRequest到ChannelPipeline中的下一个ChannelHandler,这就消除了断裂消息,保证了消息的完整。

    通常,使用到的基本语句如下:
pipeline.addLast("aggegator", new HttpObjectAggregator(512 * 1024));
  • HTTP压缩

    使用HTTP时建议压缩数据以减少传输流量,压缩数据会增加CPU负载,现在的硬件设施都很强大,大多数时候压缩数据时一个好主意。Netty支持“gzip”和“deflate”,为此提供了两个ChannelHandler实现分别用于压缩和解压。
pipeline.addLast("decompressor", new HttpContentDecompressor());
  • 使用HTTPS

    网络中传输的重要数据需要加密来保护,使用Netty提供的SslHandler可以很容易实现:
private final SSLContext context;
SSLEngine engine = context.createSSLEngine();
engine.setUseClientMode(client);
ChannelPipeline pipeline = ch.pipeline();
pipeline.addFirst("ssl", new SslHandler(engine));
  • 处理空闲连接和超时

    处理空闲连接和超时是网络应用程序的核心部分。当发送一条消息后,可以检测连接是否还处于活跃状态,若很长时间没用了就可以断开连接。使用心跳机制IdleStateEvent来进行检测并作出处理。

引导Netty应用程序

Netty提供了简单统一的方法来引导服务器和客户端。引导是配置Netty服务器和客户端程序的一个过程,Bootstrap允许这些应用程序很容易的重复使用。Netty程序的客户端和服务器都可以使用Bootstrap,其目的是简化编码过程,Bootstrap还提供了一个机制就是让一些组件(channels,pipeline,handlers等等)都可以在后台工作。

Netty包含了2个不同类型的引导,第一个是使用服务器的ServerBootstrap,用来接受客户端连接以及为已接受的连接创建子通道;第二个是用于客户端的Bootstrap,不接受新的连接,并且是在父通道类完成一些操作。

我们在前面讨论了许多用于客户端和服务器的知识,为了对客户端和服务器之间的关系提供了一个共同点,Netty使用AbstractBootstrap类。通过一个共同的父类,在本章中讨论的客户端和服务器的引导程序能够重复使用通用功能,而无需复制代码或逻辑。通常情况下,多个通道使用相同或非常类似的设置时有必要的。而不是为每一个通道创建一个新的引导,Netty使得AbstractBootstrap可复制。也就是说克隆一个已配置的引导,其返回的是一个可重用而无需配置的引导。Netty的克隆操作只能浅拷贝引导的EventLoopGroup,也就是说EventLoopGroup在所有的克隆的通道中是共享的。这是一个好事情,克隆的通道一般是短暂的,例如一个通道创建一个HTTP请求。

引导客户端

  • 创建Bootstrap实例使用new关键字,下面是Bootstrap的方法:

group(...),设置EventLoopGroup,EventLoopGroup用来处理所有通道的IO事件

channel(...),设置通道类型

channelFactory(...),使用ChannelFactory来设置通道类型

localAddress(...),设置本地地址,也可以通过bind(...)或connect(...)

option(ChannelOption<T>, T),设置通道选项,若使用null,则删除上一个设置的ChannelOption

attr(AttributeKey<T>, T),设置属性到Channel,若值为null,则指定键的属性被删除

handler(ChannelHandler),设置ChannelHandler用于处理请求事件

clone(),深度复制Bootstrap,Bootstrap的配置相同

remoteAddress(...),设置连接地址

connect(...),连接远程通道

bind(...),创建一个新的Channel并绑定

使用ServerBootstrap引导服务器

  • 先看看ServerBootstrap提供了哪些方法

group(...),设置EventLoopGroup事件循环组

channel(...),设置通道类型

channelFactory(...),使用ChannelFactory来设置通道类型

localAddress(...),设置本地地址,也可以通过bind(...)或connect(...)

option(ChannelOption<T>, T),设置通道选项,若使用null,则删除上一个设置的ChannelOption

childOption(ChannelOption<T>, T),设置子通道选项

attr(AttributeKey<T>, T),设置属性到Channel,若值为null,则指定键的属性被删除

childAttr(AttributeKey<T>, T),设置子通道属性

handler(ChannelHandler),设置ChannelHandler用于处理请求事件

childHandler(ChannelHandler),设置子ChannelHandler

clone(),深度复制ServerBootstrap,且配置相同

bind(...),创建一个新的Channel并绑定

child*方法是在子Channel上操作,通过ServerChannel来管理。

  • 从Channel引导客户端

    有时候需要从另一个Channel引导客户端,例如写一个代理或需要从其他系统检索数据。从其他系统获取数据时比较常见的,有很多Netty应用程序必须要和企业现有的系统集成,如Netty程序与内部系统进行身份验证,查询数据库等。

    当然,你可以创建一个新的引导,这样做没有什么不妥,只是效率不高,因为要为新创建的客户端通道使用另一个EventLoop,如果需要在已接受的通道和客户端通道之间交换数据则需要切换上下文线程。Netty对这方面进行了优化,可以讲已接受的通道通过eventLoop(...)传递到EventLoop,从而使客户端通道在相同的EventLoop里运行。这消除了额外的上下文切换工作,因为EventLoop继承于EventLoopGroup。除了消除上下文切换,还可以在不需要创建多个线程的情况下使用引导。

    为什么要共享EventLoop呢?一个EventLoop由一个线程执行,共享EventLoop可以确定所有的Channel都分配给同一线程的EventLoop,这样就避免了不同线程之间切换上下文,从而减少资源开销。

    下图显示相同的EventLoop管理两个Channel:

  • 添加多个ChannelHandler

    在所有的例子代码中,我们在引导过程中通过handler(...)或childHandler(...)都只添加了一个ChannelHandler实例,对于简单的程序可能足够,但是对于复杂的程序则无法满足需求。例如,某个程序必须支持多个协议,如HTTP、WebSocket。若在一个ChannelHandler中处理这些协议将导致一个庞大而复杂的ChannelHandler。Netty通过添加多个ChannelHandler,从而使每个ChannelHandler分工明确,结构清晰。

    Netty的一个优势是可以在ChannelPipeline中堆叠很多ChannelHandler并且可以最大程度的重用代码。如何添加多个ChannelHandler呢?Netty提供ChannelInitializer抽象类用来初始化ChannelPipeline中的ChannelHandler。ChannelInitializer是一个特殊的ChannelHandler,通道被注册到EventLoop后就会调用ChannelInitializer,并允许将ChannelHandler添加到ChannelPipeline;完成初始化通道后,这个特殊的ChannelHandler初始化器会从ChannelPipeline中自动删除。

    handler在初始化时就会执行,而childHandler会在客户端成功connect后才执行,这是两者的区别。

  • 使用通道选项和属性

    比较麻烦的是创建通道后不得不手动配置每个通道,为了避免这种情况,Netty提供了ChannelOption来帮助引导配置。这些选项会自动应用到引导创建的所有通道,可用的各种选项可以配置底层连接的详细信息,如通道“keep-alive(保持活跃)”或“timeout(超时)”的特性。

    Netty应用程序通常会与组织或公司其他的软件进行集成,在某些情况下,Netty的组件如通道、传递和Netty正常生命周期外使用;在这样的情况下并不是所有的一般属性和数据时可用的。这只是一个例子,但在这样的情况下,Netty提供了通道属性(channel attributes)。

    属性可以将数据和通道以一个安全的方式关联,这些属性只是作用于客户端和服务器的通道。例如,例如客户端请求web服务器应用程序,为了跟踪通道属于哪个用户,应用程序可以存储用的ID作为通道的一个属性。任何对象或数据都可以使用属性被关联到一个通道。

    使用ChannelOption和属性可以让事情变得很简单,例如Netty WebSocket服务器根据用户自动路由消息,通过使用属性,应用程序能在通道存储用户ID以确定消息应该发送到哪里。应用程序可以通过使用一个通道选项进一步自动化,给定时间内没有收到消息将自动断开连接。

** 获取SSLContext需要相关的keystore文件,这里没有关于 HTTPS可以查阅相关资料,这里只介绍在 Netty 中如何使用 **

Netty基础点滴的更多相关文章

  1. PHP基础点滴

    PHP基础点滴 双冒号::的用法: 双冒号操作符即作用域限定操作符Scope Resolution Operator可以访问静态.const和类中重写的属性与方法. 伪类型(pseudo-types) ...

  2. Netty基础系列(3) --彻底理解NIO

    前言 上一节中我们提到了同步异步与阻塞非阻塞的区别,知道了同步并不等于阻塞.而本节的主角NIO是一种同步非阻塞的I/O模型,并且是I/O多路复用模型.NIO在java中被称为 New I/O.它并不能 ...

  3. Netty基础系列(4) --堆外内存与零拷贝详解

    前言 到目前为止,我们知道Nio当中有三个最最核心的组件,分别是:Selelctor,Channel,Buffer.在Netty基础系列(3) --彻底理解NIO 这一篇文章中只是进行了大致的介绍. ...

  4. netty基础--基本收发

    使用maven构建一个基本的netty收发应用,作为其他应用的基础.客户端使用packet sender工具. 1  添加netty依赖 1  maven netty依赖 <dependency ...

  5. Netty基础系列(1) --linux网路I/O模型

    引言 我一直认为对于java的学习,掌握基础的性价比要远远高于使用框架,而基础知识中对于网络相关知识的掌握也是重中之重.对于一个java程序来说,无论是工作中还是面试,对于Netty的掌握都是及其重要 ...

  6. Netty基础系列(2) --彻底理解阻塞非阻塞与同步异步的区别

    引言 在进行I/O学习的时候,阻塞和非阻塞,同步和异步这几个概念常常被提及,但是很多人对这几个概念一直很模糊.要想学好Netty,这几个概念必须要掌握清楚. 同步和异步 同步与异步的区别在于,异步基于 ...

  7. 2、Netty基础

    一.前言 主要包含下面内容: 初识 Netty: 使用 Java NIO 搭建简单的客户端与服务端实现网络通讯: 使用 Netty 搭建简单的客户端与服务端实现网络通讯: Netty 底层操作与 Ja ...

  8. netty 基础

    Netty是一个高性能.异步事件驱动的NIO框架,提供了对TCP.UDP和文件传输的支持,作为一个异步NIO框架,Netty的所有IO操作都是异步非阻塞的,通过Future-Listener机制,用户 ...

  9. Netty基础-BIO/NIO/AIO

    同步阻塞IO(BIO): 我们熟知的Socket编程就是BIO,每个请求对应一个线程去处理.一个socket连接一个处理线程(这个线程负责这个Socket连接的一系列数据传输操作).阻塞的原因在于:操 ...

随机推荐

  1. 从零开始学习前端开发 — 9、标签嵌套规则及CSS常用样式覆盖

    1. 块级元素可以包含内联元素或某些块级元素,但内联元素却不能包含块级元素,它只能包含其它的内联元素: <div><h1></h1><p></p& ...

  2. Django实现组合搜索

    一.实现方法 1.纯模板语言实现 2.自定义simpletag实现(本质是简化了纯模板语言的判断) 二.基本原理 原理都是通过django路由系统,匹配url筛选条件,将筛选条件作为数据库查询结果,返 ...

  3. 详解python中的__init__与__new__方法

    一.__init__和__new__方法执行的顺序? 在面向对象中介绍了关于对象创建的过程,我们知道__new__方法先于__init__方法执行. 二.__new__方法是什么? 首先,我们先来看下 ...

  4. Spring Cloud Zuul网关 Filter、熔断、重试、高可用的使用方式。

    时间过的很快,写springcloud(十):服务网关zuul初级篇还在半年前,现在已经是2018年了,我们继续探讨Zuul更高级的使用方式. 上篇文章主要介绍了Zuul网关使用模式,以及自动转发机制 ...

  5. python_10_文件操作

    文件操作逻辑? 打开文件,赋值给一个对象 用对象操作文件 关闭文件 如何打开文件? 在windows中,默认格式gbk,python3.x默认unicode(utf-8),要指定编码值 语法: f = ...

  6. Log4j扩展使用--自定义输出

    写在前面的话 log4j支持自定义的输出.所有的输出都实现了自Appender接口.一般来说,自定义输出值需要继承AppenderSkeleton类,并实现几个方法就可以了. 写这篇博客,我主要也是想 ...

  7. [JAVA] - 从 m 个元素中随机选中 n 个

    之前业务中曾经遇到过从m个元素中选取 n 个的需求,当时只是跑循环根据长度进行随机选取,然后放入 Set 中去重,一直到收集到足够的个数. 这样做的缺点很明显,当剩下的元素个数越少的时候,选取的元素越 ...

  8. 《共享库PATH与ld.so.conf简析》

    这是摘抄<共享库PATH与ld.so.conf简析>1. 往/lib和/usr/lib里面加东西,是不用修改/etc/ld.so.conf的,但是完了之后要调一下ldconfig,不然这个 ...

  9. 【Java SE】如何用Java实现插入排序

    摘要:前面三期分别写了三篇简单排序的算法,今天来讲一点稍微难一点的排序算法-----插入排序. 基本思想: 设n个数据已经按照顺序排列好(假定从小排到大). 输入一个数据x,将其放在恰当的位置,使其顺 ...

  10. 【转】TCHAR

    TCHAR 就是当你的字符设置为什么就是什么例如: 当程序编译为    ANSI,    TCHAR 相当于 CHAR当程序编译为 UNICODE, TCHAR 相当于WCHAR char       ...