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

posted on 2014-10-11 18:35  亦休  阅读(1649)  评论(2编辑  收藏  举报