本文介绍如何使用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++创建一个基本的客户端-服务端通信系统。这是一个基础框架,可以根据实际需求进行扩展和改进,用于构建更复杂的网络应用。