軟件要求
- 最新版本Netty
- JDK1.6以上
寫一個(gè)Discard Server
在這個(gè)世界上最簡(jiǎn)單的協(xié)議不是 Hello world
祝闻,而是DISCARD
。該協(xié)議將一切接收到的數(shù)據(jù)全部舍棄涣觉,不做任何響應(yīng)。因此展箱,實(shí)現(xiàn)DISCARD
協(xié)議旨枯,你唯一需要做的事情是忽略所有接收到的數(shù)據(jù)
。
- 接下來(lái)混驰,我們直接從
handler
的實(shí)現(xiàn)開始編寫Discard服務(wù)攀隔,handler
主要用于處理Netty產(chǎn)生的IO事件皂贩。
package discard.server;
import io.netty.buffer.ByteBuf;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.ChannelInboundHandlerAdapter;
/**
* @ClassName DiscardHandler
* @Description TODO
* @Author xiuc_shi
* @Date 2020/8/29 下午 1:17
* @Version 1.0
**/
public class DiscardHandler extends ChannelInboundHandlerAdapter {//(1)
@Override public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {//(2)
//舍棄接收到的消息
// ((ByteBuf)msg).release();//(3)
ByteBuf in = (ByteBuf)msg;
while (in.isReadable()) {
System.out.println((char)in.readByte());
System.out.flush();
}
}
@Override public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {//(4)
super.exceptionCaught(ctx, cause);
}
}
為了看到服務(wù)器是否真的接收到數(shù)據(jù),我們直接將數(shù)據(jù)打印出來(lái)昆汹。
-
DiscardServerHandler
繼承了ChannelInboundHandlerAdapter
明刷,該類是ChannelInboundHandler
的一個(gè)實(shí)現(xiàn)。ChannelInboundHandler
提供了很多事件處理方法供你重寫满粗。 - 我們此處重寫了
channelRead()
事件處理方法辈末。當(dāng)有新消息從客戶端發(fā)出,該方法被調(diào)用來(lái)接收來(lái)自客戶端的消息映皆。在這個(gè)例子中挤聘,接收消息的類型是ByteBuf
。 - 對(duì)于DISCARD協(xié)議的實(shí)現(xiàn)捅彻,處理器會(huì)忽略接收到的消息组去。
ByteBuf
是一個(gè)引用計(jì)數(shù)對(duì)象(Reference-counted Object),因此必須通過(guò)調(diào)用release()
方法顯式釋放步淹。通常从隆,channelRead()
事件處理方法如下實(shí)現(xiàn):
@Override
public void channelRead(ChannelHandlerContext ctx, Object msg) {
try {
// Do something with msg
} finally {
ReferenceCountUtil.release(msg);
}
}
- 當(dāng)處理器在處理事件是發(fā)生異常或者出現(xiàn)IO錯(cuò)誤時(shí)缭裆,
exceptionCaught()
事件處理方法會(huì)觸發(fā)拋出Throwable
键闺。大多數(shù)情況下,應(yīng)當(dāng)記錄日志并且關(guān)閉相關(guān)的channel澈驼。實(shí)現(xiàn)不盡相同辛燥,根據(jù)你的需求,例如有時(shí)還希望在關(guān)閉連接之前發(fā)送錯(cuò)誤碼響應(yīng)消息盅藻。
- 至此购桑,我們已經(jīng)實(shí)現(xiàn)了DISCARD服務(wù)器的第一步畅铭。接下來(lái)氏淑,寫main方法用來(lái)啟動(dòng)
DiscardServerHandler
服務(wù)。
package discard.server;
import io.netty.bootstrap.ServerBootstrap;
import io.netty.channel.ChannelFuture;
import io.netty.channel.ChannelInitializer;
import io.netty.channel.ChannelOption;
import io.netty.channel.EventLoopGroup;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.SocketChannel;
import io.netty.channel.socket.nio.NioServerSocketChannel;
/**
* @ClassName Server
* @Description TODO
* @Author xiuc_shi
* @Date 2020/8/29 下午 1:39
* @Version 1.0
**/
public class DiscardServer {
private int port;
public DiscardServer(int port) {
this.port = port;
}
public void run() throws Exception{
EventLoopGroup bossGroup = new NioEventLoopGroup();//(1)
EventLoopGroup workerGroup = new NioEventLoopGroup();
try {
ServerBootstrap b = new ServerBootstrap();//(2)
b.group(bossGroup, workerGroup).channel(NioServerSocketChannel.class)//(3)
.childHandler(new ChannelInitializer<SocketChannel>() {//(4)
@Override protected void initChannel(SocketChannel socketChannel) throws Exception {
socketChannel.pipeline().addLast(new DiscardHandler());
}
}).option(ChannelOption.SO_BACKLOG, 128)//(5)
.childOption(ChannelOption.SO_KEEPALIVE, true);//(6)
//綁定端口硕噩,接收進(jìn)來(lái)的連接
ChannelFuture f = b.bind(port).sync();//(7)
//等待服務(wù)器的socket關(guān)閉
//在本例中假残,這不會(huì)發(fā)生,但你可以優(yōu)雅地關(guān)閉你的服務(wù)
f.channel().closeFuture().sync();
}finally {
workerGroup.shutdownGracefully();
bossGroup.shutdownGracefully();
}
}
public static void main(String[] args) throws Exception {
int port = 8080;
if(args.length > 0){
port = Integer.parseInt(args[0]);
}
new DiscardServer(port).run();
}
}
-
NioEventLoopGroup
是一個(gè)用于處理IO操作的多線程事件循環(huán)炉擅。Netty為不同的傳輸類型實(shí)現(xiàn)了多樣的EventLoopGroup辉懒。我們?cè)诒纠袑?shí)現(xiàn)的服務(wù)器端應(yīng)用,使用了兩個(gè)NioEventLoopGroup
谍失。其一眶俩,通常稱為boss
,接受一個(gè)進(jìn)來(lái)的連接快鱼;其二颠印,通常稱為worker
纲岭,處理boss循環(huán)器已經(jīng)接受并注冊(cè)到worker中的連接。多少個(gè)線程线罕,以及線程和Channel之間的映射取決于EventLoopGroup的實(shí)現(xiàn)止潮,或者通過(guò)構(gòu)造器配置。 -
ServerBootstrap
是一個(gè)用于配置服務(wù)器的幫助類钞楼。你可以使用channel直接配置服務(wù)器喇闸。然而,這是一個(gè)乏味的過(guò)程询件,在大多數(shù)情況下你不需要做燃乍。 - 在此處,我們指明使用
NioServerSocketChannel
類來(lái)實(shí)例化一個(gè)新的Channel來(lái)接受進(jìn)來(lái)的連接宛琅。 - 此處指明的處理器總是由新接受的channel來(lái)評(píng)估橘沥。(The handler specified here will always be evaluated by a newly accepted Channel.)
ChannelInitializer
是一個(gè)特殊的處理器,它被用于幫助用戶初始化一個(gè)新的channel夯秃。類似于你想通過(guò)添加一些處理器到channel的ChannelPipeline
以完成初始化座咆。 - 你還能夠設(shè)置參數(shù)來(lái)定制化你的Channel實(shí)現(xiàn)。本例實(shí)現(xiàn)的是TCP/IP服務(wù)器仓洼,因此可以設(shè)置socket options例如
tcpNoDelay
和keepAlive
介陶。 -
option()
是服務(wù)于NioServerSocketChannel
,接受進(jìn)來(lái)的連接色建。而childOption()
則是服務(wù)于父ServerChannel
接受的Channels哺呜。 - 最后,綁定端口然后開啟服務(wù)箕戳。
測(cè)試結(jié)果
我們使用cmd某残,輸入命令talnet localhost 8080發(fā)起請(qǐng)求,輸入1陵吸,2玻墅,3,4
壮虫,控制臺(tái)依次打印出來(lái)澳厢。