使用Socket套接字绑定函数bind的一个细节
2011-11-05 21:09
316 查看
只要稍微接触Socket套接字进行过网络编程的人,对Socket客户端调用流程或服务端调用流程都会很熟悉,传输层协议采用TCP也好,或UDP也罢。但是要写好这套“固化”的流程代码,如果稍不小心,或缺乏经历,还是很容易犯错误的,尤其当项目程序在迭代开发过程中,功能越来越多,也越来越复杂的情况下。
就在前几天,我碰到了这样一个细节问题。
协议栈Demo程序中有个设备校时的功能,采用NTP协议进行设备间的时钟同步。Demo程序在我和一位同事的PC (操作系统为MS XP)上测试时钟同步,怎么测,都没问题。但是拿到Windows 7系统下,却可能会发生问题,后来我在Windows Server 2003系统下,也碰到这个问题。
Demo A要向 Demo B发送登录请求,登出......并发送时钟同步请求(A向B登录请求时创建的套接字和A向B时钟同步请求创建的套接字不同),A 也要向Demo C登录;B向C登录。在Windows 7系统下,若打开A和B,A直接向B时钟同步请求,结果成功;若先打开A和C,A向C登录成功后,再打开B,A向B时钟同步请求,则失败。但该问题在开发环境PC XP系统下不会发生,后来在和另一位同事的共同努力下,最终找到了问题所在,并改掉了此Bug,不亦乐乎!
下边我写了一个简易的测试程序,再现了造成时钟同步请求可能会失败的关键所在。
下面是udp_bind.exe在XP系统下的运行结果截图
![](http://hi.csdn.net/attachment/201111/5/0_1320497126mPzL.gif)
下面是在Server 2003系统下的截图
![](http://hi.csdn.net/attachment/201111/5/0_1320497134IrnO.gif)
相同的代码在不同的系统下跑出不同的结果,令人有些费解!将上述测试代码中对应地方改为如下,就OK了。
看到这里,您也应该明白我们Demo程序中时钟同步的问题所在了吧?
俗话说,细节决定成败。
不论做产品,做项目,还是开发网络程序,都会面对很多细节问题,要重视你碰到的细节问题!
就在前几天,我碰到了这样一个细节问题。
协议栈Demo程序中有个设备校时的功能,采用NTP协议进行设备间的时钟同步。Demo程序在我和一位同事的PC (操作系统为MS XP)上测试时钟同步,怎么测,都没问题。但是拿到Windows 7系统下,却可能会发生问题,后来我在Windows Server 2003系统下,也碰到这个问题。
Demo A要向 Demo B发送登录请求,登出......并发送时钟同步请求(A向B登录请求时创建的套接字和A向B时钟同步请求创建的套接字不同),A 也要向Demo C登录;B向C登录。在Windows 7系统下,若打开A和B,A直接向B时钟同步请求,结果成功;若先打开A和C,A向C登录成功后,再打开B,A向B时钟同步请求,则失败。但该问题在开发环境PC XP系统下不会发生,后来在和另一位同事的共同努力下,最终找到了问题所在,并改掉了此Bug,不亦乐乎!
下边我写了一个简易的测试程序,再现了造成时钟同步请求可能会失败的关键所在。
#include <winsock2.h> #include <stdio.h> #pragma comment(lib, "WS2_32") // 链接到WS2_32.lib /* 1,测试设置 套接字sock1绑定地址信息:IP = 127.0.0.1,Port = 4567 套接字sock2绑定地址信息:IP = INADDR_ANY,Port = 4567 2,测试结果: 下列代码编译程序udp_bind.exe(Release)在XP系统下运行,只有sock1套接字发送数据成功,而sock2套接字在绑定时失败 将udp_bind.exe在MS Server 2003系统下运行,则sock2和sock2均发送成功 3,测试总结: 在使用UDP协议创建套接字,绑定本地地址时,本地地址IP应该使用同种模式,即INADDR_ANY,否则不同的套接字可能绑定到相同的端口 */ SOCKET CreateSock(char *psLocalIP, unsigned short usLocalPort) { //创建套节字 SOCKET sock = ::socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP); if( INVALID_SOCKET == sock ) { printf("Failed socket() %d \n", ::WSAGetLastError()); return 0; } //绑定本地地址 sockaddr_in addrLocal; memset(&addrLocal, 0, sizeof(addrLocal)); addrLocal.sin_family = AF_INET;
if ( '\0' == psLocalIP[0] ) { addrLocal.sin_addr.s_addr = htonl(INADDR_ANY); } else { addrLocal.sin_addr.s_addr = inet_addr(psLocalIP); } addrLocal.sin_port = htons(usLocalPort);
if ( 0 != bind(sock, (struct sockaddr*)&addrLocal, sizeof(addrLocal)) ) { printf("Failed bind() %d \n", ::WSAGetLastError()); closesocket(sock); return 0; } return sock; } int main(int argc, char* argv[]) { // 初始化WS2_32.dll WSADATA wsaData; WORD sockVersion = MAKEWORD(2, 2); if(::WSAStartup(sockVersion, &wsaData) != 0) { exit(0); } SOCKET sock1 = CreateSock("127.0.0.1", 4567); if ( 0 == sock1 ) { printf("Failed create sock1 \n"); } //int iOn = 1; int iRet = 0; //iRet = setsockopt(sock1,SOL_SOCKET,SO_REUSEADDR,(char*)&iOn,sizeof(iOn)); SOCKET sock2 = CreateSock("", 4567); if ( 0 == sock2 ) { printf("Failed create sock2 \n"); } //填写远程地址信息 sockaddr_in addrRemote; addrRemote.sin_family = AF_INET; addrRemote.sin_port = htons(5678); addrRemote.sin_addr.S_un.S_addr = inet_addr("127.0.0.1"); char szText[] = " Hello UDP! \r\n"; iRet = ::sendto(sock1, szText, strlen(szText), 0, (sockaddr*)&addrRemote, sizeof(addrRemote)); if ( SOCKET_ERROR == iRet ) { printf("Failed sendto() sock1 %d \n", ::WSAGetLastError()); closesocket(sock1); } else { printf("Successed sendto sock1 = %d, data lenth = %d \n", sock1, iRet); } iRet = ::sendto(sock2, szText, strlen(szText), 0, (sockaddr*)&addrRemote, sizeof(addrRemote)); if ( SOCKET_ERROR == iRet ) { printf("Failed sendto() sock2 %d \n", ::WSAGetLastError()); closesocket(sock2); } else { printf("Successed sendto sock2 = %d, data lenth = %d \n", sock2, iRet); } system("pause"); closesocket(sock1); closesocket(sock2); ::WSACleanup(); return 0; }
下面是udp_bind.exe在XP系统下的运行结果截图
![](http://hi.csdn.net/attachment/201111/5/0_1320497126mPzL.gif)
下面是在Server 2003系统下的截图
![](http://hi.csdn.net/attachment/201111/5/0_1320497134IrnO.gif)
相同的代码在不同的系统下跑出不同的结果,令人有些费解!将上述测试代码中对应地方改为如下,就OK了。
//if ( '\0' == psLocalIP[0] ) //{ addrLocal.sin_addr.s_addr = htonl(INADDR_ANY); //} //else //{ // addrLocal.sin_addr.s_addr = inet_addr(psLocalIP); //}
看到这里,您也应该明白我们Demo程序中时钟同步的问题所在了吧?
俗话说,细节决定成败。
不论做产品,做项目,还是开发网络程序,都会面对很多细节问题,要重视你碰到的细节问题!
相关文章推荐
- ThinkPHP中 I 函数的一个值得注意的使用细节
- [Socket网络编程]由于套接字没有连接并且(当使用一个 sendto 调用发送数据报套接字时)没有提供地址,发送或接收数据的请求没有被接受。
- 使用socket()函数创建套接字
- 使用socket()函数创建套接字
- 使用socketserver写一个套接字程序
- C++ Boost::bind函数包装器使用,boost::bind与伪函数的绑定使用
- 一个类成员函数绑定器的使用示例(二次绑定)
- 5、使用socket()函数创建套接字
- C/C++ socket编程教程之四:使用socket()函数创建套接字
- C++关于tr1中绑定成员函数的bind使用
- Linux C语言编程-Linux网络通信--Linux上使用套接字(socket)来处理信息---编写一个单进程非阻塞多客户的套接字客户端
- socket中bind函数绑定的IP是什么
- could not install *smartsocket* listaner cannot bind to 127.0.0.1:5037: 通常每个套接字地址(协议/网络地址/端口)只允许使用一次
- ZeroMQ接口函数之 :zmq_bind - 绑定一个socket
- Xcode中使用socket中bind()函数报错
- Mootools中使用bind给函数绑定对象
- asp.net下的“Eval()、XPath() 和 Bind() 这类数据绑定方法只能在数据绑定控件的上下文中使用。”错误的一个可能的成因
- Mootools中使用bind给函数绑定对象
- C/C++ socket编程教程之四:使用socket()函数创建套接字
- linux下使用socket绑定(bind)时:address already in use