netty消息边界问题(采用换行符)

在TCP连接开始到结束连接,之间可能会多次传输数据,也就是服务器和客户端之间可能会在连接过程中互相传输多条消息。理想状况是一方每发送一条消息,另一方就立即接收到一条,也就是一次write对应一次read。但是,现实不总是按照剧本来走。

Netty官方文档节选:

In a stream-based transport such as TCP/IP, received data is stored into a socket receive buffer. Unfortunately, the buffer of a stream-based transport is not a queue of packets but a queue of bytes. It means, even if you sent two messages as two independent packets, an operating system will not treat them as two messages but as just a bunch of bytes. Therefore, there is no guarantee that what you read is exactly what your remote peer wrote.

TCP是基于字节流的协议,它只能保证一方发送和另一方接收到的数据的字节顺序一致,但是,并不能保证一方每发送一条消息,另一方就能完整的接收到一条信息。有可能发送了两条对方将其合并成一条,也有可能发送了一条对方将其拆分成两条。

 

解决方案:

1、use fixed length messages

使用固定长度的消息。比如每个长度4字节,那么接收的时候按每条4字节拆分就可以了。

2、use a fixed length header that indicates the length of the body

使用固定长度的Header,Header中指定Body的长度(字节数),将信息的内容放在Body中。例如Header中指定的Body长度是100字节,那么Header之后的100字节就是Body,也就是信息的内容,100字节的Body后面就是下一条信息的Header了。

3、using a delimiter; for example many text-based protocols append a newline (or CR LF pair) after every message

使用分隔符。例如许多文本内容的协议会在每条消息后面加上换行符(CR LF,即"\r\n"),也就是一行一条消息。当然也可以用其他特殊符号作为分隔符,例如逗号、分号等等。

 

 

ServerBootstrap b = new ServerBootstrap(); 
        b.group(bossGroup, workerGroup) 
                .channel(NioServerSocketChannel.class) 
                .childHandler(new ChannelInitializer<SocketChannel>() { 
                    @Override 
                    public void initChannel(SocketChannel ch) 
                            throws Exception { 
                        ChannelPipeline pipeline = ch.pipeline(); 
                           
                        // LineBasedFrameDecoder按行分割消息 
                        pipeline.addLast(new LineBasedFrameDecoder(80)); 
                        // 再按UTF-8编码转成字符串 
                        pipeline.addLast(new StringDecoder(CharsetUtil.UTF_8)); 
                           
                        pipeline.addLast(new TcpServerHandler2()); 
                    } 
                }); 
        ChannelFuture f = b.bind(8080).sync(); 
        f.channel().closeFuture().sync();

 

posted @ 2018-04-19 11:27  psy5choit  阅读(1183)  评论(0编辑  收藏  举报