epoll 实现简单聊天程序代码示例

提示:文章写完后,目录可以自动生成,如何生成可参考右边的帮助文档


前言

提示:这里可以添加本文要记录的大概内容:

epoll 实现简单聊天程序代码示例
c代码实现,可以运行


提示:以下是本篇文章正文内容,下面案例可供参考

一、epoll是什么?

epoll 是 Linux 内核提供的一种高效的 I/O 事件通知机制,它允许程序注册多个文件描述符,并监控这些文件描述符上的 I/O 事件。epoll 是在 Linux 2.6 内核中引入的,它比早期的 select 和 poll 更加高效,尤其是在处理大量文件描述符时。

epoll 的主要特点
效率高:
epoll 使用事件驱动模型,而不是轮询所有文件描述符的状态。
epoll 只会通知那些状态发生变化的文件描述符,因此在文件描述符数量较多时效率非常高。
灵活:
epoll 支持多种事件类型,包括读、写、错误等。
可以针对每个文件描述符设置不同的事件掩码。
可扩展性:
epoll 不受限于文件描述符的数量限制,理论上可以处理成千上万个文件描述符。
epoll 的性能随着文件描述符数量的增长变化不大,非常适合处理大量并发连接。
低延迟:
epoll 使用高效的内核数据结构来存储文件描述符信息。
内核和用户空间之间的数据交换最小化,从而减少了上下文切换和复制开销。

主要函数:

epoll_create 创建一个新的 epoll 文件描述符
epoll_ctl 用于向 epoll 文件描述符中添加、修改或删除文件描述符
epoll_wait 用来等待文件描述符上的事件。

二、使用步骤

1.ser代码

代码如下(示例):

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <sys/socket.h>
#include <sys/un.h>
#include <sys/epoll.h>
#include <fcntl.h>

#define SOCKET_PATH "/tmp/chat.sock"
#define BUFFER_SIZE 1024

int main() {
    int server_socket, client_socket;
    struct sockaddr_un server_addr, client_addr;
    socklen_t client_len;
    int cli_fd = 0;

    // 创建 Unix 域套接字
    server_socket = socket(AF_UNIX, SOCK_STREAM, 0);
    if (server_socket == -1) {
        perror("socket");
        exit(EXIT_FAILURE);
    }

    // 清除旧的 socket 文件
    unlink(SOCKET_PATH);

    // 准备地址结构
    memset(&server_addr, 0, sizeof(server_addr));
    server_addr.sun_family = AF_UNIX;
    strncpy(server_addr.sun_path, SOCKET_PATH, sizeof(server_addr.sun_path) - 1);

    // 绑定套接字
    if (bind(server_socket, (struct sockaddr *)&server_addr, sizeof(server_addr)) == -1) {
        perror("bind");
        exit(EXIT_FAILURE);
    }

    // 开始监听
    if (listen(server_socket, 1) == -1) {
        perror("listen");
        exit(EXIT_FAILURE);
    }

    // 接受客户端连接
    /*client_socket = accept(server_socket, (struct sockaddr *)&client_addr, &client_len);
    if (client_socket == -1) {
        perror("accept");
        exit(EXIT_FAILURE);
    }

    // 设置套接字为非阻塞模式
    int flags = fcntl(client_socket, F_GETFL, 0);
    fcntl(client_socket, F_SETFL, flags | O_NONBLOCK);
*/
    char buffer[BUFFER_SIZE];
    struct epoll_event events[3];
    int epoll_fd = epoll_create1(0);
    if (epoll_fd == -1) {
        perror("epoll_create1");
        exit(EXIT_FAILURE);
    }

    // 添加标准输入和客户端套接字到 epoll
    struct epoll_event event;
    event.events = EPOLLIN;
    event.data.fd = STDIN_FILENO;
    if (epoll_ctl(epoll_fd, EPOLL_CTL_ADD, STDIN_FILENO, &event) == -1) {
        perror("epoll_ctl (stdin)");
        exit(EXIT_FAILURE);
    }

    event.events = EPOLLIN | EPOLLET; // 边缘触发模式
    event.data.fd = server_socket;
    if (epoll_ctl(epoll_fd, EPOLL_CTL_ADD, server_socket, &event) == -1) {
        perror("epoll_ctl (client_socket)");
        exit(EXIT_FAILURE);
    }

    while (1) {
        int nready = epoll_wait(epoll_fd, events, 3, -1);
        if (nready == -1) {
            perror("epoll_wait");
            break;
        }

        for (int i = 0; i < nready; ++i) {
            if (events[i].data.fd == STDIN_FILENO) {
                if (fgets(buffer, BUFFER_SIZE, stdin) != NULL) {
                        if(cli_fd != 0)
                        write(cli_fd, buffer, strlen(buffer)); // 发送消息给客户端
                    
                }
            }else if (events[i].data.fd == server_socket) {
                printf("events[i].data.fd == server_socket accept\n");
                client_len = sizeof(client_addr);
                client_socket = accept(server_socket, (struct sockaddr *)&client_addr, &client_len);
                if (client_socket == -1) {
                    perror("accept");
                    continue;
                }
                
                event.events = EPOLLIN | EPOLLET; // 边缘触发模式
                event.data.fd = client_socket;
                if (epoll_ctl(epoll_fd, EPOLL_CTL_ADD, client_socket, &event) == -1) {
                    perror("epoll_ctl (client_socket)");
                    continue;
                }
                
                cli_fd = client_socket;
            
            }else/* if (events[i].data.fd == client_socket) */{
                //printf("enter read, events[j].data.fd = 0x%x\n", events[i].data.fd);
                ssize_t nread = read(events[i].data.fd, buffer, BUFFER_SIZE - 1);
                if (nread > 0) {
                    buffer[nread] = '\0';
                    printf("Received: %s", buffer);
                } else if (nread == 0) {
                    printf("Client disconnected.\n");
                    epoll_ctl(epoll_fd, EPOLL_CTL_DEL, events[i].data.fd, NULL);
                    close(events[i].data.fd);
                    cli_fd = 0;
                    continue;
                } else {
                    perror("Error reading from client");
                    break;
                }
            }
        }
    }

    // 清理
    close(client_socket);
    close(server_socket);
    close(epoll_fd);
    unlink(SOCKET_PATH);

    return 0;
}

