zoukankan      html  css  js  c++  java
  • 一个poll的简单例子

    该程序使用poll事件机制实现了一个简单的消息回显的功能,其服务器端和客户端的代码如下所示:
    服务器端:

    //start from the very beginning,and to create greatness
    //@author: Chuangwei Lin
    //@E-mail:979951191@qq.com
    //@brief: 一个poll的简单例子,服务端
    #include <unistd.h>
    #include <sys/types.h>
    #include <fcntl.h>
    #include <sys/socket.h>
    #include <netinet/in.h>
    #include <arpa/inet.h>
    #include <signal.h>
    #include <sys/wait.h>
    #include <poll.h>
    
    #include <stdlib.h>
    #include <stdio.h>
    #include <errno.h>
    #include <string.h>
    
    #include <vector>
    #include <iostream>
    //错误输出宏
    #define ERR_EXIT(m) 
            do 
            { 
                    perror(m); 
                    exit(EXIT_FAILURE); 
            } while(0)
    
    //pollfd结构体
    //struct pollfd{
    //       int fd;//文件描述符
    //       short events;//所请求的事件
     //      short revents;//实际返回的事件
    //   }
    //动态数组
    typedef std::vector<struct pollfd> PollFdList;
    
    int main(void)
    {
        signal(SIGPIPE, SIG_IGN);//TIME_WAIT状态 忽略pipe信号,避免僵尸进程
        signal(SIGCHLD, SIG_IGN);//
    
        //int idlefd = open("/dev/null", O_RDONLY | O_CLOEXEC);
        int listenfd;//创建套接字
    
        //if ((listenfd = socket(PF_INET, SOCK_STREAM, IPPROTO_TCP)) < 0)
        //非阻塞套接字SOCK_NONBLOCK
        if ((listenfd = socket(PF_INET, SOCK_STREAM | SOCK_NONBLOCK | SOCK_CLOEXEC, IPPROTO_TCP)) < 0)
            {//创建socket套接字
                ERR_EXIT("socket");
            }
        //填充地址
        struct sockaddr_in servaddr;
        memset(&servaddr, 0, sizeof(servaddr));//先清零
        servaddr.sin_family = AF_INET;//协议族
        servaddr.sin_port = htons(5188);//端口号
        servaddr.sin_addr.s_addr = htonl(INADDR_ANY);//本地的IP
    
        int on = 1;
    //int setsockopt(int sockfd, int level, int optname,const void *optval, socklen_t optlen);
    //sockfd:标识一个套接口的描述字。
    //level:选项定义的层次;支持SOL_SOCKET、IPPROTO_TCP、IPPROTO_IP和IPPROTO_IPV6。
    //optname:需设置的选项。
    //optval:指针,指向存放选项待设置的新值的缓冲区。
    //optlen:optval缓冲区长度。
        if (setsockopt(listenfd, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on)) < 0)
            {//设置地址的重新利用
                ERR_EXIT("setsockopt");
            }
        if (bind(listenfd, (struct sockaddr*)&servaddr, sizeof(servaddr)) < 0)
            {//绑定地址和端口
                ERR_EXIT("bind");
            }
        if (listen(listenfd, SOMAXCONN) < 0)
            {//监听端口
                ERR_EXIT("listen");
            }
        //poll
        struct pollfd pfd;
        pfd.fd = listenfd;
        pfd.events = POLLIN;//关注pollin事件,表明有事件可读
    
        PollFdList pollfds;//创建一个动态数组(向量)
        pollfds.push_back(pfd);//把文件描述符添加到数组里面
    
        int nready;
    
        struct sockaddr_in peeraddr;
        socklen_t peerlen;
        int connfd;
    
        while (1)
        {   //动态数组首地址pollfds.data()C++11
            //参数:结构体指针,所监听文件描述符的个数,超时时间
            nready = poll(&*pollfds.begin(), pollfds.size(), -1);//负数表示无限等待,直到发生事件才返回
            if (nready == -1)
            {//出错
                if (errno == EINTR)
                    continue;
    
                ERR_EXIT("poll");
            }
            if (nready == 0)    //无事件发生
                continue;
    
            if (pollfds[0].revents & POLLIN)//监听的pollin事件到来
            {
                peerlen = sizeof(peeraddr);
                //accept4为一个新的函数,可以添加选项,非阻塞
                //接受监听
                connfd = accept4(listenfd, (struct sockaddr*)&peeraddr,&peerlen, SOCK_NONBLOCK | SOCK_CLOEXEC);
    
                if (connfd == -1)
                    ERR_EXIT("accept4");
                //把时间加入监听的事件中
                pfd.fd = connfd;
                pfd.events = POLLIN;
                pfd.revents = 0;//目前还没有任何事件返回,置为零
                pollfds.push_back(pfd);
                --nready;
                // 连接成功,打印IP和端口信息
                std::cout<<"ip="<<inet_ntoa(peeraddr.sin_addr)<<" port="<<ntohs(peeraddr.sin_port)<<std::endl;
                if (nready == 0)//事件都处理完了
                    continue;
            }
            //遍历查看哪些已连接套接字产生事件(迭代器)
            for (PollFdList::iterator it=pollfds.begin()+1;it != pollfds.end() && nready >0; ++it)
            {
                    if (it->revents & POLLIN)//如果是pollin事件
                    {
                        --nready;
                        connfd = it->fd;
                        char buf[1024] = {0};
                        int ret = read(connfd, buf, 1024);//读取消息内容
                        if (ret == -1)//出错
                            ERR_EXIT("read");
                        if (ret == 0)//
                        {
                            std::cout<<"client close"<<std::endl;
                            it = pollfds.erase(it);//这里就会自动定位了
                            --it;//循环有++,所以这里要先--
                            close(connfd);
                            continue;
                        }
                        std::cout<<buf;//打印收到的消息内容
                        write(connfd, buf, strlen(buf));                    
                    }
            }
        }
        return 0;
    }
    
    

    客户端:

    //start from the very beginning,and to create greatness
    //@author: Chuangwei Lin
    //@E-mail:979951191@qq.com
    //@brief: 一个poll的简单例子,客户端
    #include <unistd.h>
    #include <sys/types.h>
    #include <sys/socket.h>
    #include <netinet/in.h>
    #include <arpa/inet.h>
    
    #include <stdlib.h>
    #include <stdio.h>
    #include <errno.h>
    #include <string.h>
    
    #include <iostream>
    
    #define ERR_EXIT(m) 
            do 
            { 
                    perror(m); 
                    exit(EXIT_FAILURE); 
            } while(0)
    
    int main(void)
    {
        int sock;
        //创建套接字
        if ((sock = socket(PF_INET, SOCK_STREAM, IPPROTO_TCP)) < 0)
            ERR_EXIT("socket");
        //填充服务器地址和端口
        struct sockaddr_in servaddr;
        memset(&servaddr, 0, sizeof(servaddr));
        servaddr.sin_family = AF_INET;
        servaddr.sin_port = htons(5188);
        servaddr.sin_addr.s_addr = inet_addr("这个填写你的IP");
        //链接到服务器
        if (connect(sock, (struct sockaddr*)&servaddr, sizeof(servaddr)) < 0)
            ERR_EXIT("connect");
    
        struct sockaddr_in localaddr;
        socklen_t addrlen = sizeof(localaddr);
        if (getsockname(sock, (struct sockaddr*)&localaddr, &addrlen) < 0)
            ERR_EXIT("getsockname");
        //输出IP和端口信息
        std::cout<<"ip="<<inet_ntoa(localaddr.sin_addr)<<" port="<<ntohs(localaddr.sin_port)<<std::endl;
    
        char sendbuf[1024] = {0};
        char recvbuf[1024] ={0};
        while (fgets(sendbuf, sizeof(sendbuf), stdin) != NULL)
        {//发送和接受
            write(sock, sendbuf, strlen(sendbuf));
            read(sock, recvbuf, sizeof(recvbuf));
    
            fputs(recvbuf, stdout);
            //清空缓冲区
            memset(sendbuf, 0, sizeof(sendbuf));
            memset(recvbuf, 0, sizeof(recvbuf));
        }
    
        close(sock);
    
        return 0;
    }
    

    开启服务器,之后运行客户端,结果如下:
    服务器:
    这里写图片描述
    客户端:
    这里写图片描述

  • 相关阅读:
    redis 设置分布式锁要避免死锁
    jmeter的简单http接口用法
    整理一些好的网站或者好的文章来慢慢学
    多线程的共享变量的内存不可见性如何理解
    谷歌浏览器可以google了
    org.apache.commons.dbcp.DelegatingPreparedStatement.isClosed()Z和NewProxyPreparedStatement.isClosed()
    php封装curl,模拟POST和GET请求HTTPS请求
    PHP 轻量级 REST框架
    使用 spring封装的javamail linux服务器发送邮件失败解决
    安装Ruby、Sass在WebStrom配置Scss编译环境css自动压缩
  • 原文地址:https://www.cnblogs.com/sigma0-/p/12630495.html
Copyright © 2011-2022 走看看