TCP/IP学习笔记七:Netty使用–简单通信编程3
标签(空格分隔): Netty 网络编程
对于上个程序,对对象类型的传输,发送单个数据没有任何问题,运行很正常,当我们一次发送多个请求(包含多个对象)还是这样正常?会出现什么问题?
对客户端【IO事件处理类】代码进行修改:
/**
* 发送请求
*/
@Override
public void channelActive(ChannelHandlerContext ctx) throws Exception {
for (int i = 0; i < 5; i++) {
ctx.writeAndFlush(new User(i, "guozh"+i));
}
}
运行结果:
服务器端:
服务器监听8989端口
解码····
客户端发请求了···服务器接收的数据:User [id=0, name=guozh0]
编码····
客户端:
编码····
编码····
编码····
编码····
编码····
解码····
客户端接收的响应:User [id=0, name=guozh0]
为什么会出现客户端发送多次请求,而服务器端只收了一次请求和返回一次响应呢?了解TCP/IP协议的可能知道为什么,因为出现了TCP粘包/拆包现象。
TCP粘包/拆包
TCP是一个“流”协议,就是没有界限的一串数据。大家可以想想河里的流水,他们汇成一片,没有分界线。TCP底层并不了解上层业务数据的具体含义,它会根据TCP缓冲区的实际情况进行包的划分,所以在业务上认为,一个完成的包可能会被TCP拆分成多个包进行发送,也可能把多个小包封装成一个大数据包发送,这就是TCP粘包和拆包问题。
参考:Netty权威指南第四章
如图所示:
如何解决粘包/拆包问题?
服务器端【注册IO事件类】代码:
package com.netty.demo3.server;
import com.netty.demo2.ObjectCodec;
import io.netty.channel.ChannelInitializer;
import io.netty.channel.socket.SocketChannel;
import io.netty.handler.codec.LengthFieldBasedFrameDecoder;
import io.netty.handler.codec.LengthFieldPrepender;
public class ServerChannelInitializer extends ChannelInitializer<SocketChannel> {
/**
* 注册IO事件处理类
*/
@Override
protected void initChannel(SocketChannel ch) throws Exception {
//添加的长度
ch.pipeline().addLast(new LengthFieldPrepender(2));
/*
* maxFrameLength 最大的长度
* lengthFieldOffset 从那开始
* lengthFieldLength 读入长度
* lengthAdjustment
* initialBytesToStrip 截取几个字节
*/
ch.pipeline().addLast(new LengthFieldBasedFrameDecoder(65535, 0, 2, 0, 2));
ch.pipeline().addLast(new ObjectCodec());
ch.pipeline().addLast(new ServerRequestResponseHander());
}
}
客户端【注册IO事件类】代码:
package com.netty.demo3.client;
import com.netty.demo2.ObjectCodec;
import io.netty.channel.ChannelInitializer;
import io.netty.channel.socket.SocketChannel;
import io.netty.handler.codec.LengthFieldBasedFrameDecoder;
import io.netty.handler.codec.LengthFieldPrepender;
public class ClientChannelInitializer extends ChannelInitializer<SocketChannel> {
/**
* 注册IO事件处理类
*/
@Override
protected void initChannel(SocketChannel ch) throws Exception {
ch.pipeline().addLast(new LengthFieldPrepender(2));
ch.pipeline().addLast(new LengthFieldBasedFrameDecoder(65535, 0, 2, 0, 2));
ch.pipeline().addLast(new ObjectCodec());
ch.pipeline().addLast(new ClientRequestResponseHander());
}
}
其他类不做修改。
Netty给我们提供了两个方法:LengthFieldBasedFrameDecoder和LengthFieldPrepender多粘包/拆包的支持。详细介绍参考:Netty权威指南第七章
执行结果:
服务器:
服务器监听8989端口
解码····
客户端发请求了···服务器接收的数据:User [id=0, name=guozh0]
编码····
解码····
客户端发请求了···服务器接收的数据:User [id=1, name=guozh1]
编码····
解码····
客户端发请求了···服务器接收的数据:User [id=2, name=guozh2]
编码····
解码····
客户端发请求了···服务器接收的数据:User [id=3, name=guozh3]
编码····
解码····
客户端发请求了···服务器接收的数据:User [id=4, name=guozh4]
编码····
客户端:
编码····
编码····
编码····
编码····
编码····
解码····
客户端接收的响应:User [id=0, name=guozh0]
解码····
客户端接收的响应:User [id=1, name=guozh1]
解码····
客户端接收的响应:User [id=2, name=guozh2]
解码····
客户端接收的响应:User [id=3, name=guozh3]
解码····
客户端接收的响应:User [id=4, name=guozh4]
总结:
主要是对于多线程问题出现数据问题的处理。粘包和拆包,多线程下数据的传输等问题。