版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/aiyoyoyo/article/details/78722992
上篇已经讲述了如何搭建基础的服务结构,现在了解下如何完成与客户端的通讯模型。
首先,在HandlerService中,处理接收来自客户端的消息:
@Override
public void receive( ChannelHandlerContext _ctx , Object _obj ) {
ISupportCommand< ChannelHandlerContext , ByteBuf > cmd = CommonContextHolder.getBean( ISupportCommand.CMD_SERVICE );
cmd.docommand( _ctx , ( ByteBuf ) _obj );
}
然后在CommandService中处理消息,这里的消息协议是事先与客户端约定的结构、顺序等。当然你可以把ByteBuf替换为Protobuff来使用。这里不详细描述Protobuff的作用和用法了,想了解的人可以自行了解下Protobuff或者Protostuff在Netty中的用法。
@Override
public void docommand( ChannelHandlerContext _ctx , ByteBuf _buf ) {
int cmd = _buf.readInt();//假设我们只约定了一个int类型的客户端参数
System.out.println( "CommandService do command: CMD=[" + cmd + "]" );
//什么事情都没有做,将收到的内容返回客户端。
ByteBuf buf = _ctx.alloc().buffer();
buf.writeInt( cmd );
_ctx.writeAndFlush( buf );
}
基本上,针对协议的处理形式就是这样了。实际应用时,会定义不同的消息内容来完成对应的客户端请求。由于ByteBuf的读写比较繁琐,且不直观,我们会使用上述的Protobuff来代替它来作为消息传递的对象。
在使用JSTS是,会要求你使用自己的解码类来实现针对数据协议的解码粘码工作,这里我没有做相关操作,仅提供了一个消息解码类来完成解码的基本工作:
package com.jees.demo.protos;
import java.nio.ByteOrder;
import org.springframework.context.annotation.Scope;
import org.springframework.stereotype.Component;
import com.jees.jsts.netty.support.*;
import io.netty.buffer.ByteBuf;
import io.netty.channel.ChannelHandlerContext;
@Component( value = INettyHandler.NETTY_DECODER ) // 这里会注入自己的解码类到上层的服务中去
@Scope( value = INettyHandler.NETTY_CREATOR ) // 这里是因为Netty的Decoder对象要求每次都必须使用新的对象
public class MessageDecoder extends AbsNettyDecoder {
// 网络字节序,默认为大端字节序
public static final int MAX_FRAME_LENGTH = 1024 * 4;
// 消息中长度字段占用的字节数
public static final int LENGTH_FIELD_LENGTH = 4;
// 消息中长度字段偏移的字节数
private static final int LENGTH_FIELD_OFFSET = 0;
// 该字段加长度字段等于数据帧的长度
private static final int LENGTH_ADJUSTMENT = 0;
// 从数据帧中跳过的字节数
private static final int INITIAL_BYTES_TO_STRIP = 0;
public MessageDecoder() {
super( ByteOrder.LITTLE_ENDIAN , MAX_FRAME_LENGTH , LENGTH_FIELD_OFFSET , LENGTH_FIELD_LENGTH ,
LENGTH_ADJUSTMENT , INITIAL_BYTES_TO_STRIP , true );
}
@Override
protected ByteBuf decode( ChannelHandlerContext _ctx , ByteBuf _buf ) throws Exception {
return _buf;//这里什么解析工作都没有,直接通知给HandlerService处理。其实应该做解码的工作的,否则粘包不能正确处理。
}
}
下面是完整的客户端代码:
package com.jees.demo.client ;
import java.util.Random;
import io.netty.bootstrap.Bootstrap ;
import io.netty.buffer.ByteBuf;
import io.netty.channel.Channel;
import io.netty.channel.ChannelFuture ;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.ChannelOption ;
import io.netty.channel.EventLoopGroup ;
import io.netty.channel.SimpleChannelInboundHandler;
import io.netty.channel.nio.NioEventLoopGroup ;
import io.netty.channel.socket.nio.NioSocketChannel ;
public class TestNettyClient {
private EventLoopGroup worker ;
private Bootstrap booter ;
private ChannelFuture future ;
public TestNettyClient( int _cmd ) {
try {
String host = "localhost" ;
int port = 1000 ;
worker = new NioEventLoopGroup() ;
booter = new Bootstrap() ;
booter.group( worker ).channel( NioSocketChannel.class ).option( ChannelOption.SO_KEEPALIVE , true ).handler( new SimpleChannelInboundHandler< ByteBuf >() {
@Override
protected void channelRead0( ChannelHandlerContext ctx , ByteBuf buf ) throws Exception {
System.out.println( "TestNettyClient read sever msg: MSG=[" + buf.readInt() + "]" );
}
} );
future = booter.connect( host , port ).sync() ;
Channel chl = future.channel();
ByteBuf buf = chl.alloc().buffer();
buf.writeInt( _cmd );
chl.writeAndFlush( buf );
future.channel().closeFuture().sync() ;
} catch ( Exception e ) {
e.printStackTrace() ;
} finally {
worker.shutdownGracefully() ;
}
}
public static void main( String[] args ) {
new TestNettyClient( new Random().nextInt() );
}
}
最后,我们将jees-core-dispatcher.xml中的最后一行改为:
<context:component-scan base-package="com.jees.demo.*" /> <!-- 让Spring可以扫描到相关类 -->
下面是服务器和客户端的消息输出截图:
想了解更多消息的朋友,可以加入QQ群8802330,参与讨论。我会不定期更新相关内容的源代码,供各位学习和使用。
下期我将加入JDBS来进行数据的处理,喜欢的人请关注我的个人博客或者码云:https://gitee.com/aiyoyoyo/或者Github:https://github.com/aiyoyoyo。