本文介紹如何使用C++編寫一個基本的客戶端-服務端通信系統。通過這個例子,你將學到如何建立TCP連接、發送和接收消息,以及如何處理多個客戶端連接。
客戶端代碼:
#include <stdio.h> // 標準輸入輸出庫,提供基本的輸入輸出功能
#include <stdlib.h> // 標準庫,包含了一些通用的函數和動態內存分配函數
#include <string.h> // 字符串處理庫,提供字符串操作的各種函數
#include <unistd.h> // Linux系統調用接口,包含了一些常用的系統調用函數
#include <arpa/inet.h> // 提供了一些函數,用于對IPv4和IPv6地址進行轉換
#include <errno.h> // 用于獲取錯誤碼,提供 perror 函數來輸出錯誤信息int main(int argc, char *argv[]) {if (argc != 4) {fprintf(stderr, "Usage: %s <server_ip> <server_port> <message>\n", argv[0]);return EXIT_FAILURE;}int sockfd;struct sockaddr_in servaddr;// 創建套接字if ((sockfd = socket(AF_INET, SOCK_STREAM, 0)) < 0) {perror("create socket error");return EXIT_FAILURE;}// 初始化服務器地址結構體memset(&servaddr, 0, sizeof(servaddr));servaddr.sin_family = AF_INET;servaddr.sin_port = htons(atoi(argv[2])); // 將端口號從字符串轉換為整數// 將IP地址從字符串轉換為網絡地址if (inet_pton(AF_INET, argv[1], &servaddr.sin_addr) <= 0) {perror("inet_pton error");close(sockfd);return EXIT_FAILURE;}// 發起連接請求if (connect(sockfd, (struct sockaddr*)&servaddr, sizeof(servaddr)) < 0) {perror("connect error");close(sockfd);return EXIT_FAILURE;}printf("Connected to the server. Sending message: %s\n", argv[3]);// 發送消息到服務器if (send(sockfd, argv[3], strlen(argv[3]), 0) < 0) {perror("send error");close(sockfd);return EXIT_FAILURE;}// 接收服務器的響應char buffer[1024];ssize_t recv_len = recv(sockfd, buffer, sizeof(buffer), 0);if (recv_len < 0) {perror("recv error");close(sockfd);return EXIT_FAILURE;} else if (recv_len == 0) {printf("Connection closed by the server\n");} else {buffer[recv_len] = '\0';printf("Received from server: %s\n", buffer);}// 關閉套接字close(sockfd);return EXIT_SUCCESS;
}
服務端代碼:
#include <iostream> // 輸入輸出流庫,提供了輸入輸出的各種功能
#include <cstring> // 字符串處理庫,提供了字符串操作的各種函數
#include <thread> // 多線程支持庫,用于創建和管理線程
#include <vector> // 動態數組容器,提供了對動態數組的支持
#include <mutex> // 互斥鎖庫,提供了對互斥鎖的支持
#include <queue> // 隊列容器,提供了對隊列的支持
#include <netinet/in.h> // 網絡編程庫,包含了與網絡相關的數據結構和函數
#include <unistd.h> // Linux系統調用接口,包含了一些常用的系統調用函數const int MAXBUFF = 1024;std::mutex g_mx; // 用于保護共享資源的互斥鎖
std::queue<std::pair<int, std::string>> g_dataQue; // 存儲客戶端套接字和消息的隊列// 在單獨的線程中處理從客戶端接收到的消息
void writeThread() {while (true) {g_mx.lock(); // 上鎖以確保安全訪問共享資源if (!g_dataQue.empty()) {int clientFd = g_dataQue.front().first;std::string data = g_dataQue.front().second;std::cout << "從客戶端接收到的消息:" << data << std::endl;// 假設有一個處理消息并返回響應的函數std::string response = "Server response: 你好,客戶端!";send(clientFd, response.c_str(), response.size(), 0);g_dataQue.pop(); // 從隊列中移除已處理的消息}g_mx.unlock(); // 解鎖}
}int main() {int listenFd, clientFd;struct sockaddr_in servaddr;if ((listenFd = socket(AF_INET, SOCK_STREAM, 0)) < 0) {std::cerr << "創建套接字錯誤" << std::endl;return -1;}memset(&servaddr, 0, sizeof(servaddr));servaddr.sin_family = AF_INET;servaddr.sin_addr.s_addr = htonl(INADDR_ANY);servaddr.sin_port = htons(1121);if (bind(listenFd, (struct sockaddr *)&servaddr, sizeof(servaddr)) < 0) {std::cerr << "綁定套接字地址和端口錯誤" << std::endl;return -1;}if (listen(listenFd, 10) < 0) {std::cerr << "開啟監聽錯誤" << std::endl;return -1;}std::thread write_thread(writeThread); // 創建一個線程來處理從客戶端接收到的消息size_t readLen = 0;while (true) {struct sockaddr_in client_addr;socklen_t size = sizeof(client_addr);if ((clientFd = accept(listenFd, (struct sockaddr *)&client_addr, &size)) < 0) {std::cerr << "建立連接錯誤" << std::endl;return -1;}// 假設有一個讀取函數來從 clientFd 中讀取數據char buff[MAXBUFF] = {0};readLen = read(clientFd, buff, MAXBUFF);if (readLen <= 0) {close(clientFd); // 在讀取到數據后關閉客戶端連接break;}std::string data(buff, readLen);g_mx.lock(); // 上鎖以確保安全訪問共享資源g_dataQue.push(std::make_pair(clientFd, data)); // 將接收到的消息和客戶端套接字放入隊列g_mx.unlock(); // 解鎖}write_thread.join(); // 等待寫線程結束close(listenFd);return 0;
}
客戶端使用方法:
打開終端,進入客戶端代碼所在的目錄。使用以下命令運行客戶端程序:
gcc client.cpp -o client
./client <server_ip> <server_port> <message>
替換 <server_ip>
、<server_port>
和 <message>
分別為服務器的IP地址、端口號和要發送的消息。
服務端使用方法:
打開終端,進入服務端代碼所在的目錄。使用以下命令編譯并運行服務端程序:
g++ server.cpp -o server -lpthread
./server
服務端將開始監聽連接。
主要功能:
客戶端:
- 接受命令行參數,包括服務器IP、端口號和要發送的消息。
- 創建套接字,連接到服務器。
- 發送消息到服務器,接收并打印服務器的響應。
客戶端界面
Connected to the server. Sending message: 你好,服務器!
Received from server: Server response: 你好,客戶端!
服務端:
- 創建套接字,綁定地址和端口,開始監聽。
- 接受客戶端連接,將客戶端套接字和消息放入隊列。
- 在單獨的線程中處理隊列中的消息,發送響應到客戶端。
服務端界面
從客戶端接收到的消息:你好,服務器!
注意事項:
- 通過互斥鎖保護共享資源,確保線程安全的訪問。
- 在服務端中,處理客戶端消息后返回一個固定的響應。
結論:
通過這個簡單的例子,你學到了如何使用C++創建一個基本的客戶端-服務端通信系統。這是一個基礎框架,可以根據實際需求進行擴展和改進,用于構建更復雜的網絡應用。