返回

【完整源码】C++ select模型打造简单聊天室,轻松玩转网络编程

后端

一、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;
}

五、运行聊天室

  1. 在服务端机器上运行服务端程序。
  2. 在客户端机器上运行客户端程序。
  3. 在客户端程序中输入要发送的消息,然后按回车键发送。
  4. 服务端程序会将消息转发给其他客户端。
  5. 所有客户端都可以看到其他客户端发送的消息。

六、结语

通过本文,你已经学习了如何使用C++和select模型构建一个简单聊天室。我希望本文对你有帮助,也希望你能在网络编程的道路上越走越远。