用C实现网络并发群聊私聊
时间: 2025-08-21 22:07:40 浏览: 2
<think>我们使用C语言实现一个支持并发的网络聊天程序,包括群聊和私聊功能。为了实现并发,我们可以使用多线程或者I/O多路复用技术(如select、poll、epoll)。这里我们选择使用多线程,因为实现相对简单,适合初学者理解。
程序分为服务端和客户端两部分:
服务端:
1. 监听指定端口,等待客户端连接。
2. 为每个连接的客户端创建一个线程,用于接收该客户端发送的消息。
3. 维护一个客户端列表,记录每个客户端的socket、用户名等信息。
4. 实现消息转发功能:当收到一个客户端的消息时,根据消息类型(群聊或私聊)将消息转发给相应的客户端。
客户端:
1. 连接服务端。
2. 启动两个线程:一个用于接收服务端转发的消息并显示,另一个用于读取用户输入并发送给服务端。
功能设计:
- 群聊:客户端发送的消息默认是群聊消息,服务端会转发给所有客户端(除了发送者自己)。
- 私聊:客户端可以指定接收消息的用户名,格式如:@username message。服务端解析后,将消息只发送给指定用户。
数据结构:
服务端需要维护一个客户端列表,我们可以用链表或数组来存储。每个客户端信息包括:
- socket描述符
- 用户名
- 其他信息(如IP地址等)
步骤:
服务端代码框架:
1. 创建监听socket,绑定端口,监听。
2. 创建客户端列表(链表)并初始化。
3. 循环接受客户端连接。
4. 为每个客户端连接创建一个线程,处理该客户端的消息。
在客户端处理线程中:
- 接收客户端发来的用户名(或第一个消息作为用户名)。
- 将该客户端加入客户端列表。
- 循环接收该客户端的消息,并根据消息类型进行转发。
注意:在操作客户端列表(如添加、删除、遍历)时,需要使用互斥锁来保证线程安全。
客户端代码框架:
1. 连接服务端。
2. 发送用户名(第一个消息发送用户名)。
3. 创建两个线程:一个用于接收服务端消息并打印,另一个用于读取用户输入并发送。
下面我们分别给出服务端和客户端的关键代码。
服务端代码(server.c):</think>### C语言实现并发网络聊天程序(支持群聊和私聊)
下面我将分步骤实现一个支持并发、群聊和私聊功能的网络聊天程序。程序使用多线程技术处理并发连接,采用TCP协议进行通信。
#### 1. 服务端实现
```c
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <arpa/inet.h>
#include <pthread.h>
#include <sys/socket.h>
#define MAX_CLIENTS 100
#define BUFFER_SIZE 1024
#define PORT 8080
typedef struct {
int sockfd;
char username[32];
int active;
} Client;
Client clients[MAX_CLIENTS];
pthread_mutex_t clients_mutex = PTHREAD_MUTEX_INITIALIZER;
void send_message(char *message, int sender_sock) {
pthread_mutex_lock(&clients_mutex);
for (int i = 0; i < MAX_CLIENTS; i++) {
if (clients[i].active && clients[i].sockfd != sender_sock) {
if (send(clients[i].sockfd, message, strlen(message), 0) < 0) {
perror("Send failed");
clients[i].active = 0;
}
}
}
pthread_mutex_unlock(&clients_mutex);
}
void send_private(char *recipient, char *message, int sender_sock) {
pthread_mutex_lock(&clients_mutex);
for (int i = 0; i < MAX_CLIENTS; i++) {
if (clients[i].active && strcmp(clients[i].username, recipient) == 0) {
if (send(clients[i].sockfd, message, strlen(message), 0) < 0) {
perror("Private send failed");
}
return;
}
}
// 通知发送者接收者不存在
char error_msg[BUFFER_SIZE];
snprintf(error_msg, BUFFER_SIZE, "ERROR: User '%s' not found\n", recipient);
send(sender_sock, error_msg, strlen(error_msg), 0);
pthread_mutex_unlock(&clients_mutex);
}
void *handle_client(void *arg) {
int sockfd = *((int *)arg);
char buffer[BUFFER_SIZE];
char username[32];
// 获取用户名
if (recv(sockfd, username, sizeof(username), 0) <= 0) {
perror("Username recv failed");
close(sockfd);
return NULL;
}
// 添加到客户端列表
pthread_mutex_lock(&clients_mutex);
for (int i = 0; i < MAX_CLIENTS; i++) {
if (!clients[i].active) {
clients[i].sockfd = sockfd;
strcpy(clients[i].username, username);
clients[i].active = 1;
break;
}
}
pthread_mutex_unlock(&clients_mutex);
// 通知所有用户新成员加入
char welcome_msg[BUFFER_SIZE];
snprintf(welcome_msg, BUFFER_SIZE, "SYSTEM: %s joined the chat\n", username);
send_message(welcome_msg, sockfd);
// 消息处理循环
while (1) {
memset(buffer, 0, BUFFER_SIZE);
int recv_size = recv(sockfd, buffer, BUFFER_SIZE, 0);
if (recv_size <= 0) {
// 客户端断开连接
pthread_mutex_lock(&clients_mutex);
for (int i = 0; i < MAX_CLIENTS; i++) {
if (clients[i].sockfd == sockfd) {
clients[i].active = 0;
break;
}
}
pthread_mutex_unlock(&clients_mutex);
char leave_msg[BUFFER_SIZE];
snprintf(leave_msg, BUFFER_SIZE, "SYSTEM: %s left the chat\n", username);
send_message(leave_msg, sockfd);
close(sockfd);
return NULL;
}
// 处理私聊消息(格式:@username message)
if (buffer[0] == '@') {
char *space = strchr(buffer, ' ');
if (space) {
*space = '\0';
char *recipient = buffer + 1;
char *msg = space + 1;
char private_msg[BUFFER_SIZE];
snprintf(private_msg, BUFFER_SIZE, "[PRIVATE from %s]: %s", username, msg);
send_private(recipient, private_msg, sockfd);
}
}
// 处理群聊消息
else {
char public_msg[BUFFER_SIZE];
snprintf(public_msg, BUFFER_SIZE, "[PUBLIC %s]: %s", username, buffer);
send_message(public_msg, sockfd);
}
}
return NULL;
}
int main() {
int server_fd, new_socket;
struct sockaddr_in address;
int addrlen = sizeof(address);
// 创建socket
if ((server_fd = socket(AF_INET, SOCK_STREAM, 0)) == 0) {
perror("Socket creation failed");
exit(EXIT_FAILURE);
}
address.sin_family = AF_INET;
address.sin_addr.s_addr = INADDR_ANY;
address.sin_port = htons(PORT);
// 绑定端口
if (bind(server_fd, (struct sockaddr *)&address, sizeof(address)) < 0) {
perror("Bind failed");
exit(EXIT_FAILURE);
}
// 监听
if (listen(server_fd, 10) < 0) {
perror("Listen failed");
exit(EXIT_FAILURE);
}
printf("Server listening on port %d...\n", PORT);
// 初始化客户端列表
for (int i = 0; i < MAX_CLIENTS; i++) {
clients[i].active = 0;
}
// 接受连接
while (1) {
if ((new_socket = accept(server_fd, (struct sockaddr *)&address, (socklen_t*)&addrlen)) < 0) {
perror("Accept failed");
continue;
}
pthread_t thread_id;
int *client_sock = malloc(sizeof(int));
*client_sock = new_socket;
if (pthread_create(&thread_id, NULL, handle_client, (void*)client_sock) < 0) {
perror("Thread creation failed");
close(new_socket);
free(client_sock);
}
pthread_detach(thread_id);
}
return 0;
}
```
#### 2. 客户端实现
```c
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <arpa/inet.h>
#include <pthread.h>
#include <sys/socket.h>
#define BUFFER_SIZE 1024
#define SERVER_IP "127.0.0.1"
#define PORT 8080
void *receive_handler(void *sock_ptr) {
int sockfd = *((int *)sock_ptr);
char buffer[BUFFER_SIZE];
while (1) {
memset(buffer, 0, BUFFER_SIZE);
int recv_size = recv(sockfd, buffer, BUFFER_SIZE, 0);
if (recv_size <= 0) {
printf("\nDisconnected from server\n");
exit(EXIT_FAILURE);
}
printf("%s", buffer);
}
return NULL;
}
int main() {
int sockfd;
struct sockaddr_in server_addr;
char buffer[BUFFER_SIZE];
char username[32];
// 创建socket
if ((sockfd = socket(AF_INET, SOCK_STREAM, 0)) < 0) {
perror("Socket creation failed");
exit(EXIT_FAILURE);
}
server_addr.sin_family = AF_INET;
server_addr.sin_port = htons(PORT);
// 转换IP地址
if (inet_pton(AF_INET, SERVER_IP, &server_addr.sin_addr) <= 0) {
perror("Invalid address");
exit(EXIT_FAILURE);
}
// 连接服务器
if (connect(sockfd, (struct sockaddr *)&server_addr, sizeof(server_addr)) < 0) {
perror("Connection failed");
exit(EXIT_FAILURE);
}
// 输入用户名
printf("Enter username: ");
fgets(username, sizeof(username), stdin);
username[strcspn(username, "\n")] = '\0'; // 移除换行符
// 发送用户名到服务器
send(sockfd, username, strlen(username), 0);
printf("Connected to server. Commands:\n");
printf("- Type message for group chat\n");
printf("- Type @username message for private chat\n");
printf("----------------------------------------\n");
// 创建接收线程
pthread_t recv_thread;
if (pthread_create(&recv_thread, NULL, receive_handler, (void *)&sockfd) < 0) {
perror("Thread creation failed");
exit(EXIT_FAILURE);
}
// 消息输入循环
while (1) {
memset(buffer, 0, BUFFER_SIZE);
fgets(buffer, BUFFER_SIZE, stdin);
buffer[strcspn(buffer, "\n")] = '\0'; // 移除换行符
if (send(sockfd, buffer, strlen(buffer), 0) < 0) {
perror("Send failed");
break;
}
}
close(sockfd);
return 0;
}
```
#### 3. 功能说明
1. **群聊功能**:
- 默认发送的消息会广播给所有在线用户
- 格式:`普通消息内容`
2. **私聊功能**:
- 使用`@用户名 消息内容`格式发送私聊
- 示例:`@john Hello!` 只发送给用户john
3. **系统消息**:
- 用户加入/离开时自动通知所有用户
- 错误处理(如私聊用户不存在)
4. **并发处理**:
- 使用多线程处理每个客户端连接
- 互斥锁保护共享资源(客户端列表)
#### 4. 编译与运行
1. **编译服务端**:
```bash
gcc server.c -o server -lpthread
```
2. **编译客户端**:
```bash
gcc client.c -o client -lpthread
```
3. **运行服务端**:
```bash
./server
```
4. **运行客户端**(多开终端):
```bash
./client
```
#### 5. 使用示例
1. 用户A输入:`Hello everyone!`
- 所有在线用户收到:`[PUBLIC userA]: Hello everyone!`
2. 用户B输入:`@userA How are you?`
- 只有userA收到:`[PRIVATE from userB]: How are you?`
3. 用户C加入/离开:
- 所有用户收到:`SYSTEM: userC joined/left the chat`
#### 6. 技术要点
1. **并发模型**:每个客户端连接使用独立线程处理
2. **线程安全**:使用互斥锁保护共享数据结构
3. **消息协议**:
- 第一条消息作为用户名
- `@`前缀标识私聊消息
4. **错误处理**:
- 无效用户名检测
- 连接异常处理
- 私聊目标不存在提示
此实现支持基本的群聊和私聊功能,可通过扩展实现更多功能如用户列表查询、文件传输等[^1][^2][^3]。
---
阅读全文
相关推荐




















