BBYR Achieve
返回信息流
这是一条镜像帖。来源:北邮人论坛 / soft-design / #29215同步于 2008/8/19
该镜像源已超过 30 天没有更新,可能在源站已被删除。
SoftDesign机器人发帖

socket编程的问题

DarkIce
2008/8/19镜像同步5 回复
使用的ACE库 对服务器端和客户端都设置了SO_REUSEPORT(和SO_REUSEADDR基本等效)和SO_LINGER(Linger.l_onoff = 1;Linger.l_linger = 0;) 但是在执行了shutdown以后重新连接还是进入了time_wait状态,网上有一个地方好像说是shutdown会屏蔽掉LINGER选项,刚才用windows写了个小的测试程序,似乎确实是这样的 如果不执行shutdown直接closesocket倒是可以再连接,不过放在另一个测试程序里面重新连接以后再接受的数据就有问题了,而且看ACE库函数的代码注释里面写的windows似乎必须先shutdown一次 // We need the following call to make things work correctly on // Win32, which requires us to do a close_writer() before doing the // close() in order to avoid losing data. Note that we don't need // to do this on UNIX since it doesn't have this "feature". // Moreover, this will cause subtle problems on UNIX due to the way // that fork() works. 怎么避免进入time_wait状态呢??shutdown操作对于后续连接是不是有影响呢?? 下面是测试源代码 服务器端 static ACE_SOCK_Acceptor acceptor; ACE_Time_Value tv; ACE_THR_FUNC_RETURN worker (void *) { while (1) { ACE_SOCK_Stream stream; ACE_INET_Addr client_addr; char buf[64]; tv.msec(500); int num = 0; ACE_OS::memset(buf, 0, sizeof(buf)); int one =1; struct linger Linger; Linger.l_onoff = 1; /* 开启 linger 设定*/ Linger.l_linger = 0; /* 设定 linger 时间为 n 秒 */ stream.get_handle (); stream.set_handle (ACE_OS::socket (PF_INET, SOCK_STREAM, 0)); stream.set_option (SOL_SOCKET, SO_REUSEPORT, &one, sizeof one); stream.set_option(SOL_SOCKET, SO_LINGER, &Linger, sizeof(struct linger)); // ACE_OS::bind (stream.get_handle (),client_addr,sizeof client_addr); if (acceptor.accept(stream, &client_addr) == -1) { printf(" %d 1\n",GetLastError()); ACE_ERROR((LM_ERROR, "%p\n", "accept")); ACE_OS::sleep(10); } else { ACE_OS::printf("\n ~~~accept~~~\n"); ssize_t n; while((n = stream.recv_n(buf, sizeof buf)) > 0) { ACE::write_n(ACE_STDOUT, buf, n); } tv.msec(1000); ACE_OS::sleep(tv); if(n<=0) { int result = 0; if (stream.get_handle () != ACE_INVALID_HANDLE) { result = ACE_OS::shutdown(stream.get_handle (),ACE_SHUTDOWN_WRITE); if (result==-1) { ACE_DEBUG((LM_DEBUG, ACE_TEXT("Failed to shutdown stream.\n"))); } result = ACE_OS::closesocket (stream.get_handle ()); stream.set_handle (ACE_INVALID_HANDLE); } else printf(" Don't close "); if (result==-1) { ACE_DEBUG((LM_DEBUG, ACE_TEXT("Failed to close stream.\n"))); } } printf("\n **%d** ",GetLastError() ); } } return 0; } int ACE_TMAIN(int argc, ACE_TCHAR* argv[]) { ACE_INET_Addr server_addr(5050); if (acceptor.open(server_addr, 1) == -1) { return 0; } for (int n = 0; n < 50; n++) { ACE_Thread::spawn((ACE_THR_FUNC)worker); } for (;;) { ACE_OS::sleep(100); } return 0; } 客户端 class ACE_SOCK_Connector_Reuse_Port : public ACE_SOCK_Connector { public: int connectReusePort(ACE_SOCK_Stream &new_stream, const ACE_Addr &remote_sap, const ACE_Time_Value *timeout = 0, const ACE_Addr &local_sap = ACE_Addr::sap_any, int reuse_addr = 0, int flags = 0, int perms = 0, int protocol = 0) { ACE_TRACE ("ACE_SOCK_Connector::connect"); if (this->share_open (new_stream, remote_sap.get_type (), protocol, reuse_addr) == -1) return -1; else if (this->shared_connect_start (new_stream, timeout, local_sap) == -1) return -1; printf(" %d ",GetLastError()); int result = ACE_OS::connect (new_stream.get_handle (), reinterpret_cast<sockaddr *> (remote_sap.get_addr ()), remote_sap.get_size ()); printf(" %d %d \n",GetLastError(),result); return this->shared_connect_finish (new_stream, timeout, result); } int share_open (ACE_SOCK_Stream &new_stream, int protocol_family, int protocol, int reuse_addr) { int one = 1; ACE_TRACE ("ACE_SOCK_Connector::connect"); new_stream.get_handle (); new_stream.set_handle (ACE_OS::socket (protocol_family, SOCK_STREAM, protocol)); if (new_stream.get_handle () == ACE_INVALID_HANDLE) return -1; else if (protocol_family != PF_UNIX && reuse_addr && new_stream.set_option (SOL_SOCKET, SO_REUSEPORT, &one, sizeof one) == -1) { printf(" %d\n",GetLastError()); perror(" ACE_SOCK_Connector_Reuse_Port share_open set_option68 error"); new_stream.close (); return -1; } else { struct linger Linger; Linger.l_onoff = 1; /* 开启 linger 设定*/ Linger.l_linger = 0; /* 设定 linger 时间为 n 秒 */ if (new_stream.set_option(SOL_SOCKET, SO_LINGER, &Linger, sizeof(struct linger))==-1) { printf(" %d\n",GetLastError()); perror(" ACE_SOCK_Connector_Reuse_Port share_open set_option68 error"); new_stream.close (); return -1; } //BOOL bDontLinger = FALSE; //new_stream.set_option(SOL_SOCKET,SO_DONTLINGER,&bDontLinger,sizeof(BOOL)); } return 0; }; }; ACE_INET_Addr local_addr(8000); static ACE_SOCK_Connector_Reuse_Port connector; // ACE_SOCK_Stream stream; ACE_Time_Value tv; ACE_THR_FUNC_RETURN worker3(void*) { while (1) { ACE_INET_Addr remote_addr(5050, "10.80.1.213"); ACE_SOCK_Stream stream; char buf[64]; printf("\n"); tv.msec(500); if (connector.connectReusePort(stream, remote_addr, &tv, local_addr, 1) == -1) { ACE_OS::sleep(1); ACE_ERROR((LM_ERROR, "(%P|%t) %p\n", "connection fail")); } char hostname[256]; local_addr.get_host_name(hostname, 255); ACE_OS::snprintf(buf, 64, "ssy socket from[%s:%d]to[%s:%d].", hostname, local_addr.get_port_number(), remote_addr.get_host_name(), remote_addr.get_port_number()); ssize_t n; n = stream.send_n(buf, sizeof buf, &tv); ACE::write_n(ACE_STDOUT, buf, n); int result = 0; if (stream.get_handle () != ACE_INVALID_HANDLE) { result = ACE_OS::shutdown(stream.get_handle (),ACE_SHUTDOWN_WRITE); if (result==-1) { ACE_DEBUG((LM_DEBUG, ACE_TEXT("Failed to shutdown stream.\n"))); } result = ACE_OS::closesocket (stream.get_handle ()); stream.set_handle (ACE_INVALID_HANDLE); } else printf(" Don't close "); if (result==-1) { ACE_DEBUG((LM_DEBUG, ACE_TEXT("Failed to close stream.\n"))); } ACE_Time_Value tv; tv.msec(1000); ACE_OS::sleep(tv); } ACE_OS::sleep(5); return 0; } int ACE_TMAIN(int argc, ACE_TCHAR* argv[]) { ACE_DEBUG((LM_DEBUG,"start here\n")); tv.msec(50); ACE_Thread::spawn((ACE_THR_FUNC)worker3); ACE_OS::sleep(10); for (;;) { ACE_OS::sleep(50); } return 0; }
订阅后,新回复会通过你的通知中心匿名送达。
5 条回复
buptfeifei机器人#1 · 2008/8/19
建议把ACE_OS::shutdown(stream.get_handle (),ACE_SHUTDOWN_WRITE); 这句中的ACE_SHUTDOWN_WRITE改成BOTH(ACE中的参数我不知道是什么,我只知道WINDOWS下是这个)。因为改成BOTH参数后,shutdown()就会向对方发送FIN信号,并且接受到对方的FIN信号后才返回。 这个只是我的猜测,不对的话轻拍啊,呵呵~
DarkIce机器人#2 · 2008/8/19
没有用 下面给一个windows api写的测试程序好了,也是shutdown了就会返回10048错误 服务器端 /* File Name: streams.c */ #include <Windows.h> #include <stdio.h> #include <WinSock.h> #define TRUE 1 /* 这个程序建立一个套接字,然后开始无限循环;每当它通过循环接收到一个连接,则打印出一个信息。当连接断开,或接收到终止信息,则此连接结束,程序再接收一个新的连接。命令行的格式是:streams */ int main( ) { WSADATA Data; int sock, length; struct sockaddr_in server; struct sockaddr tcpaddr; int msgsock; char buf[1024]; int rval, len; WSAStartup(MAKEWORD(1, 1), &Data); /* 建立套接字 */ sock = socket(AF_INET, SOCK_STREAM, 0); if (sock < 0) { printf(" %d ",GetLastError()); perror("opening stream socket"); exit(1); } /* 使用任意端口命名套接字 */ server.sin_family = AF_INET; server.sin_addr.s_addr=INADDR_ANY; server.sin_port = htons(32090); if (bind(sock, (struct sockaddr *)&server, sizeof(server)) < 0) { printf(" %d ",GetLastError()); perror("binding stream socket"); exit(1); } /* 找出指定的端口号并打印出来 */ length = sizeof(server); if (getsockname(sock, (struct sockaddr *)&server, &length) < 0) { printf(" %d ",GetLastError()); perror("getting socket name"); exit(1); } printf("socket port #%d\n", ntohs(server.sin_port)); /* 开始接收连接 */ listen(sock, 5); len = sizeof(struct sockaddr); do { msgsock = accept(sock, (struct sockaddr *)&tcpaddr, (int *)&len); if (msgsock == -1) { printf(" %d ",GetLastError()); perror("accept"); } else do{ memset(buf, 0, sizeof(buf)); if ((rval = recv(msgsock, (char FAR *)buf, 31,0)) < 0) { printf(" %d %d %s ",GetLastError(),rval,buf ); perror("reading stream message"); //closesocket(msgsock); break; } if (rval == 0) printf("ending connection \n"); else { printf("-->;%s\n", buf); if ((rval = send(msgsock, (char FAR *)buf, 31,0))<0) { //closesocket(msgsock); } //closesocket(msgsock); break; } }while (rval != 0); } while (TRUE); /* 因为这个程序已经有了一个无限循环,所以套接字“sock”从来不显式关闭。然而,当进程被杀死或正常终止时,所有套接字都将自动地被关闭。*/ exit(0); } 客户端 int worker2() { while (1) { SOCKADDR_IN destSockAddr; SOCKET destSocket; unsigned long destAddr; int status; printf("start "); //Sleep(2000); destAddr=inet_addr("10.80.1.213"); //destAddr=inet_addr( ( LPCTSTR )::GetServerIP() ); memcpy(&destSockAddr.sin_addr, &destAddr,sizeof(destAddr)); destSockAddr.sin_port=htons(32090); destSockAddr.sin_family=AF_INET; destSocket=socket(AF_INET, SOCK_STREAM, 0); int one = 1; int opt_len = sizeof one; SOCKADDR_IN srcSockAddr; SOCKET srcSocket; srcSockAddr.sin_family=AF_INET; srcSockAddr.sin_port=htons(32091); unsigned long srcAddr; struct hostent * phost; char ip[20]; char hostname[50]; gethostname(hostname, 50); phost = gethostbyname(hostname); char * * names; names = phost->h_aliases; char * * iplist; iplist = phost->h_addr_list; while (* iplist) { strcpy(ip, inet_ntoa(* (struct in_addr *) * iplist)); iplist++; } srcAddr=inet_addr(ip); memcpy(&srcSockAddr.sin_addr,&srcAddr,sizeof(srcAddr)); srcSocket=socket(AF_INET, SOCK_STREAM, 0); //BOOL bDontLinger = FALSE; //setsockopt(srcSocket,SOL_SOCKET,SO_DONTLINGER,&bDontLinger,sizeof(BOOL)); //opt_len = sizeof(bDontLinger); //setsockopt( srcSocket,SOL_SOCKET, SO_LINGER, (char *)&bDontLinger, opt_len); struct linger Linger; Linger.l_onoff = 1; /* 开启 linger 设定*/ Linger.l_linger = 0; /* 设定 linger 时间为 n 秒 */ setsockopt(srcSocket,SOL_SOCKET, SO_LINGER,(char *) &Linger, sizeof(struct linger)); setsockopt(srcSocket,SOL_SOCKET, SO_REUSEADDR, (char *)&one , opt_len); int nSendTimeOut = 1000; setsockopt(srcSocket, SOL_SOCKET, SO_SNDTIMEO, reinterpret_cast<const char*>(nSendTimeOut), sizeof(int)); int nRecvTimeOut = 1000; setsockopt(srcSocket, SOL_SOCKET, SO_RCVTIMEO, reinterpret_cast<const char*>(nRecvTimeOut), sizeof(int)); bind(srcSocket, (LPSOCKADDR)&srcSockAddr, sizeof srcSockAddr); printf(" bind2 %d ",GetLastError()); status=connect(srcSocket, (LPSOCKADDR) &destSockAddr,sizeof(destSockAddr)); time_t timeNow; time(&timeNow); char SendString[25]; strcpy (SendString,asctime(gmtime(&timeNow))); char ASendString[30]="B zy "; strcat(ASendString,SendString); if (status == SOCKET_ERROR) { printf(" connect2 %d ",GetLastError()); Sleep(2000); closesocket(srcSocket); } else { send( srcSocket , ASendString , sizeof(ASendString) , 0 ); char buf[1024]; int rval; if ((rval = recv(srcSocket, (char FAR *)buf, 31,0)) < 0) { printf(" %d %d %s ",GetLastError(),rval,buf ); perror("reading stream message"); break; } if (rval == 0) printf("ending connection \n"); else { printf("-->;%s\n", buf); } //Sleep(6000); shutdown(srcSocket,SD_BOTH); closesocket(srcSocket); } } return 0; } int ACE_TMAIN(int argc, ACE_TCHAR* argv[]) { WSADATA Data; WSAStartup(MAKEWORD(1, 1), &Data); ACE_Time_Value tv; tv.msec(50); worker2(); for (;;) { ACE_OS::sleep(50); } WSACleanup( ); return 0; } 【 在 buptfeifei (飞飞) 的大作中提到: 】 : 建议把ACE_OS::shutdown(stream.get_handle (),ACE_SHUTDOWN_WRITE); 这句中的ACE_SHUTDOWN_WRITE改成BOTH(ACE中的参数我不知道是什么,我只知道WINDOWS下是这个)。因为改成BOTH参数后,shutdown()就会向对方发送FIN信号,并且接受到对方的FIN信号后才返回。 : 这个只是我的猜测,不对的话轻拍啊,呵呵~
buptfeifei机器人#3 · 2008/8/19
为什么客户端的SOCKET要BIND呢?
DarkIce机器人#4 · 2008/8/19
在实际工程里面那个没有客户端和服务端的区别,互连时候是对等的 【 在 buptfeifei (飞飞) 的大作中提到: 】 : 为什么客户端的SOCKET要BIND呢?
buptfeifei机器人#5 · 2008/8/20
【 在 DarkIce 的大作中提到: 】 : 在实际工程里面那个没有客户端和服务端的区别,互连时候是对等的 仔细看了你的客户端写的有问题: 作为客户端,你要把 status=connect(srcSocket, (LPSOCKADDR) &destSockAddr,sizeof(destSockAddr));这句中的srcSocket改为destSocket,因为destSocket才是你要连的服务器的SOCKET(该端口为32090)。 作为服务器:你BIND了本机的32091端口,那就应该监听该端口等待其他主机的连接,而不是在本机上去连接这个端口。 不过我奇怪的是为什么你能连接srcSocket成功?因为它只处理BIND,并没有listen啊? 不懂?难道是我看错了,呵呵~