学习内容,参见《Windows网络编程》 第7章 Winsock基础
服务器/客户端连接模型见下图。
服务端流程
客户端流程
使用windows的Winsock 2编程,创建win32控制台工程,进行工程配置。
服务端代码,TCPServer.cpp。
// TCPServer.cpp : Defines the entry point for the console application. // #include "stdafx.h" #include <WinSock2.h> #include <stdio.h> #include <stdlib.h> #define DEFAULT_PORT 5150 #define DEFAULT_BUFFER 4096 int iPort = DEFAULT_PORT;//Port to listen for clients on BOOL bInterface = FALSE;//Listen on the specified interface BOOL bRecvOnly = FALSE;//Receive data only; don't echo back TCHAR szAddress[128];//Interface to listen for clients on //Print usage information and exit void usage() { printf("usage:server[-p:x][-i:IP][-o]\n\n"); printf(" -p:x Port number to listen on\n"); printf(" -i:str Interface to listen on\n"); printf(" -o Don't echo the data back\n\n"); ExitProcess(1); } //Parse the command line arguments, and set some global flags //to indicate what actions to perform void ValidateArgs(int agrc, _TCHAR** argv) { for (int i = 1; i < agrc; i++) { if ((argv[i][0] == _T('-')) || (argv[i][0] == _T('/'))) { switch (tolower(argv[i][1])) { case _T('p'): iPort = _ttoi(&argv[i][3]); break; case _T('i'): bInterface = TRUE; if (_tcslen(argv[i]) > 3) { _tcscpy_s(szAddress, &argv[i][3]); } break; case _T('o'): //只接受数据 bRecvOnly = TRUE; break; default: usage(); break; } } } } DWORD WINAPI ClientThread(LPVOID lpParam) { SOCKET sock = (SOCKET)lpParam; char szBuff[DEFAULT_BUFFER]; while (true) { //Perform a blocking recv() call int ret = recv(sock, szBuff, DEFAULT_BUFFER, 0); if (0 == ret) { break; } else if (SOCKET_ERROR == ret) { printf("recv() failed:%d\n", WSAGetLastError()); break; } szBuff[ret] = '\0'; printf("RECV:'%s'\n", szBuff); //if we selected to echo the data back, do it if (!bRecvOnly) { int nLeft = ret; int idx = 0; //make sure we write all the data while (nLeft > 0) { ret = send(sock, &szBuff[idx], nLeft, 0); if (0 == ret) { break; } else if (SOCKET_ERROR == ret) { printf("send() failed:%d\n", WSAGetLastError()); break; } nLeft -= ret; idx += ret; } } } return 0; } int _tmain(int argc, _TCHAR* argv[]) { ValidateArgs(argc, argv); WSADATA wsd; if (WSAStartup(MAKEWORD(2,2), &wsd) != 0) { printf("Failed to load Winsock!\n"); return 1; } //Create our listening socket SOCKET sListen = socket(AF_INET, SOCK_STREAM, IPPROTO_IP); if (sListen == SOCKET_ERROR) { printf("socket() failed:%d\n", WSAGetLastError()); return 1; } //Select the local interface, and bind to it struct sockaddr_in local; if (bInterface) { local.sin_addr.s_addr = inet_addr(szAddress); if (local.sin_addr.s_addr == INADDR_NONE) { usage(); } } else { //INADDR_ANY是所有网卡地址 local.sin_addr.s_addr = htonl(INADDR_ANY); } local.sin_family = AF_INET; local.sin_port = htons(iPort); //绑定监听网卡地址和端口 if (bind(sListen, (struct sockaddr*)&local, sizeof(local)) == SOCKET_ERROR) { printf("bind() failed:%d\n", WSAGetLastError()); } //开始监听客户端 listen(sListen, 8); printf("begin accept on port:%d\n", iPort); //In a continuous loop, wait for incoming clients.Once one //is detected, create a thread and pass the handle off to it while (true) { struct sockaddr_in client; int iAddrSize = sizeof(client); SOCKET sClient = accept(sListen, (struct sockaddr*)&client, &iAddrSize); if (sClient == INVALID_SOCKET) { printf("accept() failed:%d\n", WSAGetLastError()); break; } printf("Accepted client:%s:%d\n", inet_ntoa(client.sin_addr), ntohs(client.sin_port)); DWORD dwThreadId; HANDLE hThread = CreateThread(NULL, 0, ClientThread, (LPVOID)sClient, 0, &dwThreadId); if (hThread == NULL) { printf("CreateThread() failed:%d\n", GetLastError()); break; } CloseHandle(hThread); } closesocket(sListen); WSACleanup(); return 0; }
客户端代码,TCPClient.cpp。
// TCPClient.cpp : Defines the entry point for the console application. // #include "stdafx.h" #include <WinSock2.h> #include <stdio.h> #include <stdlib.h> #define DEFAULT_COUNT 5 #define DEFAULT_PORT 5150 #define DEFAULT_BUFFER 2048 #define DEFAULT_MESSAGE _T("This is a test of the emergency broadcasting system") TCHAR szServer[128]; TCHAR szMessage[1024]; int iPort = DEFAULT_PORT; DWORD dwCount = DEFAULT_COUNT; BOOL bSendOnly = FALSE; //Print usage information and exit void usage() { printf("usage:client[-p:x][-s:IP][-n:x][-o]\n\n"); printf(" -p:x Remote port to send to \n"); printf(" -s:IP Server's IP address or host name\n"); printf(" -n:x Number of times to send message\n"); printf(" -o Send messages only; don't receive\n"); ExitProcess(1); } //Parse the command line arguments, and set some global flags //to indicate what actions to perform void ValidateArgs(int argc, _TCHAR** argv) { for (int i = 1; i < argc; i++) { if ((argv[i][0] == _T('-')) || (argv[i][0] == _T('/'))) { switch (tolower(argv[i][1])) { case _T('p'): if (_tcslen(argv[i]) > 3) { iPort = _ttoi(&argv[i][3]); } break; case _T('s'): if (_tcslen(argv[i]) > 3) { _tcscpy_s(szServer, &argv[i][3]); } break; case _T('n'): if (_tcslen(argv[i]) > 3) { dwCount = _ttol(&argv[i][3]); } break; case _T('o'): bSendOnly = TRUE; break; default: usage(); break; } } } } int _tmain(int argc, _TCHAR* argv[]) { ValidateArgs(argc, argv); WSADATA wsd; if (WSAStartup(MAKEWORD(2,2), &wsd) != 0) { printf("Failed to load Winsock library!\n"); return 1; } _tcscpy_s(szMessage, DEFAULT_MESSAGE); //Create the socket, and attempt to connect to the server SOCKET sClient = socket(AF_INET, SOCK_STREAM, IPPROTO_IP); if (sClient == INVALID_SOCKET) { printf("socket() failed:%d\n", WSAGetLastError()); return 1; } struct sockaddr_in server; server.sin_family = AF_INET; server.sin_port = htons(iPort); //If the supplied server address wasn't in the form //"aaa.bbb.ccc.ddd,"it's a host name, so try to resolve it int nServerLen = (int)_tcslen(szServer); if (0 == nServerLen) { struct hostent* host = gethostbyname(szServer); if (host == NULL) { printf("Unable to resolve server:%s\n", szServer); return 1; } CopyMemory(&server.sin_addr, host->h_addr_list[0], host->h_length); } else { server.sin_addr.s_addr = inet_addr(szServer); } if (connect(sClient, (struct sockaddr*)&server, sizeof(server)) == SOCKET_ERROR) { printf("connect() failed:%d\n", WSAGetLastError()); return 1; } //Send and receive data char szBuffer[DEFAULT_BUFFER]; int ret = 0; for (int i = 0; i < (int)dwCount; i++) { ret = send(sClient, szMessage, (int)_tcslen(szMessage), 0); if (ret == 0) { break; } else if (ret == SOCKET_ERROR) { printf("send() failed:%d\n", WSAGetLastError()); break; } printf("Send %d bytes\n", ret); if (!bSendOnly) { ret = recv(sClient, szBuffer, DEFAULT_BUFFER, 0); if (ret == 0) { break; } else if (ret == SOCKET_ERROR) { printf("recv() failed:%d\n", WSAGetLastError()); break; } szBuffer[ret] = _T('\0'); printf("RECV [%d bytes]:'%s'\n", ret, szBuffer); } } closesocket(sClient); WSACleanup(); return 0; }