古月不傲 发表于 2021-1-11 16:10

poll小例子

#include <iostream>
#include <sys/types.h>
#include <sys/socket.h>
#include <signal.h>
#include <poll.h>
#include <vector>
#include <netinet/in.h>         // INADDR_ANY
#include <errno.h>
#include <arpa/inet.h>                // inet_ntoa
#include <cstring>
#include <unistd.h>

int main(void)
{
        // 防止服务器关闭
        signal(SIGPIPE, SIG_IGN);
        // 防止产生僵尸进程
        signal(SIGCHLD, SIG_IGN);

        // 创建socket        非阻塞 | 子进程中关闭该套接字
        int listen_fd = socket(AF_INET, SOCK_STREAM | SOCK_NONBLOCK | SOCK_CLOEXEC, 0);
        if (listen_fd == -1)
                return -1;
       
        // 初始化socket信息
        struct sockaddr_in server_addr {};
        server_addr.sin_family = AF_INET;
        server_addr.sin_port = htons(5578);
        server_addr.sin_addr.s_addr = htonl(INADDR_ANY);

        int on = 1;
        int ret;

        // 防止time_wait
        ret = setsockopt(listen_fd, SOL_SOCKET, SO_REUSEADDR, (void *)&on, sizeof(on));
        if (ret == -1)
                return -1;

        // 绑定端口
        ret = bind(listen_fd, (const struct sockaddr*)&server_addr, sizeof(server_addr));
        if (ret == -1)
                return -1;

        // 监听端口
        ret = listen(listen_fd, 1);
        if (ret == -1)
                return -1;

        // poll读事件,先加入监听客户端连接事件
        struct pollfd read_pollfd;
        read_pollfd.fd = listen_fd;
        read_pollfd.events = POLLIN;
        read_pollfd.revents= 0;

        std::vector<pollfd> vec_read_poll;
        vec_read_poll.push_back(read_pollfd);

        // poll 服务器写事件
        struct pollfd write_pollfd {};
        std::vector<pollfd> vec_write_poll;

        struct sockaddr_in client_addr {};
        socklen_t addr_len = sizeof(struct sockaddr);

        int read_ready;
        int write_read_ready;
        int connected_fd;

        while (1)
        {
                read_ready = poll(vec_read_poll.data(), vec_read_poll.size(), -1);       
                // poll失败
                if (read_ready == -1)
                {
                        if (errno == EINTR)
                                continue;
                        if (errno == EMFILE)
                        {
                                printf("文件描述符超出上限\n");
                                return -1;
                        }
                        return -1;
                }
                // 没有客户端连接
                else if (read_ready == 0)
                        continue;
                // 客户端连接过来了
                else if (vec_read_poll.revents & POLLIN)       
                {
                        --read_ready;

                        // 建立三次握手
                        connected_fd = accept4(listen_fd, (sockaddr *)&client_addr, &addr_len, SOCK_NONBLOCK | SOCK_CLOEXEC);

                        if (connected_fd == -1)
                        {
                                if (errno == EINTR)
                                        continue;
                                else if (errno == ECONNABORTED)
                                {
                                        close(connected_fd);
                                        continue;
                                }
                                else
                                        return -1;
                        }       

                        // 注册读事件
                        read_pollfd.fd = connected_fd;
                        read_pollfd.events = POLLIN;
                        read_pollfd.revents = 0;

                        vec_read_poll.push_back(read_pollfd);

                        // 三次握手成功,打印客户端信息
                        std::cout << "client_ip = " << inet_ntoa(client_addr.sin_addr) << std::endl;
                        std::cout << "client_port = " << ntohs(client_addr.sin_port) << std::endl;

                        if (read_ready == 0)
                                continue;
                }
                // read_ready > 0 说明客户端发来信息了 遍历读事件
                for (auto it = vec_read_poll.begin() + 1; it != vec_read_poll.end() && read_ready; it++)
                {
                        // 客户端发来了消息
                        if (it->revents & POLLIN)
                        {
                                --read_ready;

                                char buf {};
                                ret = recv(it->fd, buf, sizeof(buf), 0);
                                if (ret == -1)
                                {
                                        // 接收缓冲区中没有数据
                                        if (errno == EAGAIN || errno == EWOULDBLOCK)
                                                continue;
                                        if (errno == EINTR)
                                                continue;
                                        return -1;
                                }
                                // 客户端正常调用 close 四次挥手
                                if (ret == 0)
                                {
                                        std::cout << "client closed" << std::endl;
                                        it = vec_read_poll.erase(it);
                                        --it;       

                                        close(connected_fd);
                                        continue;
                                }
                                std::cout << buf;

                                ret = send(it->fd, buf, strlen(buf) + 1, 0);

                                if (ret == -1)
                                {
                                        // 如果发送缓冲区满的话,就注册写事件
                                        if (errno == EAGAIN || errno == EWOULDBLOCK)
                                        {
                                                write_read_ready = poll(vec_write_poll.data(), vec_write_poll.size(), -1);       
                                                // poll失败
                                                if (write_read_ready == -1)
                                                {
                                                        if (errno == EINTR)
                                                                continue;
                                                        return -1;
                                                }

                                                write_pollfd.fd = it->fd;
                                                write_pollfd.events = POLLOUT;
                                                write_pollfd.revents = 0;

                                                vec_write_poll.push_back(write_pollfd);
                                                continue;
                                        }
                                        if (errno == EINTR)
                                                continue;
                                        return -1;
                                }

                                // 遍历写事件
                                for (auto it = vec_write_poll.begin(); it != vec_write_poll.end() && write_read_ready; it++)
                                {
                                        if (write_pollfd.revents & POLLOUT)
                                        {
                                                write_read_ready--;

                                                send(it->fd, buf, strlen(buf) + 1, 0);

                                                it = vec_read_poll.erase(it);
                                                --it;               
                                        }
                                }
                        }
                }
        }
        return 0;
}

ayaoko 发表于 2021-1-11 16:24

这个能不能搞到windows上搞阿

pzx521521 发表于 2021-1-11 17:01

ayaoko 发表于 2021-1-11 16:24
这个能不能搞到windows上搞阿

不能 windows 没有poll 有WSAPoll 替换就好了

ayaoko 发表于 2021-1-13 15:50

pzx521521 发表于 2021-1-11 17:01
不能 windows 没有poll 有WSAPoll 替换就好了

大侠这个有没有跨平台,有没有例子给一个,多谢
页: [1]
查看完整版本: poll小例子