IO多路复用

epoll功能及参数:

int epoll_ctl(int epfd, int op, int fd, struct epoll_event *event);

功能:控制epoll属性,比如给红黑树添加节点。

参数: 1. epfd: epoll_create函数的返回句柄。

2. op:表示动作类型,有三个宏:

EPOLL_CTL_ADD:注册新的fd到epfd中

EPOLL_CTL_MOD:修改已注册fd的监听事件

EPOLL_CTL_DEL:从epfd中删除一个fd

3. 要操作的文件描述符

4. 结构体信息:

返回值:成功:0, 失败:-1

  1. 特点:

  1. 监听的文件描述符没有了限制。
  2. 异步IO,epoll当有世纪那唤醒之后,发生事件的文件描述符会主动的调用callback回调函数,拿到对应的文件描述符。不需要要轮询,效率高。
  3. epoll不需要构造表,只需要从用户空间拷贝到内核空间一次。

总结:

  • select:适用于小规模应用,简单易用但有文件描述符限制。
  • poll:比 select 更灵活,适用于中等规模应用,但性能瓶颈仍然存在。
  • epoll:最适合大规模、高并发应用,性能优越但在 Linux 上才可用。

代码小练习:每有一个客户端连接创建一个进程进行通信

思路:

  1. 创建套接字:使用 socket() 函数创建一个用于通信的套接字。
  2. 绑定地址:用 bind() 函数将套接字绑定到指定的 IP 地址和端口上。
  3. 监听连接:调用 listen() 函数,使服务器开始监听客户端的连接请求。
  4. 接受连接:使用 accept() 函数接受客户端连接请求,并创建一个新的套接字用于与客户端通信。
  5. 创建子进程:通过 fork() 函数创建一个子进程来处理客户端的通信。
  6. 通信处理:子进程使用 recv() 接收数据,处理完毕后通过 send() 发送响应。
  7. 关闭套接字:通信结束后,子进程关闭与客户端的套接字,父进程关闭监听套接字。

代码实现:

#include <stdio.h>
#include <sys/types.h> 
#include <sys/socket.h>
#include <netinet/in.h>
#include <netinet/ip.h>
#include <unistd.h>
#include <arpa/inet.h>
#include <string.h>
#include <stdlib.h>
#include <signal.h>
#include <sys/wait.h>
// 处理子进程终止信号的处理函数
void handler(int sig)
{
    wait(NULL); // 回收子进程的资源
}
int main(int argc, char const *argv[])
{
    pid_t pid;
    char buf[128] = {0}; // 数据缓冲区
    int ret, acceptfd;
    
    // 1. 创建套接字(socket)-----------------》有手机
    int sockfd = socket(AF_INET, SOCK_STREAM, 0);
    if (sockfd < 0)
    {
        perror("socket err");
        return -1;
    }
    printf("sockfd:%d\n", sockfd); // 打印套接字文件描述符

    // 2. 指定网络信息---------------------------》有号码
    struct sockaddr_in saddr, caddr;
    saddr.sin_family = AF_INET;            // 使用 IPV4 协议
    saddr.sin_port = htons(atoi(argv[1])); // 设置端口号
    // saddr.sin_addr.s_addr = inet_addr("192.168.50.13"); // 虚拟机IP
    // saddr.sin_addr.s_addr = inet_addr("0.0.0.0"); // 绑定到所有接口
    saddr.sin_addr.s_addr = INADDR_ANY; // 绑定到所有网络接口
    int len = sizeof(caddr);
    
    // 3. 绑定套接字(bind)------------------》绑定手机(插卡)
    if (bind(sockfd, (struct sockaddr *)&saddr, sizeof(saddr)) < 0)
    {
        perror("bind err");
        return -1;
    }
    printf("bind ok\n");
    
    // 4. 监听套接字(listen)-----------------》待机
    if (listen(sockfd, 6) < 0)
    {
        perror("listen err");
        return -1;
    }
    printf("listen ok\n");
    
    // 5. 接收客户端连接请求(accept)--------》接电话
    signal(SIGCHLD, handler); // 设置信号处理方式
    
    while (1)
    {
        acceptfd = accept(sockfd, (struct sockaddr *)&caddr, &len);
        if (acceptfd < 0)
        {
            perror("accept err");
            return -1;
        }
        
        // 打印客户端信息
        printf("port:%d ip:%s\n", ntohs(caddr.sin_port), inet_ntoa(caddr.sin_addr));
        printf("acceptfd:%d\n", acceptfd);
        
        pid = fork(); // 创建子进程
        if (pid < 0)
        {
            perror("fork err");
            return -1;
        }
        else if (pid == 0)
        {
            // 6. 接收、发送数据(recv/send)------》通话
            while (1)
            {
                ret = recv(acceptfd, buf, sizeof(buf), 0);
                if (ret < 0)
                {
                    perror("recv err");
                    break;
                }
                else if (ret == 0)
                {
                    printf("client exit\n");
                    break;
                }
                else
                {
                    printf("buf:%s\n", buf); // 打印接收到的数据
                    memset(buf, 0, sizeof(buf)); // 清空缓冲区
                }
            }
            // 7. 关闭套接字(close)--------------》挂电话
            close(acceptfd);
            exit(0); // 结束子进程
        }
        else
        {
            close(acceptfd); // 父进程关闭通信描述符
        }
    }

    close(sockfd); // 关闭监听套接字

    return 0;
}

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值