c++下基於windows socket的單線程服務器客戶端程序(基於TCP協議)


今天自己編寫了一個簡單的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 }

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM