上一篇博文中,有介绍到用换行符分割消息的方法。但是这种方法有个小问题,如果消息中本身就包含换行符,那将会将这条消息分割成两条,结果就不对了。

本文介绍另外一种消息分割方式,即上一篇博文中讲的第2条:use a fixed length header that indicates the length of the body,用一个固定字节数的Header前缀来指定Body的字节数,以此来分割消息。

上面图中Header固定为4字节,Header中保存的是一个4字节(32位)的整数,例如12即为0x0000000C,这个整数用来指定Body的长度(字节数)。当读完这么多字节的Body之后,又是下一条消息的Header。

下面分别用MINA、Netty、Twisted来实现对这种消息的切合和解码。

MINA:

MINA提供了PrefixedStringCodecFactory来对这种类型的消息进行编码解码,PrefixedStringCodecFactory默认Header的大小是4字节,当然也可以指定成1或2。

public class TcpServer {  

    public static void main(String[] args) throws IOException {
IoAcceptor acceptor = new NioSocketAcceptor(); // 4字节的Header指定Body的字节数,对这种消息的处理
acceptor.getFilterChain().addLast("codec",
new ProtocolCodecFilter(new PrefixedStringCodecFactory(Charset.forName("UTF-8")))); acceptor.setHandler(new TcpServerHandle());
acceptor.bind(new InetSocketAddress(8080));
} } class TcpServerHandle extends IoHandlerAdapter { @Override
public void exceptionCaught(IoSession session, Throwable cause)
throws Exception {
cause.printStackTrace();
} // 接收到新的数据
@Override
public void messageReceived(IoSession session, Object message)
throws Exception { String msg = (String) message;
System.out.println("messageReceived:" + msg); } @Override
public void sessionCreated(IoSession session) throws Exception {
System.out.println("sessionCreated");
} @Override
public void sessionClosed(IoSession session) throws Exception {
System.out.println("sessionClosed");
}
}

Netty:

Netty使用LengthFieldBasedFrameDecoder来处理这种消息。下面代码中的new LengthFieldBasedFrameDecoder(80, 0, 4, 0, 4)中包含5个参数,分别是int maxFrameLength, int lengthFieldOffset, int lengthFieldLength, int lengthAdjustment, int initialBytesToStrip。maxFrameLength为消息的最大长度,lengthFieldOffset为Header的位置,lengthFieldLength为Header的长度,lengthAdjustment为长度调整(默认Header中的值表示Body的长度,并不包含Header自己),initialBytesToStrip为去掉字节数(默认解码后返回Header+Body的全部内容,这里设为4表示去掉4字节的Header,只留下Body)。

public class TcpServer {  

    public static void main(String[] args) throws InterruptedException {
EventLoopGroup bossGroup = new NioEventLoopGroup();
EventLoopGroup workerGroup = new NioEventLoopGroup();
try {
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(); // LengthFieldBasedFrameDecoder按行分割消息,取出body
pipeline.addLast(new LengthFieldBasedFrameDecoder(80, 0, 4, 0, 4));
// 再按UTF-8编码转成字符串
pipeline.addLast(new StringDecoder(CharsetUtil.UTF_8)); pipeline.addLast(new TcpServerHandler());
}
});
ChannelFuture f = b.bind(8080).sync();
f.channel().closeFuture().sync();
} finally {
workerGroup.shutdownGracefully();
bossGroup.shutdownGracefully();
}
} } class TcpServerHandler extends ChannelInboundHandlerAdapter { // 接收到新的数据
@Override
public void channelRead(ChannelHandlerContext ctx, Object msg) { String message = (String) msg;
System.out.println("channelRead:" + message);
} @Override
public void channelActive(ChannelHandlerContext ctx) {
System.out.println("channelActive");
} @Override
public void channelInactive(ChannelHandlerContext ctx) {
System.out.println("channelInactive");
} @Override
public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) {
cause.printStackTrace();
ctx.close();
}
}

Twisted:

