socket接包切数据时遇到的诡异问题

最近在写一个socket方面的小程序,遇见一个我个人觉得很诡异问题。

客户端发包我用php写的,代码很简单并且我也测试过,没有任何问题,我就不贴出来献丑了,调用接口为:

function send($socket, $data)

$data参数就是php客户端要发送给服务器的数据,这个send函数会把数据切割成一个一个的片段,并给每个片段增加协议头,然后调用socket_write发送给服务器。

服务器程序用c++写的,一个简单的测试代码,功能是:连接好socket后,先尝试接收固定长度的数据,这个数据就是协议头,从协议头中取出给定好的数据长度和数字签名,然后按照数据长度获取数据,拿到数据后再和数字签名比对看看是否合法,若合法则表明本次收包没问题,接续接受下一个包。。。。

整个流程很简单,下面我贴一下我写的c++代码:

/**

* receive the data from the socket

*

* protocol format: [flag(1) | size(4) | md5(32) | data(size) ]

*

* @param socket

* @param data

*/

int Receive(int socket, std::string *data)

{

while(1)

{

//first, get the protocol header

int total = HEADERSIZE;

char header[HEADERSIZE] = {'0'};

int howMany = 0;

while(total > 0)

{

howMany = read(socket, header + howMany, total);

if(howMany < 0)

return -1;

total -= howMany;

}

//verify the flah

if('M' != header[0])

return -2;

//get the data size

char tmpLength[4];

strncpy(tmpLength, header+1, 4);

int length = atoi(tmpLength);

if(length <= 0)

return length; //if the length < 0, means something wrong happen, or if the length = 0, means the data finish.

//get the data

char buffer[length];

//memset(buffer, '0', length);

howMany = 0;

while(length > 0)

{

howMany = read(socket, buffer + howMany, length);

if(howMany < 0)

return -1;

length -= howMany;

}

//verify the data

std::string onePart(buffer);

MD5 md5(onePart);

std::string verification(md5.md5());

if(verification.compare(0, 32, header + 5, 32) != 0)

return -3;

//merge the part of data

data->append(onePart);

}

std::cout << std::endl; //i really fucking do not know why must do this code .

return 0;

}

注意看send方法的倒数第二行,我不明白为什么去掉这行,这个函数就会见鬼的在第二次(第一次竟ok)被调用时,return -3(也就是说没有通过签名验证),我排查了好久,发现只有我在为debug而增加打印代码的时候才会ok,去掉所有打印代码就又出现-3。这让我有点抓狂!最后发现只要增加个std::endl,好像就没问题了,这个std::endl是有着刷新缓冲区的作用(在记忆中有看到过),谁能来给我讲讲其中奥妙呢?

由于问题太诡异,没看明白我的描述的,可以跟帖留言,我是真心求解,不是来搞笑的,谢谢~

PS:顺便问问,c++字符串截取时,有时候最后一个字符会是007F,是为啥?我有截图:

181B85E3-150E-4946-8FDE-7342008610B9.png

补一下我的测试环境:

centOS6.3 x86_64

回答:

你这边的read写的有点问题

howMany = read(socket, header + howMany, total);

假如第一次没读全数据,howMany大小为第一次读取的数据长度

第二次读取时buffer的位置就变成了header指针+第一次读取的长度,同时howMany是第二次读取的长度。这里还没有问题。

假如还有第三次读取就会有问题,buffer的位置变成了header+第二次读取长度。正确的位置应该是header+第一次读取长度+第二次读取长度。

最后字符串截取的问题怀疑是传入字符串的时候长度没控制好。把结束符和结束符后面一个字符也给传过去了。

另外memset的时候一般都设置成0,而不是'0'。后面一个是字符0,实际的值是0x30

回答:

length = atoi(tmpLength);。。。所以你的size是用ascii的方式保存,也就是说一个数据包最多9999个字符吗?还真是奇葩协议。

strncpy这个函数不靠谱,即便你完全明白它做了什么,也不建议使用。

试试tmpLength的长度改成5,最后一个字符记得置零。

回答:

如wowh的答复,你将读取到的数据填充的到本地buffer的位置不对。当某一个的结果不是在一次read中全部读到时,数据就错乱了,自然验证码校验失败。
另外你传输的数据应该大于你预期的length,所以第一次成功。第二次会分两次读到,2.1次是读到的第一次的未读完数据,2.2次读到的是第二次数据的前面一部分。
当你加入cout<<endl后,缓冲区被刷新,这里看起来是把sock buf也给刷掉了

以上是 socket接包切数据时遇到的诡异问题 的全部内容, 来源链接: utcz.com/p/192357.html

回到顶部