2.cli代码

代码如下(示例):

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <sys/socket.h>
#include <sys/un.h>
#include <sys/epoll.h>
#include <fcntl.h>

#define SOCKET_PATH "/tmp/chat.sock"
#define BUFFER_SIZE 1024

int main() {
    int client_socket;
    struct sockaddr_un server_addr;

    // 创建 Unix 域套接字
    client_socket = socket(AF_UNIX, SOCK_STREAM, 0);
    if (client_socket == -1) {
        perror("socket");
        exit(EXIT_FAILURE);
    }

    // 准备地址结构
    memset(&server_addr, 0, sizeof(server_addr));
    server_addr.sun_family = AF_UNIX;
    strncpy(server_addr.sun_path, SOCKET_PATH, sizeof(server_addr.sun_path) - 1);

    // 连接到服务器
    if (connect(client_socket, (struct sockaddr *)&server_addr, sizeof(server_addr)) == -1) {
        perror("connect");
        exit(EXIT_FAILURE);
    }

    // 设置套接字为非阻塞模式
    int flags = fcntl(client_socket, F_GETFL, 0);
    fcntl(client_socket, F_SETFL, flags | O_NONBLOCK);

    char buffer[BUFFER_SIZE];
    struct epoll_event events[2];
    int epoll_fd = epoll_create1(0);
    if (epoll_fd == -1) {
        perror("epoll_create1");
        exit(EXIT_FAILURE);
    }

    // 添加标准输入和客户端套接字到 epoll
    struct epoll_event event;
    event.events = EPOLLIN;
    event.data.fd = STDIN_FILENO;
    if (epoll_ctl(epoll_fd, EPOLL_CTL_ADD, STDIN_FILENO, &event) == -1) {
        perror("epoll_ctl (stdin)");
        exit(EXIT_FAILURE);
    }

    event.events = EPOLLIN | EPOLLET; // 边缘触发模式
    event.data.fd = client_socket;
    if (epoll_ctl(epoll_fd, EPOLL_CTL_ADD, client_socket, &event) == -1) {
        perror("epoll_ctl (client_socket)");
        exit(EXIT_FAILURE);
    }

    while (1) {
        int nready = epoll_wait(epoll_fd, events, 2, -1);
        if (nready == -1) {
            perror("epoll_wait");
            break;
        }

        for (int i = 0; i < nready; ++i) {
            if (events[i].data.fd == STDIN_FILENO) {
                if (fgets(buffer, BUFFER_SIZE, stdin) != NULL) {
                    write(client_socket, buffer, strlen(buffer)); // 发送消息给服务器
                }
            } else if (events[i].data.fd == client_socket) {
                ssize_t nread = read(client_socket, buffer, BUFFER_SIZE - 1);
                if (nread > 0) {
                    buffer[nread] = '\0';
                    printf("Received: %s", buffer);
                } else if (nread == 0) {
                    printf("Server disconnected.\n");
                    break;
                } else {
                    perror("Error reading from server");
                    break;
                }
            }
        }
    }

    // 清理
    close(client_socket);
    close(epoll_fd);

    return 0;
}

总结

epoll 实现简单聊天程序c代码示例,stdin输入发送到cli端,代码可以运行,仅供参考

  • 8
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值