返回
【完整源码】C++ select模型打造简单聊天室,轻松玩转网络编程
后端
2024-01-01 08:50:26
一、select模型简介
select模型是一种I/O复用模型,它允许单个进程同时监视多个文件符,当任何一个文件符有I/O事件发生时,select模型会通知进程。
二、聊天室系统设计
聊天室系统主要包括两个部分:服务端和客户端。
- 服务端负责监听客户端连接,并为客户端提供聊天服务。
- 客户端负责连接到服务端,并与其他客户端进行聊天。
三、聊天室服务端源码
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <unistd.h>
#define MAX_CLIENTS 10
#define MAX_MSG_SIZE 1024
int main() {
int sockfd, newsockfd, portno, n;
char buffer[MAX_MSG_SIZE];
struct sockaddr_in serv_addr, cli_addr;
socklen_t clilen;
// 创建一个套接字
sockfd = socket(AF_INET, SOCK_STREAM, 0);
if (sockfd < 0) {
perror("创建套接字失败");
exit(1);
}
// 设置服务器地址结构体
memset(&serv_addr, 0, sizeof(serv_addr));
serv_addr.sin_family = AF_INET;
serv_addr.sin_addr.s_addr = INADDR_ANY;
serv_addr.sin_port = htons(portno);
// 绑定套接字到服务器地址
if (bind(sockfd, (struct sockaddr *) &serv_addr, sizeof(serv_addr)) < 0) {
perror("绑定套接字失败");
exit(1);
}
// 监听套接字
if (listen(sockfd, MAX_CLIENTS) < 0) {
perror("监听套接字失败");
exit(1);
}
// 创建一个select文件描述符集
fd_set readfds;
// 将sockfd添加到select文件描述符集中
FD_ZERO(&readfds);
FD_SET(sockfd, &readfds);
// 循环等待客户端连接
while (1) {
// 使用select函数监视sockfd
int activity = select(sockfd + 1, &readfds, NULL, NULL, NULL);
// 如果sockfd有I/O事件发生
if (FD_ISSET(sockfd, &readfds)) {
// 接受客户端连接
clilen = sizeof(cli_addr);
newsockfd = accept(sockfd, (struct sockaddr *) &cli_addr, &clilen);
if (newsockfd < 0) {
perror("接受客户端连接失败");
continue;
}
// 将新sockfd添加到select文件描述符集中
FD_SET(newsockfd, &readfds);
}
// 遍历所有客户端sockfd,检查是否有数据可读
for (int i = 0; i < MAX_CLIENTS; i++) {
if (FD_ISSET(i, &readfds)) {
// 读取客户端数据
n = read(i, buffer, MAX_MSG_SIZE);
if (n <= 0) {
// 客户端断开连接
close(i);
FD_CLR(i, &readfds);
continue;
}
// 将数据转发给其他客户端
for (int j = 0; j < MAX_CLIENTS; j++) {
if (j != i && FD_ISSET(j, &readfds)) {
write(j, buffer, n);
}
}
}
}
}
// 关闭套接字
close(sockfd);
return 0;
}
四、聊天室客户端源码
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <unistd.h>
#define MAX_MSG_SIZE 1024
int main() {
int sockfd, portno, n;
char buffer[MAX_MSG_SIZE];
struct sockaddr_in serv_addr;
// 创建一个套接字
sockfd = socket(AF_INET, SOCK_STREAM, 0);
if (sockfd < 0) {
perror("创建套接字失败");
exit(1);
}
// 设置服务器地址结构体
memset(&serv_addr, 0, sizeof(serv_addr));
serv_addr.sin_family = AF_INET;
serv_addr.sin_addr.s_addr = inet_addr("127.0.0.1");
serv_addr.sin_port = htons(portno);
// 连接到服务器
if (connect(sockfd, (struct sockaddr *) &serv_addr, sizeof(serv_addr)) < 0) {
perror("连接到服务器失败");
exit(1);
}
// 创建一个新线程来处理从服务器接收到的数据
pthread_t tid;
pthread_create(&tid, NULL, recv_msg, (void *) sockfd);
// 主线程负责从标准输入读取数据并发送给服务器
while (1) {
// 读取标准输入
fgets(buffer, MAX_MSG_SIZE, stdin);
// 将数据发送给服务器
n = write(sockfd, buffer, strlen(buffer));
if (n <= 0) {
perror("发送数据失败");
exit(1);
}
}
// 关闭套接字
close(sockfd);
return 0;
}
void *recv_msg(void *arg) {
int sockfd = (int) arg;
char buffer[MAX_MSG_SIZE];
// 从服务器接收数据
while (1) {
int n = read(sockfd, buffer, MAX_MSG_SIZE);
if (n <= 0) {
perror("接收数据失败");
exit(1);
}
// 将数据打印到标准输出
printf("%s", buffer);
}
return NULL;
}
五、运行聊天室
- 在服务端机器上运行服务端程序。
- 在客户端机器上运行客户端程序。
- 在客户端程序中输入要发送的消息,然后按回车键发送。
- 服务端程序会将消息转发给其他客户端。
- 所有客户端都可以看到其他客户端发送的消息。
六、结语
通过本文,你已经学习了如何使用C++和select模型构建一个简单聊天室。我希望本文对你有帮助,也希望你能在网络编程的道路上越走越远。