TCP粘包问题

在TCP协议中,每次发送数据的长度是不确定的,从接受方来看,可能每次接受到的数据都不完整,或者受到多个数据包。这就是粘包问题。
解决方式1:通过应用层的协议来解决粘包问题,每个应用包都带一个包头,包头指示了整个包的长度,当服务器端收到了指定的包长的数据包时,才说明获取了完整的数据包。
解决方式2:指定包的结束方法,一旦获取到指定的标识,说明获取了完整的数据包。

asio的异步读接口很容易解决粘包问题。
举个例子。假设一个数据包是由包头和包体组成的,包头4个字节,用来指示整个数据包的长度,包体才是真正的应用数据。
接受过程:先接收包头长度的数据,在收到包头数据后,从包头中获取包体长度,然后再接收包体长度的数据,这时才收到了一个完整的数据包,最后将这个数据包回调到应用层去处理。

这里给一个数据包的具体实现

class Message{ //包头里存放的数据是数据包的总长度
public:
enum {header_length = 4}; //包头长度4个字节用来存储包体长度
enum {max_body_length = 512}; //最大的包体长度可以根据实际来调节
Message() {} //默认构造
Message(): body_length(0) {} //构造
const char* data() const { return data_;} //返回整个数据包
char* data() }{ return data_;}
size_t length() const { return header_length + body_length_;} //返回数据包的长度包括包头和包体
const char* body() const { return data_ + header_length;} //返回包体数据
char* body() { return data_ + header_length;}
size_t body_length()const { return body_length_;} //返回包体长度
void body_length(size_t new_length) //设置包体长度,超出最大长度就截取为最大长度
{
body_length = new_length;
if(body_length_ > max_body_length)
body_length_ = max_body_length;
}
bool decode_header() //将包体长度从字符串转换问数字
{
char header[header_length + 1] = "";
std::strncat(header, data_, header_length); //取包头数据
body_length_ = std::atoi(header) - header_length; //获取包体长度
if(body_length_ > max_body_length) //根据包体的长度是否在最大长度范围内来判断是否有效
{
body_length_ = 0;
return false;
}
return true;
}
void encode_header() //将包体长度从数字转换问字符串
{
char header[header_length + 1] = "";
std::sprintf(header, "%4d", body_length_);
std::memcpy(data_, header, header_length);
}
private:
char data_[header_length + max_body_length]; //数据包:包括包头和包体
std::size_t body_length_; //包体长度
};

该数据包只有两个成员函数,第一是数据包内容,二是包体长度。

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
st_asio_wrapper是一组类库,功能是对boost.asio的包装(调试环境:boost-1.51.0),目的是简化boost.asio开发; 其特点是效率高、跨平台、完全异步(当然这是从boost.asio继承而来)、自动重连,数据透明传输,自动解决分包粘包问题(你可以像udp一样使用它); 注:只支持tcp协议; 教程:http://blog.csdn.net/yang79tao/article/details/7724514 2.3版更新内容: 消息(std::string包装)不再用boost::shared_ptr包装,之前有过度使用智能指针之嫌。效率上,std::string如果支持引用记数,或者编译器支持std::move语义,是没有损失的(因为也不存在内存的拷贝,反而省了智能指针使用上的开销),幸好vc支持std::move语义(虽然它不支持引用记数,linux则都支持)。这样带来一个问题,原来所有的接口中的boost::shared_ptr<std::string>数据类型,全部换成了std::string引用,升级到2.3的朋友要注意修改之前重写虚函数的签名,如果不改,则重写肯定不生效,变成了新增加虚函数了(因为签名不一样)。这样向大家道歉,接口签名以后应该不会变化了,但可能增加接口; 修复使用std::advance的一个BUG,此BUG在linux下不存在,这里顺便向大家说一下,std::advance在vc和gcc下面,语义一样,但处理方式有些不同,一定要注意; 增加了个专门用于服务端压力测试的客户端框架st_test_client,并写了一个demo test_client,可以在performance_test目录下面找到; 把连接服务端逻辑从st_client剥离出来,定义了一个新的类st_connector,st_client和st_test_client将从它继承; 增加对vc2010的支持,和编译时对编译器版本的检测,如果达不到vc2010及其以上的版本,st_asio_wrapper将直接报错。
st_asio_wrapper是一组类,功能是对boost.asio的包装(调试环境:boost-1.50.0),目的是简化boost.asio开发; 其特点是效率高、跨平台、完全异步,当然这是从boost.asio继承而来; 自动重连,数据透明传输,自动解决分包粘包问题(你可以像udp一样使用它); 注:只支持tcp协议; 教程:http://blog.csdn.net/yang79tao/article/details/7724514 1.1版更新内容: 增加了自定义数据模式的支持,可用于st_asio_wrapper server与其它客户端的通信、或者st_asio_wrapper client与其它服务端的通信;当然,两端都是st_asio_wrapper的话,就用透明传输即可(1.0版已经支持了)。 1.2版更新内容: 修复BUG:当stop_service之后,再start_service时,client_base内部某些成员变量可能没有得到复位; 服务端增加修改监听地址功能,当然仍然要在start_service之前调用set_server_addr函数。 1.3版更新内容: 增加自定义消息格式的发送,这个本来是在1.1版本实现的,结果我漏掉了,只实现了自定义消息格式的接收。 1.4版更新内容: 将打包与解包器从client_base分离出来,以简化这个日益复杂的基类; 可以在运行时修改打包解包器。 1.5版更新内容: 增加ipv6支持,默认是ipv4,服务端和客户端都通过设置一个ipv6的地址来开启这个功能; 增加了一些服务端helper函数,小改了一下客户端set_server_addr函数签名(调换了两个参数的位置以保持和服务端一样)。 1.6版更新内容: 增加了接收消息缓存(改动较大,on_msg的语义有所变化,请看开发教程第三篇)。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值