官方的 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。 


对于编解码器decoderEncoder,以及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的。