一:前言
基于 https://www.cnblogs.com/zhanyifan/p/11054738.html 进行深入
二: 正文
1. 自定义协议格式
HEADcontent-length:xxxxHEADBODYxxxxxBODY
2 建立服务端代码
2.1 建立Server端, (相比于helloworld的代码,仅仅修改了bootstrap.handler中的方法,添加入codec中给定的handler进行处理)
public ChannelFuture doAccept(int port, ChannelHandler ... acceptorHandlers) throws InterruptedException { /** * childHandler 使bootstrap 独有的方法,是用于提供处理对象的 * 可以一次性添加若干个处理逻辑,是类似责任链模式处理方式。 * 增加A,B两个处理逻辑,在处理客户端请求数据的时候,根据A->B 顺序依次处理 * ChannelInitializer 用于提供处理器的模型对象 * 其中定义了 initChannel方法,用于初始化处理逻辑责任链的。 * 可以保证服务端的bootstrap只初始化一次处理器,提供处理逻辑的重用,避免反复创建处理器对象,节约资源 */ bootstrap.childHandler(new ChannelInitializer<SocketChannel>() { @Override protected void initChannel(SocketChannel socketChannel) throws Exception { socketChannel.pipeline().addLast(new StringDecoder(StandardCharsets.UTF_8)); socketChannel.pipeline().addLast(acceptorHandlers); } }); //bind 绑定端口,可以绑定多个端口 //sync 开启监听,返回ChannelFuture,代表监听成功后的一个对应的未来结果 //可以使用此对象实现后续的服务器和客户端的交互 return bootstrap.bind(port).sync(); }
2.2 建立服务端处理逻辑
@Override public void channelRead(ChannelHandlerContext ctx,Object msg) throws Exception { String message = msg.toString(); System.out.println("service receive content" + message); message = ProtocolParser.parse(message); if(null == message){ System.out.println("message is null"); return; } System.out.println("from client:" +message); String line = "from server send"; line = ProtocolParser.transferTo(line); System.out.println("server send content:" + line); ctx.writeAndFlush(Unpooled.copiedBuffer(line.getBytes(StandardCharsets.UTF_8))); }
这里使用了一个工具类进行协议的转化
class ProtocolParser{ public static String parse(String message){ // String[] temp = message.split("HEADBODY"); temp[0] = temp[0].substring(4); temp[1] = temp[1].substring(0,(temp[1].length()-4)); int length = Integer.parseInt(temp[0].substring(temp[0].indexOf(":")+1)); if(length != temp[1].length()){ return null; }else{ return temp[1]; } } public static String transferTo(String message){ message = "HEADcontent-length:" + message.length() + "HEADBODY" + message + "BODY"; return message; } }
2.2 建立客户端代码
主要修改代码为
public ChannelFuture doRequest(String host, int port, final ChannelHandler ... handlers) throws InterruptedException { /** * 客户端的bootstrap没有childHandler,只有handler * * 在客户端必须绑定处理器,也就是必须绑定handler方法。 * 服务器必须绑定处理器,必须绑定childHandler方法 */ this.bootstrap.handler(new ChannelInitializer<SocketChannel>() { @Override protected void initChannel(SocketChannel socketChannel) throws Exception { socketChannel.pipeline().addLast(new StringDecoder(StandardCharsets.UTF_8)); socketChannel.pipeline().addLast(handlers); } }); //connect 连接 return this.bootstrap.connect(host, port).sync(); }
public static void main(String[] args) { Client4Protocol client = null; ChannelFuture future = null; try{ client = new Client4Protocol(); future = client.doRequest("localhost",9999,new Client4ProtocolHandler()); Scanner s = null; while (true){ s = new Scanner(System.in); System.out.println("enter the message to server"); String line = s.nextLine(); line = Client4ProtocolHandler.ProtocolParser.transferTo(line); System.out.println("client send content:" + line); future.channel().writeAndFlush(Unpooled.copiedBuffer(line.getBytes(StandardCharsets.UTF_8))); TimeUnit.SECONDS.sleep(1); } }catch (Exception e){ e.printStackTrace(); }finally { if(null != future){ try { future.channel().closeFuture().sync(); } catch (InterruptedException e) { e.printStackTrace(); } } if(null != client){ client.release(); } } }
客户端处理器:
@Override public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception { try{ String message = msg.toString(); System.out.println("client receive content:" + message); message = ProtocolParser.parse(message); if(null == message){ System.out.println("receive error"); return; } System.out.println("from server: " + message); }finally { ReferenceCountUtil.release(msg); } }
三:总结
通过协议进行数据的拆包和分包,可以达到自定义的效果,常见的协议有: http等协议