在Twisted中需要继承Int32StringReceiver,不再继承Protocol。Int32StringReceiver表示固定32位(4字节)的Header,另外还有Int16StringReceiver、Int8StringReceiver等。而需要实现的接受数据事件的方法不再是dataReceived,也不是lineReceived,而是stringReceived。

# -*- coding:utf-8 –*-  

from twisted.protocols.basic import Int32StringReceiver
from twisted.internet.protocol import Factory
from twisted.internet import reactor class TcpServerHandle(Int32StringReceiver): # 新的连接建立
def connectionMade(self):
print 'connectionMade' # 连接断开
def connectionLost(self, reason):
print 'connectionLost' # 接收到新的数据
def stringReceived(self, data):
print 'stringReceived:' + data factory = Factory()
factory.protocol = TcpServerHandle
reactor.listenTCP(8080, factory)
reactor.run()

下面是Java编写的一个客户端测试程序:

public class TcpClient {  

    public static void main(String[] args) throws IOException {  

        Socket socket = null;
DataOutputStream out = null; try { socket = new Socket("localhost", 8080);
out = new DataOutputStream(socket.getOutputStream()); // 请求服务器
String data1 = "牛顿";
byte[] outputBytes1 = data1.getBytes("UTF-8");
out.writeInt(outputBytes1.length); // write header
out.write(outputBytes1); // write body String data2 = "爱因斯坦";
byte[] outputBytes2 = data2.getBytes("UTF-8");
out.writeInt(outputBytes2.length); // write header
out.write(outputBytes2); // write body out.flush(); } finally {
// 关闭连接
out.close();
socket.close();
}
}
}

MINA服务器输出结果:

sessionCreated
messageReceived:牛顿
messageReceived:爱因斯坦
sessionClosed

Netty服务器输出结果:

channelActive
channelRead:牛顿
channelRead:爱因斯坦
channelInactive

Twisted服务器输出结果:

connectionMade
stringReceived:牛顿
stringReceived:爱因斯坦
connectionLost

MINA、Netty、Twisted一起学系列

MINA、Netty、Twisted一起学(一):实现简单的TCP服务器

MINA、Netty、Twisted一起学(二):TCP消息边界问题及按行分割消息

MINA、Netty、Twisted一起学(三):TCP消息固定大小的前缀(Header)

MINA、Netty、Twisted一起学(四):定制自己的协议

MINA、Netty、Twisted一起学(五):整合protobuf

MINA、Netty、Twisted一起学(六):session

MINA、Netty、Twisted一起学(七):发布/订阅(Publish/Subscribe)

MINA、Netty、Twisted一起学(八):HTTP服务器

MINA、Netty、Twisted一起学(九):异步IO和回调函数

MINA、Netty、Twisted一起学(十):线程模型

MINA、Netty、Twisted一起学(十一):SSL/TLS

MINA、Netty、Twisted一起学(十二):HTTPS

源码

https://github.com/wucao/mina-netty-twisted

