实现 TCP 服务器/客户端的多人聊天室,需要按照以下步骤进行:
1. 设计数据结构:需要设计用户信息结构体,保存用户的账号、密码等信息。
2. 编写服务器端代码:服务器端代码需要实现以下功能:
- 读取用户信息文件,保存到内存中。
- 等待客户端连接,接受客户端连接请求。
- 接收客户端发送的数据,处理聊天室的消息。
- 将聊天室消息广播给所有连接的客户端。
3. 编写客户端代码:客户端代码需要实现以下功能:
- 注册新用户,向服务器发送注册信息。
- 登录聊天室,向服务器发送登录信息。
- 发送聊天消息,向服务器发送聊天消息。
- 接收聊天消息,显示在客户端界面上。
以下是 Windows 下实现 C/C++ TCP 服务器/客户端的多人聊天室的示例代码:
服务器端代码:
```c++
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <winsock2.h>
#define PORT 8888
#define MAX_CLIENTS 30
#define MAX_USERNAME_LEN 20
#define MAX_PASSWORD_LEN 20
#define MAX_MSG_LEN 1024
// 用户信息结构体
typedef struct {
char username[MAX_USERNAME_LEN];
char password[MAX_PASSWORD_LEN];
} user_info_t;
// 全局变量
SOCKET clients[MAX_CLIENTS]; // 客户端列表
int num_clients = 0; // 当前连接的客户端数量
user_info_t users[MAX_CLIENTS]; // 用户信息
int num_users = 0; // 当前注册的用户数量
// 读取用户信息文件
void read_user_info_file() {
FILE* fp = fopen("user_info.txt", "r");
if (fp == NULL) {
printf("Failed to open user info file.\n");
exit(1);
}
while (fscanf(fp, "%s %s", users[num_users].username, users[num_users].password) != EOF) {
num_users++;
}
fclose(fp);
}
// 处理客户端连接
void handle_client(SOCKET client_socket) {
printf("Client connected.\n");
clients[num_clients++] = client_socket;
// 接收客户端发送的消息
char msg[MAX_MSG_LEN];
int recv_size;
while ((recv_size = recv(client_socket, msg, MAX_MSG_LEN, 0)) > 0) {
// 处理注册请求
if (strncmp(msg, "register ", 9) == 0) {
// 解析用户名和密码
char* username = strtok(msg + 9, " ");
char* password = strtok(NULL, " ");
if (username != NULL && password != NULL) {
// 检查用户名是否已经存在
int i;
for (i = 0; i < num_users; i++) {
if (strcmp(users[i].username, username) == 0) {
send(client_socket, "Username already exists.", strlen("Username already exists."), 0);
break;
}
}
if (i == num_users) {
// 添加新用户
strcpy(users[num_users].username, username);
strcpy(users[num_users].password, password);
num_users++;
send(client_socket, "Registration successful.", strlen("Registration successful."), 0);
}
}
}
// 处理登录请求
else if (strncmp(msg, "login ", 6) == 0) {
// 解析用户名和密码
char* username = strtok(msg + 6, " ");
char* password = strtok(NULL, " ");
if (username != NULL && password != NULL) {
// 检查用户名和密码是否匹配
int i;
for (i = 0; i < num_users; i++) {
if (strcmp(users[i].username, username) == 0 && strcmp(users[i].password, password) == 0) {
send(client_socket, "Login successful.", strlen("Login successful."), 0);
break;
}
}
if (i == num_users) {
send(client_socket, "Incorrect username or password.", strlen("Incorrect username or password."), 0);
}
}
}
// 处理聊天消息
else {
// 广播聊天消息给所有客户端
int i;
for (i = 0; i < num_clients; i++) {
if (clients[i] != client_socket) {
send(clients[i], msg, recv_size, 0);
}
}
}
}
// 客户端断开连接
printf("Client disconnected.\n");
int i;
for (i = 0; i < num_clients; i++) {
if (clients[i] == client_socket) {
while (i < num_clients - 1) {
clients[i] = clients[i + 1];
i++;
}
num_clients--;
break;
}
}
closesocket(client_socket);
}
int main() {
// 初始化 Winsock 库
WSADATA wsa;
if (WSAStartup(MAKEWORD(2, 2), &wsa) != 0) {
printf("Failed to initialize Winsock.\n");
return 1;
}
// 创建监听 socket
SOCKET listen_socket = socket(AF_INET, SOCK_STREAM, 0);
if (listen_socket == INVALID_SOCKET) {
printf("Failed to create listen socket.\n");
return 1;
}
// 绑定监听 socket
sockaddr_in server_addr;
server_addr.sin_family = AF_INET;
server_addr.sin_addr.s_addr = INADDR_ANY;
server_addr.sin_port = htons(PORT);
if (bind(listen_socket, (sockaddr*)&server_addr, sizeof(server_addr)) == SOCKET_ERROR) {
printf("Failed to bind listen socket.\n");
return 1;
}
// 开始监听连接请求
listen(listen_socket, 5);
printf("Server started.\n");
// 读取用户信息文件
read_user_info_file();
// 处理客户端连接
sockaddr_in client_addr;
int client_addr_len = sizeof(client_addr);
while (1) {
SOCKET client_socket = accept(listen_socket, (sockaddr*)&client_addr, &client_addr_len);
if (client_socket == INVALID_SOCKET) {
printf("Failed to accept client connection.\n");
break;
}
handle_client(client_socket);
}
// 关闭监听 socket
closesocket(listen_socket);
// 清理 Winsock 库
WSACleanup();
return 0;
}
```
客户端代码:
```c++
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <winsock2.h>
#define PORT 8888
#define MAX_MSG_LEN 1024
// 发送消息
void send_msg(SOCKET socket, const char* msg) {
send(socket, msg, strlen(msg), 0);
}
// 接收消息
void recv_msg(SOCKET socket, char* msg) {
int recv_size = recv(socket, msg, MAX_MSG_LEN, 0);
msg[recv_size] = '\0';
}
int main() {
// 初始化 Winsock 库
WSADATA wsa;
if (WSAStartup(MAKEWORD(2, 2), &wsa) != 0) {
printf("Failed to initialize Winsock.\n");
return 1;
}
// 创建 socket
SOCKET socket_client = socket(AF_INET, SOCK_STREAM, 0);
if (socket_client == INVALID_SOCKET) {
printf("Failed to create socket.\n");
return 1;
}
// 连接服务器
sockaddr_in server_addr;
server_addr.sin_family = AF_INET;
server_addr.sin_addr.s_addr = inet_addr("127.0.0.1");
server_addr.sin_port = htons(PORT);
if (connect(socket_client, (sockaddr*)&server_addr, sizeof(server_addr)) == SOCKET_ERROR) {
printf("Failed to connect to server.\n");
return 1;
}
// 登录聊天室
char username[20], password[20], msg[MAX_MSG_LEN];
printf("Enter your username:\n");
scanf("%s", username);
printf("Enter your password:\n");
scanf("%s", password);
sprintf(msg, "login %s %s", username, password);
send_msg(socket_client, msg);
recv_msg(socket_client, msg);
printf("%s\n", msg);
// 发送和接收聊天消息
fd_set read_fds;
FD_ZERO(&read_fds);
FD_SET(socket_client, &read_fds);
FD_SET(STDIN_FILENO, &read_fds);
while (1) {
fd_set tmp_fds = read_fds;
int ret = select(0, &tmp_fds, NULL, NULL, NULL);
if (ret < 0) {
printf("Failed to select.\n");
break;
}
if (FD_ISSET(socket_client, &tmp_fds)) {
// 接收聊天消息
recv_msg(socket_client, msg);
printf("%s\n", msg);
}
if (FD_ISSET(STDIN_FILENO, &tmp_fds)) {
// 发送聊天消息
scanf("%s", msg);
send_msg(socket_client, msg);
}
}
// 关闭 socket
closesocket(socket_client);
// 清理 Winsock 库
WSACleanup();
return 0;
}
```