Tcp Netty客户端未从fast服务器接收消息
我有一个示例服务器/客户机应用程序,使用netty进行它们之间的tcp通信 我面临的问题是,当服务器以较慢的速度将消息推送到连接的客户机时,客户机接收所有消息,但当从服务器推送数据之间没有延迟时,客户机不会接收所有消息。这是我的示例服务器代码Tcp Netty客户端未从fast服务器接收消息,tcp,netty,broadcast,Tcp,Netty,Broadcast,我有一个示例服务器/客户机应用程序,使用netty进行它们之间的tcp通信 我面临的问题是,当服务器以较慢的速度将消息推送到连接的客户机时,客户机接收所有消息,但当从服务器推送数据之间没有延迟时,客户机不会接收所有消息。这是我的示例服务器代码 public class NettyServer { public NettyServer() throws InterruptedException { final ClientChannelHolder clientChannelHolder
public class NettyServer {
public NettyServer() throws InterruptedException {
final ClientChannelHolder clientChannelHolder = new ClientChannelHolder();
NioEventLoopGroup group = new NioEventLoopGroup();
ServerBootstrap bootstrap = new ServerBootstrap();
bootstrap.group(group).channel(NioServerSocketChannel.class)
.localAddress(new InetSocketAddress(9000)).childHandler(new ChannelInitializer<SocketChannel>() {
@Override
protected void initChannel(SocketChannel ch) throws Exception {
System.out.println("Client connected");
ch.pipeline().addLast(new ClientChannelHandler(clientChannelHolder));
}
});
ChannelFuture future = bootstrap.bind().addListener(new ChannelFutureListener(){
@Override
public void operationComplete(ChannelFuture future) throws Exception {
System.out.println("Server bound ");
}
});
synchronized (clientChannelHolder) {
// wait for client to be conncted
clientChannelHolder.wait();
}
for (int i = 0 ; i < 2; i++) {
// loop to broadcast to all connected clients
clientChannelHolder.broadCast(i);
//Thread.sleep(1000); if I add this sleep client works fine and gets all messages
}
}
public static void main(String[] args) throws InterruptedException {
NettyServer nettyServer = new NettyServer();
}
// collection for all connected clients.
public static class ClientChannelHolder {
private List<Channel> clientChannels = new LinkedList<>();
private synchronized void addClientChannel(Channel socketChannel) {
// notify the cleint is connected
notify();
clientChannels.add(socketChannel);
}
private synchronized void removeClientChannel(SocketChannel socketChannel) {
clientChannels.remove(socketChannel);
}
public void broadCast(int msgId) {
for (Channel channel : clientChannels) {
byte[] bytes = ("server msg " + msgId).getBytes(CharsetUtil.UTF_8);
System.out.println(bytes.length);
ByteBuf byteBuf = Unpooled.buffer();
byteBuf.writeByte((byte)1);
byteBuf.writeInt(1);
byteBuf.writeInt(bytes.length);
byteBuf.writeBytes(bytes);
channel.writeAndFlush(byteBuf).addListener(new GenericFutureListener<Future<? super Void>>() {
@Override
public void operationComplete(Future<? super Void> future) throws Exception {
if (future.isSuccess()) {
System.out.println("Write successfull");
}
}
});
}
}
}
public static class ClientChannelHandler extends ChannelInboundHandlerAdapter{
private final ClientChannelHolder clientChannelHolder;
public ClientChannelHandler(ClientChannelHolder clientChannelHolder) {
this.clientChannelHolder = clientChannelHolder;
}
@Override
public void channelActive(ChannelHandlerContext ctx) throws Exception {
clientChannelHolder.addClientChannel(ctx.channel());
System.out.println("Client connected");
super.channelActive(ctx);
}
@Override
public void channelInactive(ChannelHandlerContext ctx) throws Exception {
super.channelInactive(ctx);
}
@Override
public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
ByteBuf in = (ByteBuf) msg;
in.markReaderIndex();
byte version = in.readByte();
int msgType = in.readInt();
int msgLength = in.readInt();
byte[] msgArray = new byte[msgLength];
if (in.readableBytes() < msgLength) {
in.resetReaderIndex();
return;
}
in.readBytes(msgArray, 0, msgLength);
System.out.println(
"Server received: " + new String(msgArray, CharsetUtil.UTF_8));
}
}
}
公共类NettyServer{
public NettyServer()抛出InterruptedException{
最终ClientChannelHolder ClientChannelHolder=新ClientChannelHolder();
NioEventLoopGroup组=新的NioEventLoopGroup();
ServerBootstrap bootstrap=newserverbootstrap();
bootstrap.group(group.channel)(NioServerSocketChannel.class)
.localAddress(新的InetSocketAddress(9000)).childHandler(新的ChannelInitializer()){
@凌驾
受保护的void initChannel(SocketChannel ch)引发异常{
System.out.println(“客户端连接”);
ch.pipeline().addLast(新的ClientChannelHandler(clientChannelHolder));
}
});
ChannelFuture=bootstrap.bind().addListener(新的ChannelFutureListener()){
@凌驾
public void operation complete(ChannelFuture future)引发异常{
System.out.println(“服务器绑定”);
}
});
已同步(clientChannelHolder){
//等待连接客户端
clientChannelHolder.wait();
}
对于(int i=0;i<2;i++){
//循环以广播到所有连接的客户端
客户频道持有人。广播(一);
//sleep(1000);如果我添加这个sleep客户端,它可以正常工作并获取所有消息
}
}
公共静态void main(字符串[]args)引发InterruptedException{
NettyServer NettyServer=新NettyServer();
}
//收集所有连接的客户端。
公共静态类ClientChannelHolder{
private List clientChannels=new LinkedList();
专用同步void addClientChannel(通道socketChannel){
//通知cleint已连接
通知();
clientChannels.add(socketChannel);
}
专用同步void removeClientChannel(SocketChannel SocketChannel){
clientChannels.移除(socketChannel);
}
公共无效广播(int msgId){
用于(频道:客户端频道){
字节[]字节=(“服务器消息”+msgId).getBytes(CharsetUtil.UTF_8);
System.out.println(字节.长度);
ByteBuf ByteBuf=unmooled.buffer();
byteBuf.writeByte((字节)1);
byteBuf.writeInt(1);
byteBuf.writeInt(字节.长度);
byteBuf.writeBytes(字节);
channel.writeAndFlush(byteBuf).addListener(新的GenericFutureListenerTL;DR:您编写的客户端处理程序假定通过teToMessageDecoder扩展,但扩展的ChannelInboundHandlerAdapter
替代
在Netty中,有不同的方法来读取传入数据,虽然这令人困惑,但这是因为TCP的编写方式,即基于流的方式。这意味着,虽然TCP保证所有数据按顺序进入应用程序,但它不保证任何数据包大小,也不保证传入数据包的大小
对于代码,您从最简单的类扩展而来,即ChannelInboundHandlerAdapter
。这意味着您的程序在TCP向您的程序提供数据时获取所有传入数据。虽然这通常(但并不总是,请考虑数据包丢失)数据发送间隔1秒很好,当数据包发送得更频繁时,它会崩溃
要想知道为什么会出错,可以想象一个包含两个协议特定数据包的ByteBuf
,如果执行了代码,那么基本上只读取第一个数据包
解决问题
通过查看原始客户端代码,它看起来像是为扩展ByteToMessageDecoder
而编写的,而不是ChannelInboundHandlerAdapter
,从markReaderIndex
和resetReaderIndex
的使用可以看出,因此更改父类(以及重写的方法)是第一步
public class ClientChannelHandler extends ByteToMessageDecoder {
@Override
protected void decode(ChannelHandlerContext ctx, ByteBuf in, List<Object> out) {
in.markReaderIndex();
byte version = in.readByte();
int msgType = in.readInt();
int msgLength = in.readInt();
System.out.println(msgLength);
byte[] msgArray = new byte[msgLength];
if (in.readableBytes() < msgLength) {
in.resetReaderIndex();
return;
}
in.readBytes(msgArray, 0, msgLength);
System.out.println(clientId +
" Client received: " + new String(msgArray, CharsetUtil.UTF_8));
}
}
解决消息低于3的问题
与前一个问题相比,解决这个问题很容易。我们只需要在消息太小时返回
public class ClientChannelDecoderHandler extends ByteToMessageDecoder {
@Override
protected void decode(ChannelHandlerContext ctx, ByteBuf in, List<Object> out) {
if (in.readableBytes() < 1 + 4 + 4) { // byte + int + int
return;
}
in.markReaderIndex();
....
公共类ClientChannelDecoderHandler扩展ByteToMessageDecoder{
@凌驾
受保护的无效解码(ChannelHandlerContext ctx、ByteBuf输入、列表输出){
如果(in.readableBytes()<1+4+4){//byte+int+int
返回;
}
in.markReaderIndex();
....
public class ClientChannelDecoderHandler extends ByteToMessageDecoder {
@Override
protected void decode(ChannelHandlerContext ctx, ByteBuf in, List<Object> out) {
in.markReaderIndex();
byte version = in.readByte();
int msgType = in.readInt();
int msgLength = in.readInt();
System.out.println(msgLength);
byte[] msgArray = new byte[msgLength];
if (in.readableBytes() < msgLength) {
in.resetReaderIndex();
return;
}
in.readBytes(msgArray, 0, msgLength);
out.add(clientId +
" Client received: " + new String(msgArray, CharsetUtil.UTF_8));
}
}
public static class ClientChannelPrinterHandler extends ChannelInboundHandlerAdapter {
@Override
public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
System.out.println((String)msg);
}
}
// When making your bootstrap netty object:
.handler(new ChannelInitializer<SocketChannel>() {
@Override
protected void initChannel(SocketChannel ch) throws Exception {
clientChannelHolder.addClientChannel(ch);
ch.pipeline().addLast(new ClientChannelDecodeHandler(clientId));
ch.pipeline().addLast(new ClientChannelPrintingHandler(clientId));
}
});
public class ClientChannelDecoderHandler extends ByteToMessageDecoder {
@Override
protected void decode(ChannelHandlerContext ctx, ByteBuf in, List<Object> out) {
if (in.readableBytes() < 1 + 4 + 4) { // byte + int + int
return;
}
in.markReaderIndex();
....