2.4 写一个回声客户端
现在,所有的服务端代码都写好了,我们来创建客户端。
客户端包含以下任务:
连接服务端
写数据
等待接收从服务端返回的完全相同的数据
关闭连接
带着思考,让我们来写客户端。
2.4.1 引导客户端
引导客户端和引导服务端类似。不同的是,客户端引导连接同时接受一个host和port,而服务端只有port。
public class EchoClient {
private final String host;
private final int port;
public EchoClient(String host, int port) {
this.host = host;
this.port = port;
}
public void start()
throws Exception {
EventLoopGroup group = new NioEventLoopGroup();
try {
Bootstrap b = new Bootstrap();
b.group(group)
.channel(NioSocketChannel.class)
.remoteAddress(new InetSocketAddress(host, port))
.handler(new ChannelInitializer<SocketChannel>() {
@Override
public void initChannel(SocketChannel ch)
throws Exception {
ch.pipeline().addLast(
new EchoClientHandler());
}
});
ChannelFuture f = b.connect().sync();
f.channel().closeFuture().sync();
} finally {
group.shutdownGracefully().sync();
}
}
public static void main(String[] args)
throws Exception {
if (args.length != 2) {
System.err.println("Usage: " + EchoClient.class.getSimpleName() +
" <host> <port>"
);
return;
}
final String host = args[0];
final int port = Integer.parseInt(args[1]);
new EchoClient(host, port).start();
}
}和之前一样,这里用的也是NIO传输。值得一提的是,你使用哪种传输都没关系。你可以同时在客户端和服务端使用不同的传输。在服务端使用NIO传输在客户端使用OIO传输也是可能的。你将在第四章学习更多关于如何在特定场景何种传输的内容。
让我们重点关注以下几点:
创建一个Bootstrap实例来引导客户端
创建一个NioEventLoopGroup 实例,指派处理事件,像创建新连接、收发数据等
远程的InetSocketAddress指定客户端连接的对象
设置一个handler,一旦连接建立,它将会被执行
每件事设置完成后, 调用Bootstrap.connect()方法去连接远程端点
2.4.2 实现客户端逻辑
本节的例子依旧保持简单,因为任何至今还未覆盖到的类将在将来的章节讨论。和之前一样,写一个SimpleChannelInboundHandlerAdapter实现去处理任何需要的任务。通过覆盖我们感兴趣的三个方法来处理事件:
channActive() -- 建立与服务端的连接后调用
channelRead0()--从服务端收到数据后调用
exceptionCaught() 处理期间发生任何异常时调用
如前所述,你覆写列表中三个方法。这个三个方法是编写程序是必须实现的。channelActive()是建立与服务端的连接后调用,这里的逻辑很简单,一旦建立连接,发送一串字节到服务端。消息的内容无关紧要,本例使用的是“Netty rocks!”的编码,重写此方法可以让书记尽早写到服务端。
接下来,重写channelRead0()方法。一旦接收到该数据该接口将被调用。注意,收到的字节可能是破碎的,意味着服务端写五个字节,不能保证一次完全接受5个字节。为了接收这5个字节,channelRead0()可能被调用两次。例如,第一次被调用,一个ByteBuffer含有三个字节,第二次含有2个。唯一能保证的是,接收的顺序和发送的一致。这仅适用于TCP和其他流定向协议。
第三个是重写exceptionCaught()。用法和EchoServerHandler一致。记录Throwable日志,关闭管道,意味着和服务端的连接关闭。
你或许会想,为什么这里用SimpleChannelInboundHandler 而不是像EchoServerHandler中使用ChannelInboundHandlerAdapter。“原因是:使用ChannelInboundHandlerAdapter,你需要负责在处理完接收到的数据后,释放资源。使用ByteBuf 的时候,将调用ByteBuf.release()。SimpleChannelInboundHandler则不会出现这种情况。因为一旦channelRead0()完成后,它将释放消息。它由Netty完成,通过处理实现ReferenceCounted的所有消息”。但为什么不在EchoServerHandler中这样呢?这样做的原因是我们要回显消息,这意味着我们还不能释放它,因为在channelRead(...)返回之后写入操作可能会完成(记住写入是异步的)。写入完成后,Netty将自动释放该消息。这就是客户端所需的一切。现在是时候测试您的代码并查看实际效果了。
最后更新于
这有帮助吗?