poll实现TCP连接的IO多路复用

本文介绍了多路复用的概念,包括select、poll和epoll三种技术,重点讲解了poll在实现IO多路复用中的特点和使用方法。通过示例展示了如何使用poll函数完成多个客户端与服务器之间的TCP连接及通信。服务器端代码利用poll进行监听和处理事件,客户端则进行连接和发送/接收数据。
摘要由CSDN通过智能技术生成
    • 基础概念,什么是多路复用

  • 多路:指多个socket网络连接

  • 复用:指的是复用一个线程

  • 多路复用目前有三种技术:select ,poll ,epoll。其中epoll是最新的也是目前最好的多路复用的技术

    • poll实现IO多路复用的特点

  • 相比较于select来说,扩大了对文件描述符的检测的限制,可以由程序员自己设置(极限要根据系统配置)

  • 每次由用户态拷贝至内核态不再重新构造文件描述符表,但是依旧需要不断地拷贝数据,效率很低

    • 本次实现目的

完成多个客户端向服务点的链接以及相互通信(TCP)

    • 具体相关函数

int poll(struct pollfd *fds, nfds_t nfds, int timeout);
参p数:
struct pollfd *fds
关心的文件描述符数组struct pollfd fds[N];
nfds:个数
timeout: 超时检测
毫秒级的:如果填1000,1秒
如果-1,阻塞

struct pollfd {
int fd; /* 检测的文件描述符 */
short events; /* 检测事件 */
short revents; /* 调用poll函数返回填充的事件,poll函数一旦返回,将对应事件自动填充结构体这个成员。只需要判断这个成员的值就可以确定是否产生事件 */
};
事件: POLLIN :读事件
POLLOUT : 写事件
POLLERR:异常事件

代码如下:

服务器端:

#include <stdio.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <netinet/ip.h>
#include <arpa/inet.h>
#include <unistd.h>
#include <stdlib.h>
#include <signal.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <string.h>
 #include <poll.h>

int main(int argc, char const *argv[])
{
    if (argc != 2)
    {
        printf("please input %s <port>\n", argv[0]);
        return -1;
    }
    
    int sockfd, acceptfd;
    sockfd = socket(AF_INET, SOCK_STREAM, 0);
    if (sockfd < 0)
    {
        perror("socket err.");
        return -1;
    }
    printf("socket ok %d\n", sockfd);

    
    struct sockaddr_in serveraddr, caddr;
    serveraddr.sin_family = AF_INET;
    serveraddr.sin_port = htons(atoi(argv[1]));
    //serveraddr.sin_addr.s_addr = inet_addr("0.0.0.0"); //0.0.0.0 自动获取主机ip
    // serveraddr.sin_addr.s_addr = htonl(INADDR_ANY);
    serveraddr.sin_addr.s_addr = INADDR_ANY;

    socklen_t len = sizeof(caddr);

    //&serveraddr -->struct sockaddr_in *

    
    if (bind(sockfd, (struct sockaddr *)&serveraddr, sizeof(serveraddr)) < 0)
    {
        perror("bind err.");
        return -1;
    }
    printf("bind ok.\n");

    
    if (listen(sockfd, 5) < 0)
    {
        perror("listen err.");
        return -1;
    }
    printf("listen ok.\n");

    
     struct pollfd fds[20]={};  //大小自己确定,没有限定个数
    
     fds[0].fd = 0;
     fds[0].events=POLLIN;//读事件

     fds[1].fd=sockfd;
     fds[1].events=POLLIN;


    int n = 2, ret;
    char buf[128];
    int recvbyte;
    
    while (1)
    {
        ret = poll(fds, n, -1);
        if (ret < 0)
        {
            perror("poll err.");
            return -1;
        }
        //处理事件
        for (int i = 0; i < n; i++)
        {
            if (fds[i].revents == POLLIN)
            {
                if (fds[i].fd == 0)
                {
                    fgets(buf, sizeof(buf), stdin);
                    printf("key:%s\n", buf);
                    
                    for(int j=2;j<n;j++)
                    {
                        send(fds[j].fd,buf,sizeof(buf),0);
                    }
                }
                else if (fds[i].fd == sockfd)
                {
              
                    acceptfd = accept(sockfd, (struct sockaddr *)&caddr, &len);

                    if (acceptfd < 0)
                    {
                        perror("accept err.");
                        return -1;
                    }
                    printf("accept ok.\n");

                    
                    printf("ip:%s ,port:%d\n",
                           inet_ntoa(caddr.sin_addr), ntohs(caddr.sin_port));

                    
                    fds[n].fd=acceptfd;
                    fds[n].events=POLLIN;
                    n++;
                }
                else
                {
                    
                    recvbyte = recv(fds[i].fd, buf, sizeof(buf), 0);
                    if (recvbyte < 0)
                    {
                        perror("recv err.");
                        // return -1;
                    }
                    else if (recvbyte == 0)
                    {
                        printf("%d client exit.\n",fds[i].fd);
                        close(fds[i].fd);
                        fds[i]=fds[n-1];
                        n--;
                        i--;              
                        break;
                    }
                    else
                    {
                        printf("%d buf:%s\n",fds[i].fd, buf);
                    }
                }
            }
        }
    }
    close(sockfd);
    return 0;
}

客户端:

#include <stdio.h>
#include <sys/types.h> /* See NOTES */
#include <sys/socket.h>
#include <netinet/in.h>
#include <netinet/ip.h> /* superset of previous */
#include <arpa/inet.h>
#include <unistd.h>
#include <sys/wait.h>


int main(int argc, const char *argv[])
{
    int sockfd, acceptfd;
    sockfd = socket(AF_INET, SOCK_STREAM, 0);
    if (sockfd < 0)
    {
        perror("socket err");
        return -1;
    }
    printf("socket OK!\n");
    struct sockaddr_in addr;
    addr.sin_family = AF_INET;
    addr.sin_port = htons(8888);
    addr.sin_addr.s_addr = inet_addr("192.168.50.247");//根据网络自己设置
    if (connect(sockfd, (struct sockaddr *)&addr, sizeof(addr)) < 0)
    {
        perror("connect err");
        return -1;
    }
    printf("connect OK!\n");
    
    pid_t pid = fork();
    if (pid < 0)
    {
        perror("fork err");
        return -1;
    }
    else if (pid == 0)
    {
        char buf[123]="";
        while (1)
        {
            scanf("%s", buf);
            send(sockfd, buf, 32, 0);
        }
    }
    else
    {
        char buf1[32] = "";
        int recvfd;
        while (1)
        {
            recvfd = recv(sockfd, buf1, 32, 0);
            if (recvfd < 0)
            {
                perror("recv err");
                return -1;
            }
            else
            {
                printf("%s\n", buf1);
            }
        }
        wait(NULL);
    }

    
    return 0;
}

~白嫖完记得点赞关注加评论哈~

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值