今天自己編寫了一個簡單的c++服務器客戶端程序,注釋較詳細,在此做個筆記。
windows下socket編程的主要流程可概括如下:初始化ws2_32.dll動態庫-->創建套接字-->綁定地址信息-->服務器進行監聽/客戶端連接服務器-->數據交換-->關閉套接字對象。
服務器端:
1 #include <Winsock2.h> 2 #include <Ws2tcpip.h> 3 #include <iostream> 4 5 #pragma comment(lib, "ws2_32.lib") //socket編程需要引用該庫 6 7 using std::cerr; 8 using std::cout; 9 using std::endl; 10 11 const char DEFAULT_PORT[] = "4000"; 12 const int RECV_BUF_SIZE = 256; 13 const size_t IP_BUF_SIZE = 65; 14 15 //服務器 16 int main() { 17 WSADATA wsa_data; //WSADATA變量,包含windows socket執行的信息 18 int i_result = 0; //接收返回值 19 SOCKET sock_server = INVALID_SOCKET; //創建服務器套接字 20 SOCKET sock_client = INVALID_SOCKET; //創建客戶端套接字 21 //addrinfo是getaddrinfo()函數用來保存主機地址信息的結構體 22 addrinfo *result = nullptr; //result是存儲地址信息的鏈表 23 addrinfo hints; 24 //初始化winsock動態庫(ws2_32.dll),MAKEWORD(2, 2)用於請求使用winsock2.2版本 25 i_result = WSAStartup(MAKEWORD(2, 2), &wsa_data); 26 if (i_result != 0) { 27 cerr << "WSAStartup() function failed: " << i_result << "\n"; 28 system("pause"); 29 return 1; 30 } 31 //用0填充內存區域,是ZeroMemory的更安全版本 32 SecureZeroMemory(&hints, sizeof(addrinfo)); 33 hints.ai_family = AF_INET; 34 hints.ai_socktype = SOCK_STREAM; //流式套接字用於TCP協議 35 hints.ai_protocol = IPPROTO_TCP; 36 hints.ai_flags = AI_PASSIVE; //socket的地址會被用於bind()函數的調用 37 //確定服務器的地址與端口,將相關信息寫入result中 38 i_result = getaddrinfo(nullptr, DEFAULT_PORT, &hints, &result); 39 if (i_result != 0) { 40 cerr << "getaddrinfo() function failed with error: " << WSAGetLastError() << "\n"; 41 WSACleanup(); 42 system("pause"); 43 return 1; 44 } 45 //創建服務器套接字 46 sock_server = socket(result->ai_family, result->ai_socktype, result->ai_protocol); 47 //套接字創建失敗 48 if (sock_server == INVALID_SOCKET) { 49 cerr << "socket() function failed with error: " << WSAGetLastError() << "\n"; 50 //將getaddrinfo()函數動態分配的addrinfo中的地址信息釋放掉 51 freeaddrinfo(result); 52 //釋放套接字資源 53 WSACleanup(); 54 system("pause"); 55 return 1; 56 } 57 //將服務器套接字與地址對象綁定,result->ai_addr是結構體的指針 58 i_result = bind(sock_server, result->ai_addr, static_cast<int>(result->ai_addrlen)); 59 //綁定失敗 60 if (i_result == SOCKET_ERROR) { 61 cerr << "bind() function failed with error: " << WSAGetLastError() << "\n"; 62 freeaddrinfo(result); 63 closesocket(sock_server); 64 WSACleanup(); 65 system("pause"); 66 return 1; 67 } 68 freeaddrinfo(result); 69 cout << "server started successfully..." << endl; 70 //開始監聽 71 cout << "start listening..." << endl; 72 i_result = listen(sock_server, SOMAXCONN); 73 if (i_result == SOCKET_ERROR) { 74 cerr << "listen() function failed with error: " << WSAGetLastError() << "\n"; 75 closesocket(sock_server); 76 system("pause"); 77 return 1; 78 } 79 //接收客戶端請求,獲取客戶端ip地址 80 SOCKADDR_IN addr_client; 81 int len_addr = sizeof(SOCKADDR_IN); 82 char ip_buf[IP_BUF_SIZE]; 83 SecureZeroMemory(ip_buf, IP_BUF_SIZE); 84 sock_client = accept(sock_server, (SOCKADDR*)&addr_client, &len_addr); 85 if (sock_client == INVALID_SOCKET) { 86 cerr << "accept() function failed with error: " << WSAGetLastError() << "\n"; 87 closesocket(sock_server); 88 WSACleanup(); 89 system("pause"); 90 return 1; 91 } 92 cout << "client connected..." << endl; 93 //ip地址轉換 94 inet_ntop(AF_INET, &addr_client, ip_buf, IP_BUF_SIZE); 95 cout << "client ip address: " << ip_buf << endl; 96 //接收和發送數據 97 char recv_buf[RECV_BUF_SIZE]; 98 int send_result = 0; 99 do { 100 //不可缺少,若不將內存空間清零會輸出亂碼,這是因為輸送過來的信息未必有256個字節 101 SecureZeroMemory(recv_buf, RECV_BUF_SIZE); 102 //標志位一般設置為0 103 i_result = recv(sock_client, recv_buf, RECV_BUF_SIZE, 0); 104 if (i_result > 0) { 105 //exit表示客戶端請求斷開連接 106 if (strcmp(recv_buf, "exit") == 0) { 107 cout << "client requests to close the connection..." << endl; 108 break; 109 } 110 //輸出接收的字節數 111 cout << "Bytes received: " << i_result << endl; 112 cout << "message received: " << recv_buf << endl; 113 //向客戶端發送接收到的數據 114 send_result = send(sock_client, recv_buf, i_result, 0); 115 if (send_result == SOCKET_ERROR) { 116 cerr << "send() function failed with error: " << WSAGetLastError() << "\n"; 117 closesocket(sock_client); 118 WSACleanup(); 119 system("pause"); 120 return 1; 121 } 122 } 123 //i_result的值為0表示連接已經關閉 124 else if (i_result == 0) { 125 cout << "connection closed..." << endl; 126 } 127 else { 128 cerr << "recv() function failed with error: " << WSAGetLastError() << "\n"; 129 closesocket(sock_client); 130 WSACleanup(); 131 system("pause"); 132 return 1; 133 } 134 } while (i_result > 0); //do...while語句后注意要有分號 135 //shutdown()禁用套接字的接收或發送功能 136 i_result = shutdown(sock_client, SD_SEND); 137 if (i_result == SOCKET_ERROR) { 138 cerr << "shutdown() function failed with error: " << WSAGetLastError() << "\n"; 139 closesocket(sock_client); 140 WSACleanup(); 141 system("pause"); 142 return 1; 143 } 144 //關閉套接字 145 i_result = closesocket(sock_server); 146 WSACleanup(); 147 cout << "socket closed..." << endl; 148 system("pause"); 149 return 0; 150 }
客戶端:
1 #include <iostream> 2 #include <WinSock2.h> 3 #include <Ws2tcpip.h> 4 5 #pragma comment(lib, "ws2_32.lib") 6 7 using std::cin; 8 using std::cerr; 9 using std::cout; 10 using std::endl; 11 using std::flush; 12 13 const char DEFAULT_PORT[] = "4000"; 14 const int SEND_BUF_SIZE = 256; 15 16 //客戶端 17 int main() { 18 WSADATA wsa_data; //WSADATA變量,包含windows socket執行的信息 19 int i_result = 0; //接收返回值 20 SOCKET sock_client = INVALID_SOCKET; 21 addrinfo *result = nullptr, hints; 22 //初始化winsock動態庫(ws2_32.dll),MAKEWORD(2, 2)用於請求使用winsock2.2版本 23 i_result = WSAStartup(MAKEWORD(2, 2), &wsa_data); 24 if (i_result != 0) { 25 cerr << "WSAStartup() function failed: " << i_result << "\n"; 26 system("pause"); 27 return 1; 28 } 29 SecureZeroMemory(&hints, sizeof(hints)); 30 hints.ai_family = AF_UNSPEC; 31 hints.ai_socktype = SOCK_STREAM; 32 hints.ai_protocol = IPPROTO_TCP; 33 // 34 i_result = getaddrinfo("127.0.0.1", DEFAULT_PORT, &hints, &result); 35 if (i_result != 0) { 36 cerr << "getaddrinfo() function failed with error: " << WSAGetLastError() << "\n"; 37 WSACleanup(); 38 system("pause"); 39 return 1; 40 } 41 //創建套接字 42 sock_client = socket(result->ai_family, result->ai_socktype, result->ai_protocol); 43 if (sock_client == INVALID_SOCKET) { 44 cerr << "socket() function failed with error: " << WSAGetLastError() << "\n"; 45 WSACleanup(); 46 system("pause"); 47 return 1; 48 } 49 //連接服務器 50 i_result = connect(sock_client, result->ai_addr, result->ai_addrlen); 51 if (i_result == SOCKET_ERROR) { 52 cerr << "connect() function failed with error: " << WSAGetLastError() << "\n"; 53 WSACleanup(); 54 system("pause"); 55 return 1; 56 } 57 cout << "connect server successfully..." << endl; 58 // 59 freeaddrinfo(result); 60 // 61 char send_buf[SEND_BUF_SIZE]; 62 int recv_result = 0; 63 //SecureZeroMemory(send_buf, SEND_BUF_SIZE); 64 do { 65 cout << "enter the message that you want to send: " << flush; 66 cin.getline(send_buf, SEND_BUF_SIZE); 67 i_result = send(sock_client, send_buf, static_cast<int>(strlen(send_buf)), 0); 68 if (i_result == SOCKET_ERROR) { 69 cerr << "send() function failed with error: " << WSAGetLastError() << "\n"; 70 closesocket(sock_client); 71 WSACleanup(); 72 system("pause"); 73 return 1; 74 } 75 cout << "Bytes sent: " << i_result << endl; 76 //接收服務器返回的數據 77 recv_result = recv(sock_client, send_buf, SEND_BUF_SIZE, 0); 78 if (recv_result > 0) { 79 cout << "feedback from server: " << send_buf << endl; 80 } 81 else if (recv_result == 0) { 82 cout << "connection closed..." << endl; 83 } 84 else { 85 cerr << "recv() function failed with error: " << WSAGetLastError() << "\n"; 86 closesocket(sock_client); 87 WSACleanup(); 88 system("pause"); 89 return 1; 90 } 91 } while (recv_result > 0); 92 // 93 i_result = shutdown(sock_client, SD_SEND); 94 if (i_result == SOCKET_ERROR) { 95 cerr << "shutdown() function failed with error: " << WSAGetLastError() << "\n"; 96 closesocket(sock_client); 97 WSACleanup(); 98 system("pause"); 99 return 1; 100 } 101 closesocket(sock_client); 102 WSACleanup(); 103 cout << "socket closed..." << endl; 104 system("pause"); 105 return 0; 106 }