winsock2学习篇(二) 利用winsock2的API做简单的数据交换
上一篇简单介绍了getaddrinfo()函数的用法,在本篇中还将继续使用这个函数,并补充几点:
getaddrinfo()函数的第一个参数pNodeName为空时,MSDN给出的解释(If the pNodeName parameter contains an empty string, all registered addresses on the local computer are returned.)的大概意思是返回在本地机器上注册的所有地址。在我的测试机上,这个返回值是回送地址127.0.0.1。
当第一个参数pNodeName为计算机全名(由gethostname()函数获取)时,返回本机IP(如果你的机器处于专用网内则返回专用地址如10.多少多少)。
数据在一个客户端程序和一个服务器程序之间传输,用到的都是WSAPI中几个基本的函数
创建套接字socket();
连接函数connect();
发送数据send();
接收数据recv();
关闭套接字closesocket();
将套接字绑定与通信地址绑定bind();
将套接字设置为监听状态listen();
等待连接accept();
服务器端程序初始化WSAPI、用getaddrinfo()函数解析将要绑定的地址信息、创建监听套接字、将监听套接字与绑定地址绑定、将监听套接字设置为监听状态、等待连接;
客户端程序初始化WSAPI、用getaddrinfo()函数解析将要连接的地址信息、连接服务器。
一旦服务器端程序与客户端程序之间的连接建立好,就可以使用recv()函数和send()函数进行数据传输。
服务器端程序源码:
1 #include <WinSock2.h> 2 #include <Ws2tcpip.h> 3 #include <stdio.h> 4 5 #pragma comment(lib, "Ws2_32.lib") 6 7 #define DEFAULT_PORT "27016" 8 #define DEFAULT_BUF_LEN 512 9 #define DETAULT_HOSTNAME_LEN 128 10 11 int __cdecl main(int argc, char **argv) 12 { 13 //声明变量 14 WSADATA wsaData; 15 struct addrinfo pHints; 16 struct addrinfo *pResult; 17 struct sockaddr* ClientAddress; 18 int iResult = 0, iSendResult = 0; 19 SOCKET MonitorSocket = INVALID_SOCKET; 20 SOCKET ConnectionSocket = INVALID_SOCKET; 21 char caRecvBuf[DEFAULT_BUF_LEN]; 22 char caHostName[DETAULT_HOSTNAME_LEN] = ""; 23 //初始化winsock2 24 WSAStartup(MAKEWORD(2,2), &wsaData); 25 //获取计算机全名 26 gethostname(caHostName, (int)sizeof(caHostName)); 27 printf("%s\n", caHostName); 28 //解析服务器端口信息,在client中指定了端口27015,所以 29 //返回的struct addrinfo链表中只有一个成员 30 ZeroMemory(&pHints, sizeof(pHints)); 31 pHints.ai_family = AF_INET; 32 pHints.ai_protocol = IPPROTO_TCP; 33 pHints.ai_socktype = SOCK_STREAM; 34 iResult = getaddrinfo(caHostName, DEFAULT_PORT, &pHints, &pResult); 35 if(iResult != 0) 36 { 37 printf("fail to call getaddrinfo function\n"); 38 WSACleanup(); 39 return 1; 40 } 41 //打印解析出来的地址信息 42 struct sockaddr_in* pIpv4 = (struct sockaddr_in*)pResult->ai_addr; 43 printf("address: %s\nport: %d\n", inet_ntoa(pIpv4->sin_addr), ntohs(pIpv4->sin_port)), 44 //创建监听套接字 45 MonitorSocket = socket(pResult->ai_family, pResult->ai_socktype, pResult->ai_protocol); 46 if(MonitorSocket == INVALID_SOCKET) 47 { 48 printf("fail to call socket function\n"); 49 freeaddrinfo(pResult); 50 WSACleanup(); 51 return 1; 52 } 53 //将监听套接字与本地端口绑定 54 iResult = bind(MonitorSocket, pResult->ai_addr, (int)pResult->ai_addrlen); 55 if(iResult != 0) 56 { 57 printf("fail to call bind function\n"); 58 closesocket(MonitorSocket); 59 freeaddrinfo(pResult); 60 WSACleanup(); 61 return 1; 62 } 63 //释放pResult 64 freeaddrinfo(pResult); 65 //将监听套接字设定为监听状态 66 iResult = listen(MonitorSocket, SOMAXCONN); 67 if(iResult != 0) 68 { 69 printf("fail to call listen function\n"); 70 closesocket(MonitorSocket); 71 WSACleanup(); 72 return 1; 73 } 74 //一旦有连接接入则创建一个连接套接字用来传输数据 75 ConnectionSocket = accept(MonitorSocket, NULL, NULL); 76 if(ConnectionSocket == INVALID_SOCKET) 77 { 78 printf("fail to call accept function\n"); 79 closesocket(ConnectionSocket); 80 WSACleanup(); 81 return 1; 82 } 83 closesocket(MonitorSocket); 84 //接收客户端发过来的数据并回射给客户端 85 do 86 { 87 iResult = recv(ConnectionSocket, caRecvBuf, DEFAULT_BUF_LEN, NULL); 88 if(iResult>0) 89 { 90 printf("Receive %d bytes of data...\n", iResult); 91 iSendResult = send(ConnectionSocket, caRecvBuf, iResult, NULL); 92 if(iSendResult == SOCKET_ERROR) 93 { 94 printf("fail to call send function\n"); 95 closesocket(ConnectionSocket); 96 closesocket(MonitorSocket); 97 WSACleanup(); 98 return 1; 99 } 100 printf("Send %d bytes of data...\n", iResult); 101 } 102 else if(iResult == 0) 103 { 104 printf("End sending data\n"); 105 } 106 else 107 { 108 printf("fail to call recv function\n"); 109 closesocket(ConnectionSocket); 110 closesocket(MonitorSocket); 111 WSACleanup(); 112 return 1; 113 } 114 }while(iResult>0); 115 //关闭连接 116 iResult = shutdown(ConnectionSocket, SD_BOTH); 117 if(iResult == SOCKET_ERROR) 118 { 119 printf("fail to call shutdown function\n"); 120 closesocket(ConnectionSocket); 121 closesocket(MonitorSocket); 122 WSACleanup(); 123 return 1; 124 } 125 // 126 closesocket(ConnectionSocket); 127 WSACleanup(); 128 return 0; 129 }
客户端程序源码:
1 #include <winSock2.h> 2 #include <WS2tcpip.h> 3 #include <stdio.h> 4 5 #pragma comment(lib, "Ws2_32.lib") 6 7 #define DEFAULT_PORT "27016" 8 #define DEFAULT_BUF_LEN 512 9 10 int __cdecl main(int argc, char **argv) 11 { 12 //声明变量 13 WSADATA wsaData; 14 int iResult = 0, iErrorCode = 0, i = 0; 15 struct addrinfo pHints; 16 struct addrinfo *pResult, *ptr; 17 SOCKET ConnectionSocket; 18 char *cpsendBuffer = "this_is_a_test"; 19 char carecvBuffer[DEFAULT_BUF_LEN]; 20 //验证参数是否合法 21 if(argc != 2) 22 { 23 printf("input program and address of server\n"); 24 return 1; 25 } 26 //初始化WSA 27 WSAStartup(MAKEWORD(2,2), &wsaData); 28 //获取服务器信息 29 ZeroMemory(&pHints, sizeof(pHints)); 30 pHints.ai_family = AF_UNSPEC; 31 pHints.ai_protocol = IPPROTO_TCP; 32 pHints.ai_socktype = SOCK_STREAM; 33 iResult = getaddrinfo(argv[1], DEFAULT_PORT, &pHints, &pResult); 34 //打印解析出来的地址信息 35 struct sockaddr_in* pIpv4 = (struct sockaddr_in*)pResult->ai_addr; 36 printf("address: %s\nport: %d\n", inet_ntoa(pIpv4->sin_addr), ntohs(pIpv4->sin_port)); 37 if(iResult != 0) 38 { 39 printf("fail to call getaddrinfo function\n"); 40 WSACleanup(); 41 return 1; 42 } 43 //创建连接套接字并尝试连接服务器 44 for(ptr = pResult;ptr != NULL;ptr = ptr->ai_next) 45 { 46 ConnectionSocket = socket(ptr->ai_family, ptr->ai_socktype, ptr->ai_protocol); 47 if(ConnectionSocket == INVALID_SOCKET) 48 { 49 printf("fail to call socket function\n"); 50 WSACleanup(); 51 return 1; 52 } 53 iResult = connect(ConnectionSocket, ptr->ai_addr, (int)ptr->ai_addrlen); 54 if(iResult == SOCKET_ERROR) 55 { 56 iErrorCode = WSAGetLastError(); 57 printf("ErrorCode: %d\n", iErrorCode); 58 closesocket(ConnectionSocket); 59 ConnectionSocket = INVALID_SOCKET; 60 continue; 61 } 62 break; 63 } 64 //释放pResult 65 freeaddrinfo(pResult); 66 if(ConnectionSocket == INVALID_SOCKET) 67 { 68 printf("fail to connect server\n"); 69 WSACleanup(); 70 return 1; 71 } 72 //发送缓冲区中的测试数据 73 iResult = send(ConnectionSocket, cpsendBuffer, (int)strlen(cpsendBuffer), 0); 74 if(iResult == SOCKET_ERROR) 75 { 76 printf("fail to send test data\n"); 77 closesocket(ConnectionSocket); 78 WSACleanup(); 79 return 1; 80 } 81 printf("Sending %d bytes of data...\n", iResult); 82 //关闭连接套接字的发送功能,仍可以接收数据 83 iResult = shutdown(ConnectionSocket, SD_SEND); 84 if(iResult == SOCKET_ERROR) 85 { 86 printf("fail to call shutdown function to stop sending data to server\n"); 87 closesocket(ConnectionSocket); 88 WSACleanup(); 89 return 1; 90 } 91 //接收服务器端发过来的数据 92 do 93 { 94 iResult = recv(ConnectionSocket, carecvBuffer, DEFAULT_BUF_LEN, 0); 95 if(iResult > 0) 96 { 97 printf("Receiving %d bytes of data...\n", iResult); 98 for(; i < iResult; i ++) 99 printf("%c", carecvBuffer[i]); 100 printf("\n"); 101 } 102 if(iResult == 0) 103 { 104 printf("End receiving data\n"); 105 } 106 if(iResult == SOCKET_ERROR) 107 { 108 printf("fail to receive data\n"); 109 } 110 }while(iResult>0); 111 closesocket(ConnectionSocket); 112 WSACleanup(); 113 return 0; 114 }
测试结果:
单机测试通信正常;专用网内双机通信正常。
出错时可以调用WSAGetLastError()函数返回错误码,根据错误码查询MSDN给出的解释,错误码查询链接http://msdn.microsoft.com/en-us/library/windows/desktop/ms740668(v=vs.85).aspx#WSAENOTSOCK。