:socket操作相关函数)
使用socket函数应先包含winsock2.h头文件和链接Ws2_32.lib文件#include winsock2.h#pragma comment(lib, Ws2_32.lib)1、WSAStartup()和WSACleanup()WSAStartup()用来加载套接字库函数执行成功返回0否则返回错误码。int WSAStartup( _In_ WORD wVersionRequested, _Out_ LPWSADATA lpWSAData );wVersionRequested要加载的Winsock库版本。其低位字节为主版本号高位字节为副版本号最新版本号为2.2XP下可用可以用MAKEWORD(x, y)宏来构成这个WORD(unsigned short)类型的值其中x为低位字节y为高位字节。lpWSAData返回值参数指向WSAData结构的指针。可以通过LOBYTE(WORD wValue)和HIBYTE(WORD wValue)分别获得WORD类型值的低位字节值和高位字节值。WSACleanup()用来结束对Winsock库的使用释放资源。函数原型int WSACleanup(void);egWORD wVersionRequested MAKEWORD(1, 1);//指定要加载的Winsock库主版本号和副版本号 WSADATA wsaData; int err; err WSAStartup(wVersionRequested, wsaData); if(err ! 0) return 1; if(LOBYTE(wsaData.wVersion)!1 || HIBYTE(wsaData.wVersion)!1) { WSACleanup(); return 1; } ...... WSACleanup(); return 0;2、socket()/closesocket()socket()用来创建套接字函数调用成功会返回创建的套接字失败返回INVALID_SOCKETSOCKET WSAAPI socket( _In_ int af, _In_ int type, _In_ int protocol );af地址族。对于TCP/IP协议套接字来说应为AF_INET(也可写成PF_INET)。typesocket类型。SOCK_STREAM表示面向流的套接字用于TCP协议SOCK_DGRAM表示面向数据报的套接字用于UDP协议SOCK_RAW表示原始套接字用于IP协议。protocol使用的协议设为0的话为让系统自动选择使用的协议如果是原始套接字的话应该手动设置协议。SOCKET ListenSocket INVALID_SOCKET; ListenSocket socket(AF_INET, SOCK_STREAM, 0); if (ListenSocket INVALID_SOCKET) { wprintf(Lsocket function failed with error: %u\n, WSAGetLastError()); WSACleanup(); return 1; }closesocket()用来关闭套接字从而关闭连接。closesocket()的一些行为受setsockopt()影响。3、bind()bind()用来将套接字绑定到本机的某个地址和端口上函数执行成功返回0否则返回SOCKET_ERROR。int bind( _In_ SOCKET s, _In_ const struct sockaddr *name, _In_ int namelen );s要绑定的套接字。namesockaddr地址结构指针其包含了要绑定过的本机地址信息在TCP/IP socket编程中可以用sockaddr_in地址结构来替代此结构以方便我们填写地址信息。struct sockaddr_in { short sin_family; //地址族对于TCP/IP来说应为AF_INET u_short sin_port; //端口号 struct in_addr sin_addr; //IP地址 char sin_zero[8]; //为与sockaddr结构长度相同增加的填充值无意义 };其中sin_addr成员实际上是一个联合故直接向成员s_addr赋值即可。struct in_addr {union {struct { u_char s_b1,s_b2,s_b3,s_b4; } S_un_b;struct { u_short s_w1,s_w2; } S_un_w;u_long S_addr;} S_un;#define s_addr S_un.S_addr.......}inet_pton()、inet_addr()可以将一个点分十进制格式的IPV4地址转换为一个32位网络字节序IP地址。inet_ntop()、inet_ntoa()可以将一个in_addr结构体类型转换为点分十进制格式IP地址。需要注意的是inet_pton()、inet_ntop()的最低支持版本为Vista。htons()可以将端口号转换为网络字节序。ntohs()可以将网络字节序的端口号转化为主机字节序。namelen为地址结构长度。egsockaddr_in service; service.sin_family AF_INET; service.sin_addr.s_addr inet_addr(127.0.0.1); service.sin_port htons(27015); //---------------------- // Bind the socket. iResult bind(ListenSocket, (SOCKADDR *) service, sizeof (service)); if (iResult SOCKET_ERROR) { wprintf(Lbind failed with error %u\n, WSAGetLastError()); closesocket(ListenSocket); WSACleanup(); return 1; }如果想要绑定本机所有IP地址比如回路地址127.0.0.1 内网地址或者本机有多块网卡可以直接将S_addr赋值为INADDR_ANY即可eg:service.sin_addr.S_un.S_addr INADDR_ANY;4、setsockopt()/getsockopt()setsockopt()/getsockopt()用来设置/获得socket套接字的选项值函数原型int setsockopt( _In_ SOCKET s, _In_ int level, _In_ int optname, _In_ const char *optval, _In_ int optlen );s要设置的套接口描述字。level选项所定义的层次协议SOL_SOCKET: socket层IPPROTO_TCP: TCP层IPPROTO_IP: IP层.......optname要设置的选项名。optval要设置的选项值。optlenoptval缓冲区的长度。有两种类型的套接口选项一种是布尔型选项允许或禁止一种特性允许一个布尔型选项则将optval指向非零整形数禁止一个选项将optval指向一个等于零的整形数。另一种是整形或结构选项optval指向包含所需选项的整形数或结构而optlen则为整形数或结构的长度。socket层选项SO_BROADCAST BOOL 发送广播信息。SO_DEBUG BOOL 启用调试输出。SO_DONTLINER BOOL 允许该项则closesocket()调用立即返回但是如果可能排队的数据将在套接口关闭前发送(相当于使用SO_LINGER的时候结构体成员l_onoff设为0l_linger不关心)。SO_DONTROUTE BOOL 禁止路由选径直接传送。SO_KEEPALIVE BOOL 发送“保持活动”包即传输层心跳包来保持连接默认不开启。开启后在连接空闲即应用层无数据传输两小时后才会发送心跳包发送心跳包间隔为1秒linux为75秒当发送失败超过10次后则认为连接断开内核会关闭连接并向应用返回错误如TIMEOUT。应当在连接之前或者连接完成之后设置SO_KEEPALIVE套接字选项如果 connect 函数是异步调用的则需要等待连接完成然后再尝试设置 SO_KEEPALIVE 套接字选项。默认两小时无传输才发送心跳的时间过长比如路由器、防火墙可能会因为连接空闲超时而关闭连接可以使用SIO_KEEPALIVE_VALS选项来调整心跳包发送的相关时间具体可参考SO_KEEPALIVE 套接字选项 (Ws2def.h) - Win32 apps | Microsoft Learn 中SIO_KEEPALIVE_VALS选项部分内容。SO_LINGER struct linger 如关闭socket时有未发送数据则等待或不等待。SO_OOBINLINE BOOL 在常规数据流中接收带外数据。SO_RCVBUF int socket接收缓冲区大小。SO_REUSEADDR BOOL 允许套接口和一个已在使用中的地址捆绑即地址重用推荐服务器的监听socket打开它。SO_RCVTIMEO int 接收超时时间。SO_SNDBUF int socket发送缓冲区大小SO_SNDTIMEO int 发送超时时间。SO_MAX_MSG_SIZEunsigned int 发送的数据包的最大长度仅对应面向数据报UDP的情况下。SO_CONNECT_TIMEDWORD 连接已经建立了多长时间检查是否接受了连接SO_ERROR int 获得错误码........其中SOCK_STREAM类型的套接口不支持SO_BROADCAST选项SOCK_DGRAM类型的套接口不支持SO_DONTLINGER 、SO_KEEPALIVE、SO_LINGER和SO_OOBINLINE选项。setsockopt()使用举例以下转自http://bbs.csdn.net/topics/604301312.可以设置接收、发送的超时时间仅支持阻塞socketint nNetTimeout1000;//1秒//发送时限setsockopt(socketSOL_S0CKET,SO_SNDTIMEO(char *)nNetTimeout,sizeof(int));//接收时限setsockopt(socketSOL_S0CKET,SO_RCVTIMEO(char *)nNetTimeout,sizeof(int));如果不设置发送超时的话写入socket的时候写入缓冲区已满那么写入操作就会阻塞等待。接收、发送超时会返回WSAETIMEDOUTlinux返回EAGAIN/EWOULDBLOCK。需要注意的地方SO_SNDTIMEO在 Windows 上会影响connect()超时。3.在send()的时候返回的是实际发送出去的字节(同步)或发送到socket缓冲区的字节(异步);系统默认的状态发送和接收缓冲区为8K在实际的过程中发送数据和接收数据量比较大可以设置socket缓冲区// 接收缓冲区int nRecvBuf32*1024;//设置为32Ksetsockopt(s,SOL_SOCKET,SO_RCVBUF,(const char*)nRecvBuf,sizeof(int));//发送缓冲区int nSendBuf32*1024;//设置为32Ksetsockopt(s,SOL_SOCKET,SO_SNDBUF,(const char*)nSendBuf,sizeof(int));4. 如果在发送数据的时希望不经历由系统缓冲区到socket缓冲区的拷贝而影响程序的性能int nZero0;setsockopt(socketSOL_S0CKET,SO_SNDBUF(char *)nZero,sizeof(nZero));5.同上在recv()完成上述功能(默认情况是将socket缓冲区的内容拷贝到系统缓冲区)int nZero0;setsockopt(socketSOL_S0CKET,SO_RCVBUF(char *)nZero,sizeof(int));6.一般在发送UDP数据报的时候希望该socket发送的数据具有广播特性BOOL bBroadcastTRUE;setsockopt(s,SOL_SOCKET,SO_BROADCAST,(const char*)bBroadcast,sizeof(BOOL));7.在client连接服务器过程中如果处于非阻塞模式下的socket在connect()的过程中可以设置connect()延时,直到accpet()被呼叫(本函数设置只有在非阻塞的过程中有显著的作用在阻塞的函数调用中作用不大)BOOL bConditionalAcceptTRUE;setsockopt(s,SOL_SOCKET,SO_CONDITIONAL_ACCEPT,(const char*)bConditionalAccept,sizeof(BOOL));8.SO_LINGER选项可以设置closesocket()时的动作struct linger {u_short l_onoff;u_short l_linger;};eg:linger m_sLinger;m_sLinger.l_onoff1;m_sLinger.l_linger5;setsockopt(s,SOL_SOCKET,SO_LINGER,(const char*)m_sLinger,sizeof(linger));SO_LINGER选项l_onoffl_lingerType of closeWait for close?zeroDo not care优雅关闭closesocket()调用立即返回但如果可能,排队的数据将在关闭前发送Nononzerozero强制关闭closesocket()调用立即返回不论是否有排队数据未发送或未被确认Nononzerononzeroclosesocket()调用直到所剩数据发送完毕或超时才返回。对于非阻塞socketclosesocket()将立即返回而不论数据是否发送完毕或时间是否到达。优雅关闭如果排队数据在l_linger指定的时间内被全部发送强制关闭如果排队数据在l_linger指定的时间内没有被全部发送,closesocket()返回WSAEWOULDBLOCK.8.SO_REUSEADDR也有称端口复用的当socket关闭的时候会产生4次挥手比如正在等待对方的关闭回应这就是TCP的TIME_WAIT状态如果在这个状态下我们再次启动程序使用相同的地址和端口的话会提示端口被占用。我们可以开启这个选项避免应用关掉立马重启后产生的端口被占用提示。当然许多服务程序会使用固定的端口而客户端的话因为一般采用随机端口因此会出现两个客户端程序绑定到同样端口的可能性不大。总结就是为了确保一个进程被关闭后及时它还没有释放该端口同一个主机上的其他进程还可以立刻重用该端口可以开启该选项。5、listen()listen()设置socket为监听模式并为socket建立一个未决连接的队列。listen()会将到来的但还未来得及accept()的连接请求放到此队列中一旦连接被accept()那么就从这个队列中删除这个未决连接。函数执行成功返回0否则返回SOCKET_ERROR。int listen( _In_ SOCKET s, _In_ int backlog );s套接字。backlog未决连接队列的最大长度如果当前连接数超过这个最大长度则新的连接请求会被拒绝客户调用WSAGetLastError()会得到一个WSAECONNREFUSED错误所以客户此时应该稍后重试。比如backlog设为100当前有80个连接到来在accept这些连接之前又有50个连接到来那么只有20个连接会被接收以待accept剩下的30个连接会返回WSAECONNREFUSED错误。backlog的最大值是由下层协议提供程序决定的还不存在合理最大值的标准SOMAXCONN为下层服务自动设置此值为合理的一个最大值通常为几百或更大。linux下默认支持的最大长度为128cat /proc/sys/net/core/somaxconn如果想要支持更大的长度可以这样设置echo 2048 /proc/sys/net/core/somaxconn不过系统重启后somaxconn还是会变回原来的128另一个方法就是在/etc/sysctl.conf中添加如下: net.core.somaxconn 2048然后在终端中执行sysctl -p这样就永久保存了somaxconn。egif (listen(ListenSocket, SOMAXCONN) SOCKET_ERROR) wprintf(Llisten function failed with error: %d\n, WSAGetLastError());6、accept()accept()接收客户端的连接请求函数执行成功返回一个新的已连接的socket使用这个socket同客户端进行通信而原来的监听socket可以接收其它客户的连接请求。运行失败返回INVALID_SOCKET。SOCKET accept( _In_ SOCKET s, _Out_ struct sockaddr *addr, _Inout_ int *addrlen );s已设置为监听状态的套接字。addr返回值参数包含客户端的IP地址和端口。addrlen返回值参数地址信息的长度。egSOCKET AcceptSocket; sockaddr_in addrClient; int len sizeof(SOCKADDR); AcceptSocket accept(ListenSocket, (SOCKADDR*)addrClient, len); if (AcceptSocket INVALID_SOCKET) { wprintf(Laccept failed with error: %ld\n, WSAGetLastError()); closesocket(ListenSocket); WSACleanup(); return 1; }else{ char sendBuf[100]; sprintf(sendBuf, Welcome %s:%d!, inet_ntoa(addrClient.sin_addr), ntohs(addrClient.sin_port)); }7、send()和recv()send和recv一般用于TCP连接。send()向一个已经连接的套接字发送数据如果没有错误发生则返回已经发送的数据的大小其值可能小于len否则返回SOCKET_ERROR。实际上send()返回结果只是表示是否发送到了socket发送缓冲区中并不代表已经成功发送到网络中因为数据传输由操作系统控制必须遵循拥塞控制等流量控制协议。如果调用setsockopt()将套接字发送缓冲区大小设为0的话send()返回值表示发送到网卡的数据大小当socket发送缓冲区已满的话send在阻塞模式下会阻塞在非阻塞模式返回WSAEWOULDBLOCK错误。int send( _In_ SOCKET s, _In_ const char *buf, _In_ int len, _In_ int flags );s一个已经连接的套接字。buf要发送的数据缓冲区。lenbuf的大小。flags0或以下值及其组合MSG_DONTROUTE 指明数据不选径WINDOWS套接字供应商可以忽略此标志。MSG_OOB 发送带外数据仅适用于SOCK_STREAM。egint send_bytes; char buf[] welcome to TCP test server(version 0.1).; int send_total 0; while(send_total strlen(buf)) { send_bytes send(conect_socket, buf, strlen(buf)-send_total, 0); if(send_bytes SOCKET_ERROR) { wprintf(Lshutdown failed with error: %d\n, WSAGetLastError()); closesocket(ConnectSocket); break; } else { send_total send_bytes; } }recv()从一个已经连接的套接字接收数据。如果没有错误发生函数返回接收到数据的大小如果对方已经将连接优雅的关闭则返回0超时或者发生错误返回SOCKET_ERROR。int recv( _In_ SOCKET s, _Out_ char *buf, _In_ int len, _In_ int flags );s一个已经连接的套接字。buf接收数据的缓冲区。lenbuf的大小flags可以为0或以下值MSG_PEEK读取当前系统缓冲区中数据但系统缓冲区中数据不会被删除读取的数据大小可能与缓冲区中数据大小不同。MSG_OOB处理带外数据。MSG_WAITALL直到buf已满或者连接关闭或者出现错误函数才返回。如果底层传输不支持该选项或socket已经被设置成非阻塞的则函数会失败错误为WSAEOPNOTSUPP。如果同时指定了MSG_OOB或MSG_PEEK函数也会出错。当socket接收缓冲区为空的话recv在阻塞模式下会阻塞在非阻塞模式返回WSAEWOULDBLOCK错误。eg#define DEFAULT_BUFLEN 512 char recvbuf[DEFAULT_BUFLEN] ; int iResult; //接收数据直到对方关闭连接 do { iResult recv(ConnectSocket, recvbuf, recvbuflen, 0); if ( iResult 0 ) wprintf(LBytes received: %d\n, iResult); else if ( iResult 0 ) wprintf(LConnection closed\n); else wprintf(Lrecv failed with error: %d\n, WSAGetLastError()); } while( iResult 0 );8、connect()connect()将套接字与远端地址进行连接。如果没有错误发生函数返回0否则返回SOCKET_ERROR。int connect( _In_ SOCKET s, _In_ const struct sockaddr *name, _In_ int namelen );s本地套接字是即将在其上建立连接的套接字。name对方地址。namelen地址信息长度。egint iResult; sockaddr_in clientService; clientService.sin_family AF_INET; clientService.sin_addr.s_addr inet_addr(127.0.0.1); clientService.sin_port htons(27015); iResult connect(ConnectSocket, (SOCKADDR *) clientService, sizeof (clientService)); if (iResult SOCKET_ERROR) { wprintf(Lconnect function failed with error: %ld\n, WSAGetLastError()); iResult closesocket(ConnectSocket); if (iResult SOCKET_ERROR) wprintf(Lclosesocket function failed with error: %ld\n, WSAGetLastError()); }9、sendto()和recvfrom()sendto和recvfrom一般用于UDP连接但也能用于TCP连接其比send和recv多了对方地址信息和长度剩余参数和返回值意义同send和recv。需要注意的地方是对于面向数据报的socket比如面向数据报的UDP传输在发送数据的时候必需注意发送的数据包大小不应超过底层数据包的最大长度底层数据包的最大长度可以通过getsockopt()函数获得参数optname传入SO_MAX_MSG_SIZE。int sendto( _In_ SOCKET s, _In_ const char *buf, _In_ int len, _In_ int flags, _In_ const struct sockaddr *to,//对方地址信息 _In_ int tolen//对方地址信息长度 );eg//UDP下发送数据 int iResult; sockaddr_in RecvAddr; RecvAddr.sin_family AF_INET; RecvAddr.sin_port htons(Port); RecvAddr.sin_addr.s_addr inet_addr(192.168.1.1); wprintf(LSending a datagram to the receiver...\n); iResult sendto(SendSocket, SendBuf, BufLen, 0, (SOCKADDR *) RecvAddr, sizeof (RecvAddr)); if (iResult SOCKET_ERROR) { wprintf(Lsendto failed with error: %d\n, WSAGetLastError()); closesocket(SendSocket); }int recvfrom( _In_ SOCKET s, _Out_ char *buf, _In_ int len, _In_ int flags, _Out_ struct sockaddr *from,//对方地址信息 _Inout_opt_ int *fromlen//对方地址信息长度 );eg//UDP下接受数据 int iResult 0; sockaddr_in SenderAddr; int SenderAddrSize sizeof (SenderAddr); wprintf(LReceiving datagrams...\n); iResult recvfrom(RecvSocket, RecvBuf, BufLen, 0, (SOCKADDR *) SenderAddr, SenderAddrSize); if (iResult SOCKET_ERROR) { wprintf(Lrecvfrom failed with error %d\n, WSAGetLastError()); }10、WSASend()/WSARecv()、WSASendto()/WSARecvfrom()WSASend和WSARecvWSASendto和WSARecvfrom用于异步IO。typedef struct _WSABUF { CHAR *buf; /* the pointer to the buffer */ ULONG len; /* the length of the buffer */ } WSABUF, * LPWSABUF; int WSASend( _In_ SOCKET s, //套接字句柄 _In_ LPWSABUF lpBuffers, //WSABUF结构的数组WSABUF结构包含一个缓冲区指针和对应缓冲区的长度 _In_ DWORD dwBufferCount, //上面WSABUF数组的大小 _Out_ LPDWORD lpNumberOfBytesSent,//如果I/O操作立即完成的话此参数取得实际传输数据的字节数 _In_ DWORD dwFlags, //标志与send()中flags意义相同一般设为0 _In_ LPWSAOVERLAPPED lpOverlapped, //与此I/O操作相关的WSAOVERLAPPED结构 _In_ LPWSAOVERLAPPED_COMPLETION_ROUTINE lpCompletionRoutine //指定一个I/O操作完成后执行的例程一般设为NULL ); int WSARecv( _In_ SOCKET s, _Inout_ LPWSABUF lpBuffers, _In_ DWORD dwBufferCount, _Out_ LPDWORD lpNumberOfBytesRecvd, _Inout_ LPDWORD lpFlags, _In_ LPWSAOVERLAPPED lpOverlapped, _In_ LPWSAOVERLAPPED_COMPLETION_ROUTINE lpCompletionRoutine );lpBuffers这个参数是一个指针它指向一个WSABUF结构的数组。这个数组可以是瞬态的transient就是说你可以定义一个局部变量的WSABUF传入当异步I/O返回后系统会保存这个数组的副本。异步I/O操作都接收一个WSAOVERLAPPED结构的数据参数这些I/O操作调用之后会立即返回它们依靠传递的WSAOVERLAPPED结构来管理I/O请求的完成。调用WSASend可以完成一次发送多个缓冲区中的数据来进行集中写入提高了性能应该相当于unix上的writev好处看来是避免Nagle算法。11、WSASocket()WSASocket()主要用于完成端口中使对于socket的发送操作和接收操作都可以被重叠使用即投递多个重叠操作。SOCKET WSASocket( _In_ int af, //地址协议族一般设为AF_INET或AF_INET6 _In_ int type, //套接字类型一般设为SOCK_DREAM或SOCK_DGRAM _In_ int protocol, //使用协议一般设为0让系统自动设置协议 _In_ LPWSAPROTOCOL_INFO lpProtocolInfo, //套接口的特性一般我们设为NULL _In_ GROUP g, //套接字组的描述字0为不执行组操作 _In_ DWORD dwFlags //套接字属性在完成端口中我们一般将其设为WSA_FLAG_OVERLAPPED以支持重叠I/O操作 );12、shutdown()closesocket()会把描述符的引用计数减1只有在该计数为0时才会关闭套接字在多进程并发服务器中比如父子进程共享着套接字当只是父进程或某一子进程close掉套接字时套接字引用计数减1但不为0所以连接不会被关闭。调用close()时默认情况下TCP将尝试发送已排队等待发送到对端的任何数据发送完毕后再关闭连接可以调用setsockopt()来设置closesocket()时对于排队数据的处理方式。shutdown()不会去关心引用计数而直接关闭套接字。shutdown()还可以仅关闭读方向或写方向。一般情况下我们发送完数据再调用closesocket()关闭socket后数据最多是能保证已经写入到TCP缓冲区中怎么能确定对端已经读取数据后才返回呢一个办法是发送完数据后使用shutdown来先关闭socket的写端然后再阻塞的调用一个recv()此时对端会在收到数据后收到一个FIN当对端关闭socket后recv()会返回0。13、ioctlsocket()ioctlsocket()用来设置套接字的IO模式常用来设置套接字为非阻塞模式。int ioctlsocket( _In_ SOCKET s, _In_ long cmd, _Inout_ u_long *argp );s一个标识套接口的描述字。cmd对套接口s的操作命令。FIONBIO允许或禁止套接字非阻塞。默认情况下套接字为阻塞模式若无数据send/recv会发生阻塞。而WSAAsyncSelect和WSAEventSelect会自动将套接字设为非阻塞模式。如果已对一个套接口进行了WSAAsynSelect() 操作则任何用ioctlsocket()来把套接口重新设置成阻塞模式的试图将以WSAEINVAL失败为了把套接口重新设置成阻塞模式必须首先用WSAAsynSelect()调用IEvent参数置为0来禁止WSAAsynSelect()。eg将套接字设为非阻塞模式 u_long Len1; ioctlsocket(SocketServer,FIONBIO,Len);将套接字设为阻塞模式 u_long Len0; ioctlsocket(SocketServer,FIONBIO,Len);FIONREAD使用该参数的意义是获得系统读缓冲区中数据大小。SIOCATMARK确实是否所有的带外数据都已被读入。这个命令仅适用于SOCK_STREAM类型的套接口且该套接口已被设置为可以在线接收带外数据SO_OOBINLINE。argp指向cmd命令所带参数的指针。Linux下设置sockt为非阻塞使用fcntl()函数如int flags fcntl(listensock, F_GETFL); if(flags 0) { printf(fcntl(F_GETFL) error: %s\n, strerror(errno)); goto end; } int iRet fcntl(listensock, F_SETFL, flags | O_NONBLOCK); if(iRet 0) { printf(fcntl(F_SETFL) error: %s\n, strerror(errno)); goto end; }14、TCP关闭连接相关以下转载自小林coding “拔掉网线后 原本的 TCP 连接还存在吗”参考《孙鑫VC深入详解》Windows网络与通信程序设计王艳平http://baike.baidu.com/view/569202.htm?fraladdin