Mina、Netty、Twisted一起学(三):TCP消息固定大小的前缀(Header)的更多相关文章

  1. Mina、Netty、Twisted一起学(二):TCP消息边界问题及按行分割消息

    在TCP连接开始到结束连接,之间可能会多次传输数据,也就是服务器和客户端之间可能会在连接过程中互相传输多条消息.理想状况是一方每发送一条消息,另一方就立即接收到一条,也就是一次write对应一次rea ...

  2. Mina、Netty、Twisted一起学(一):实现简单的TCP服务器

    MINA.Netty.Twisted为什么放在一起学习?首先,不妨先分别看一下它们官方网站对其的介绍: MINA: Apache MINA is a network application frame ...

  3. Mina、Netty、Twisted一起学(八):HTTP服务器

    HTTP协议应该是目前使用最多的应用层协议了,用浏览器打开一个网站就是使用HTTP协议进行数据传输. HTTP协议也是基于TCP协议,所以也有服务器和客户端.HTTP客户端一般是浏览器,当然还有可能是 ...

  4. Mina、Netty、Twisted一起学(十):线程模型

    要想开发一个高性能的TCP服务器,熟悉所使用框架的线程模型非常重要.MINA.Netty.Twisted本身都是高性能的网络框架,如果再搭配上高效率的代码,才能实现一个高大上的服务器.但是如果不了解它 ...

  5. Mina、Netty、Twisted一起学(九):异步IO和回调函数

    用过JavaScript或者jQuery的同学都知道,JavaScript特别是jQuery中存在大量的回调函数,例如Ajax.jQuery的动画等. $.get(url, function() { ...

  6. Mina、Netty、Twisted一起学(七):发布/订阅(Publish/Subscribe)

    消息传递有很多种方式,请求/响应(Request/Reply)是最常用的.在前面的博文的例子中,很多都是采用请求/响应的方式,当服务器接收到消息后,会立即write回写一条消息到客户端.HTTP协议也 ...

  7. Mina、Netty、Twisted一起学(六):session

    开发过Web应用的同学应该都会使用session.由于HTTP协议本身是无状态的,所以一个客户端多次访问这个web应用的多个页面,服务器无法判断多次访问的客户端是否是同一个客户端.有了session就 ...

  8. Mina、Netty、Twisted一起学(五):整合protobuf

    protobuf是谷歌的Protocol Buffers的简称,用于结构化数据和字节码之间互相转换(序列化.反序列化),一般应用于网络传输,可支持多种编程语言. protobuf如何使用这里不再介绍, ...

  9. Mina、Netty、Twisted一起学(四):定制自己的协议

    在前面的博文中,介绍一些消息分割的方案,以及MINA.Netty.Twisted针对这些方案提供的相关API.例如MINA的TextLineCodecFactory.PrefixedStringCod ...

随机推荐

  1. Leetcode 189 Rotate Array stl

    题意:将数组旋转k次,如将数组[1,2,3,4,5]旋转1次得到[2,3,4,5,1],将数组[1,2,3,4,5]旋转2次得到[3,4,5,1,2]..... 本质是将数组分成两部分a1,a2,.. ...

  2. Leetcode 303 Range Sum Query - Immutable

    题意:查询一个数组在(i,j]范围内的元素的和. 思路非常简单,做个预处理,打个表就好 拓展:可以使用树状数组来完成该统计,算法复杂度为(logn),该数据结构强力的地方是实现简单,而且能完成实时更新 ...

  3. [推荐]PMO学习贴大集合

    [推荐]PMO学习贴大集合 http://wenku.baidu.com/view/a9b19bd4240c844769eaeed9.html http://wenku.baidu.com/view/ ...

  4. Android调用浏览器打开网址遇到的问题

    我自己的手机(一加一代,升级了氢OS),然后在点击游戏内一个"隐私政策"-- 需要打开一个网页,然后就crash了.出错的信息如下: 完全是看不出来,然后我单独写了一个demo来测 ...

  5. iOS RSA 证书加密

    #import "GLQyRsa.h" #import "GLSupprot.h" #import "GLLoginViewController.h& ...

  6. mac os x安装ngigx+php fastcgi+mysql+memcache详细流程

    Part 1: MacPorts Mac上装软件常用的是MacPorts和homebrew,这个软件会很方便地提供软件的安装.装这些前先得装Xcode,Xcode在appstore上有,一个多G,下载 ...

  7. javascript一种新的对象创建方式-Object.create()

    1.Object.create() 是什么? Object.create(proto [, propertiesObject ]) 是E5中提出的一种新的对象创建方式,第一个参数是要继承的原型,如果不 ...

  8. 编译fresco源码

    同步发表于http://avenwu.net/fresco/2015/05/07/build_fresco_sample fresco出来已经有一阵子了,曾经尝试过一次clone源码编译,主要是看其自 ...

  9. Storage Systems topics and related papers

    In this post, I will distill my own ideas and my own views into a structure for a storage system cou ...

  10. 单元测试mock之mockito使用

    先来一个简单的例子来感受一下 外部接口类:TestService.java package com.yzl.mock; /** * 测试用服务 * * @author yangzhilong */ p ...