TCP传输是提供给用户一种可靠的面向连接的数据服务,开销当然也大。在编程实践中对应于流式套接字网络程序设计了。
对于发送数据且不作论,咱们很容易遇到一个问题就是套接字在读取接收缓冲的时候,我们以什么办法来保证读取一次完整的应用数据包?
面向连接的套接字操作中,尤其说在接收消息过程中,我们是输入一数据存储来调用套接字接收消息,当有可读的内容时,会返回写入输入数据区实际字节数。这很容易造成的问题就是,读到的数据很有可能不是一个完整的包,或可以是多个数据包粘连一起了等等现象。从网上随便一搜TCP粘包现象,就一堆话题出来。当然粘包现象只是我们要考虑的一种,对与对策就是分包算法。但这里,不独细究粘包现象与分包算法。
因为除了粘包问题,同时我们接收也可能出现数据包被分片而截断等种种问题。最终我们想要的是怎么保证收取消息过程中从读出的数据内容中将独个完整的数据包分离出来,并交付于逻辑层处理。
Okay,多余的不多说。下面针对单个套接字的收取线程循环来设计相应对策。代码见下
线程循环,套接字消息收取部分代码
// the socke recv thread code
msglen = recv(s, buffer, buffsz, 0);
if (msglen>0)
{
int offset = 0;
char *compacket = NULL;// point to a complete data packet
while (compacket = get_complete_packet(buffer + offset, msglen - offset))
{
// packet_lengthof(char*) to get the length of the data packet
offset += packet_lengthof(compacket);
}
}
packet_lengthof 为从某个数据头得到该包的长度
get_complete_packet 重点函数,完整包检出代码
// function definition, the complete packet retrive from the buffer
char* get_complete_packet(char* buffer, int length)
{
if (length <= 0) return NULL;
#define MSG_MAXSIZE 1024
static char __thread packet[MSG_MAXSIZE] = {0};
static int __thread offset = 0; // TLS
int pkt_len = packet_lengthof(offset == 0 ? buffer : packet)
int szcpy = (length > pkt_len ? pkt_len : length);
memcpy(packet + offset, buffer, szcpy);
offset += szcpy;
char *packet_ret = NULL;
if (offset == pkt_len)
{
packet_ret = packet;
offset = 0;
}
return packet_ret;
}
以上代码并不是直接可执行 get_complete_packet 中用到了局部静态变量,为了使得该函数能够多线程可重入,将其标识为线程本地存储对象了,在gcc下加__thread表示。
另外对于取个完整数据包长度 packet_lengthof 没有给出定义,因为这必须依据实际应用协议来定。能够表示明白是应用层的数据包长度就不错了。
由于本人近来项目都要求使用C开发,所以写的全是些过程算法。本来我还是甚是喜欢C++的,他的种种高级特性为众多程序员所持捧,我也不例外。