focus on linux, c/c++, lua

memcpy这个不定时炸弹

从上周开始,当大规模测试商城购买道具时,偶尔会出现客户端收不到消息的情况。一开始怀疑是逻辑层出了问题,排查3天后,把问题定位在了底层的
发送网络消息这块有问题。大家都知道分析网络包是一件非常痛苦的事情,主要是费眼。
我抓取了一下包,进行大致了分析:

//
 发送方
data=0 18 6 5E 0 FFFFFFD5 5B 2E 53 5D FFFFFFCA 52 12 FFFFFFB7 FFFFFF8E 7D 1D 40 FFFFFFBC 4E 6B 46 59 FFFFFFFA C FFFFFFDF FFFFFF92 37 FFFFFFE0 3F

//
接收方
data=46 59 FFFFFFFA C FFFFFFDF FFFFFF92 37 FFFFFFE0 3F 5D FFFFFFCA 52 12 FFFFFFB7 FFFFFF8E 7D 1D 40 FFFFFFBC 4E 6B 46 59 FFFFFFFA C FFFFFFDF FFFFFF92 37 FFFFFFE0 3F

data=C FFFFFFDF FFFFFF92 37 FFFFFFE0 3F C FFFFFFDF FFFFFF92 37 FFFFFFE0 3F C FFFFFFDF FFFFFF92 37 FFFFFFE0 3F C FFFFFFDF FFFFFF92 37 FFFFFFE0 3F C FFFFFFDF FFFFFF92 37 FFFFFFE0 3F
发送方的数据和我程序解析后的数据是一致的,但是接收方的数据出错了,经过多次的对比发现,发送方和接收方的数据有交集,而且有一定规律的偏移。
最终的定论接收方在拆包和粘包时处理数据出错了。
其中有一段代码如下:
memcpy(m_pSrc, &m_pSrc[nLen], nOffset);

引用上述的网络包为用例如下:
拷贝前的数据为:
data=46 59 FFFFFFFA C FFFFFFDF FFFFFF92 37 FFFFFFE0 3F 5D FFFFFFCA 52 12 FFFFFFB7 FFFFFF8E 7D 1D 40 FFFFFFBC 4E 6B 46 59 FFFFFFFA C FFFFFFDF FFFFFF92 37 FFFFFFE0 3F
m_pSrc定义为 char m_pSrc[1024*10];
当nLen=6, nOffset=24,调用
memcpy(m_pSrc, &m_pSrc[nLen], nOffset); 
拷贝后的数据为:
data=C FFFFFFDF FFFFFF92 37 FFFFFFE0 3F C FFFFFFDF FFFFFF92 37 FFFFFFE0 3F C FFFFFFDF FFFFFF92 37 FFFFFFE0 3F C FFFFFFDF FFFFFF92 37 FFFFFFE0 3F C FFFFFFDF FFFFFF92 37 FFFFFFE0 3F
这里我均打印出了m_pSrc的前30个字节的数据。

对,问题就是出在这里了,至于为什么,现在还没有时间去仔细排查,据说是memcpy的一个使用陷阱,我换了一种方式移动数据后,貌似问题就解决了。
等我确定原因后,把答案补充在这里,目前紧张测试中。
眼睛盯着屏幕的0XABCDEF,都快花眼了,这真是一件苦逼的活。。。。 

2012-09-11
第一次更新:
用memmove取代memcpy之后,问题得到了解决,但不符合常理,因为这里基本否定了地址重叠的情况,摸索中。。。

第二次更新:
左思右想,没有想到结论的前提下,我照抄了memcpy函数,给他起了个别名
void * my_memcpy (void *dest, const void *src, size_t len)
{
char *d = (char*)dest;
const char *s = (const char*)src;
while (len--)
*d++ = *s++;
return dest;
}
改成:my_memcpy(m_pSrc, &m_pSrc[nLen], nOffset);
后,运行结果正常,我了个擦,到底发生了什么,难道系统函数不是这么写的?
付上版本号:
Linux version 2.6.32-38-generic-pae (buildd@rothera) (gcc version 4.4.3 (Ubuntu 4.4.3-4ubuntu5) ) #85-Ubuntu SMP Wed Jan 25 15:37:46 UTC 2012

posted on 2012-09-10 16:31 zuhd 阅读(3201) 评论(15)  编辑 收藏 引用 所属分类: c/c++

评论

# re: memcpy这个不定时炸弹 2012-09-11 00:21 ilvu

当memcpy有重叠时就要用memmove  回复  更多评论   

# re: memcpy这个不定时炸弹[未登录] 2012-09-11 09:04 korall

应该是重叠引致的吧。
复制时数据重叠本来就应该要注意。
相对来说,memcpy 不考虑重叠的情况,复制效率好;memmove会考虑到源于目标的重叠,效率会低一点但是能正确工作。
但支持重叠也有限,如果目标头部和源尾部重叠,那么两者都无能为力。
其实这是程序员的事情  回复  更多评论   

# re: memcpy这个不定时炸弹[未登录] 2012-09-11 09:08 korall

哦,倒是忘了,memmove 还有一种倒序拷贝,能支持目标头部与源尾部重叠的情况。  回复  更多评论   

# re: memcpy这个不定时炸弹 2012-09-11 09:14 注册好麻烦

既然是粘包,怎么会有重叠问题呢。难道一块内存还有多种用途?
这种情况一般都是在数据量大的时候,出现的小几率事件造成的,应该检查读取逻辑  回复  更多评论   

# re: memcpy这个不定时炸弹 2012-09-11 09:23 zuhd

@注册好麻烦
也有拆包的情况
  回复  更多评论   

# re: memcpy这个不定时炸弹 2012-09-14 15:51 hydra

明显的地址重叠问题,msdn上对memcpy的这种情景已经说的很清楚了:
If the source and destination overlap, the behavior of memcpy is undefined. Use memmove to handle overlapping regions.  回复  更多评论   

# re: memcpy这个不定时炸弹 2012-09-15 06:55 neutralevil

关于memcpy的实现,还真就不是你抄出来的代码那样的,现代编译器都支持所谓Intrinsics技术,像memcpy这样的函数根本不会照源码老老实实生成函数代码和函数调用代码,而是在调用处直接插入优化后的汇编代码。要探究memcpy的真正行为,最好通过调试器查看汇编代码。  回复  更多评论   

# re: memcpy这个不定时炸弹[未登录] 2014-09-23 08:24 FongLuo

char m_pSrc[1024*10];
当nLen=6, nOffset=24,调用
memcpy(m_pSrc, &m_pSrc[nLen], nOffset);
明显重叠了:
源范围:&m_pSrc[6] <==> &m_pSrc[6+24]
目的范围:&m_pSrc[0] <==> &m_pSrc[24]   回复  更多评论   


只有注册用户登录后才能发表评论。
网站导航: 博客园   IT新闻   BlogJava   知识库   博问   管理