官方的 user-guide-for-5.x 中第一个例子 [ Writing a Discard Server ]就是最简单的例子
源代码
自带的全部的example代码都可以在官方下载的压缩文件 netty-5.0.0.Alpha1.tar.bz2 中找到,解压该文件后,jar 目录中的 netty-example-5.0.0.Alpha1-sources.jar 作为普通压缩文件解压即可看到源代码。
类的说明
io\netty\example\discard 中能找到4个类
DiscardClient.java -- 客户端入口
DiscardClientHandler.java -- 客户端 handle
DiscardServer.java -- 服务器入口
DiscardServerHandler.java -- 服务器 handle
测试
当然,你可以将源代码编译成JAR文件后,用BAT文件去驱动它,创建客户端,然后连接服务器。为了更看清楚,服务器这里可以直接在ECLIPSE里运行,这样,客户端一连上,就能在服务器端输出些什么到控制台或日志文件中,以确认服务器端真的生效了
可是,有2个问题
1 能否有更简单的客户端来测试
2 现有的服务器代码可是啥都没干,我怎么知道它是正常工作了
简易客户端
事实上,你可以用WINDOWS里自带的telnet来连接。
不过,WIN7默认不开启telnet服务,你需要手动去开启它,这个搜BAIDU可以教会你,我们就不说了。
telnet服务开启后,WIN7中“开始”上的输入栏,输入 cmd,打开的窗口中输入命令
telnet 127.0.0.1 8089
注:我的服务器端在本机,所以地址是 127.0.0.1,开启的端口号是 8089
服务器代码的简单修改
官方文档上说在你的 DiscardServerHandler 中这么写
@Override
public void channelRead(ChannelHandlerContext ctx, Object msg) {
}
问题是,我们看到的代码
@Override
public void messageReceived(ChannelHandlerContext ctx, Object msg) throws Exception {
}
方法名不同,估计是不同版本的代码吧,假设我们这么改
@Override
public void messageReceived(ChannelHandlerContext ctx, Object msg) throws Exception {
ReferenceCountUtil.release(msg); // (2)
}
嗯,会报错,象这样:
五月 23, 2014 5:30:05 下午 io.netty.example.discard.DiscardServerHandler exceptionCaught
警告: Unexpected exception from downstream.
io.netty.util.IllegalReferenceCountExc eption: refCnt: 0, decrement: 1
at io.netty.buffer.AbstractReferenceCounted ByteBuf.release(AbstractReferenceCounted ByteBuf.java:115)
at io.netty.buffer.WrappedByteBuf.release(WrappedByteBuf.java:819)
at io.netty.buffer.SimpleLeakAwareByteBuf.release(SimpleLeakAwareByteBuf.java:34)
at io.netty.util.ReferenceCountUtil.release(ReferenceCountUtil.java:68)
at io.netty.channel.SimpleChannelInboundHand ler.channelRead(SimpleChannelInboundHand ler.java:110)
at io.netty.channel.ChannelHandlerInvokerUti l.invokeChannelReadNow(ChannelHandlerInvokerUti l.java:74)
at io.netty.channel.DefaultChannelHandlerInv oker.invokeChannelRead(DefaultChannelHandlerInv oker.java:138)
at io.netty.channel.DefaultChannelHandlerCon text.fireChannelRead(DefaultChannelHandlerCon text.java:320)
at io.netty.channel.DefaultChannelPipeline.fireChannelRead(DefaultChannelPipeline.java:846)
at io.netty.channel.nio.AbstractNioByteChannel$NioByteUnsafe.read(AbstractNioByteChannel.java:127)
at io.netty.channel.nio.NioEventLoop.processSelectedKey(NioEventLoop.java:485)
at io.netty.channel.nio.NioEventLoop.processSelectedKeysOptim ized(NioEventLoop.java:452)
at io.netty.channel.nio.NioEventLoop.run(NioEventLoop.java:346)
at io.netty.util.concurrent.SingleThreadEventExecuto r$5.run(SingleThreadEventExecuto r.java:794)
at java.lang.Thread.run(Thread.java:744)
查了一下,这是因为现在的版本,你不用写这句了:
ReferenceCountUtil.release(msg);
框架自己帮你做了,你再写这句就会触发这个错误。
所以最后,大概这个方法是这样:
@Override
public void messageReceived(ChannelHandlerContext ctx, Object msg) throws Exception {
ByteBuf in = (ByteBuf) msg;
while (in.isReadable()) { // (1)
System.out.print((char) in.readByte());
System.out.flush();
}
}
这样,你输入什么,就能在服务器端输出什么了。。。
关于TCP和UDP
socket可以基于TCP,也可以基于UDP。区别在于UDP的不保证数据包都正确收到,所以性能更好,但容错不高。TCP保证不错,所以性能没那么好。
UDP基本只适合做在线视频传输之类,我们的需求应该会是TCP。
那这2种方式在写法上有什么不同?网上搜到这样的说法:
在ChannelFactory 的选择上,UDP的通信选择 NioDatagramChannelFactory,TCP的通信我们选择的是NioServerSocketChannelFactory;
在Bootstrap的选择上,UDP选择的是ConnectionlessBootstrap,而TCP选择的是ServerBootstrap。
对于编解码器decoder和Encoder,以及ChannelPipelineFactory,UDP开发与TCP并没有什么区别,在此不做详细介绍。
对于ChannelHandler,是UDP与TCP区别的核心所在。大家都知道UDP是无连接的,也就是说你通过 MessageEvent 参数对象的 getChannel() 方法获取当前会话连接,但是其 isConnected() 永远都返回 false。
UDP 开发中在消息获取事件回调方法中,获取了当前会话连接 channel 对象后可直接通过 channel 的 write 方法发送数据给对端 channel.write(message, remoteAddress),第一个参数仍然是要发送的消息对象,
第二个参数则是要发送的对端 SocketAddress 地址对象。
这里最需要注意的一点是SocketAddress,在TCP通信中我们可以通过channel.getRemoteAddress()获得,但在UDP通信中,我们必须从MessageEvent中通过调用getRemoteAddress()方法获得对端的SocketAddress 地址。
因此按这个说法看,我们这个例子就是基于TCP的。