poll机制 socket进程间通讯

poll,Linux中的字符设备驱动中有一个函数,Linux 2.5.44版本后被epoll取代。

poll()函数:这个函数是某些Unix系统提供的用于执行与select()函数同等功能的函数,下面是这个函数的声明:
#include <poll.h>
int poll(struct pollfd fds[], nfds_t nfds, int timeout);
此函数在系统调用select内部被使用,作用是把当前的文件指针挂到设备内部定义的等待
队列中。这里的参数table可以不考虑,是在select函数实现过程中的一个内部变量。
函数具体实现时:
wait_queue_head_t t = ((struct mydev *)filp->private_data)->wait_queue;
poll_wait(filp, t, table);
return mask;
这里mask可以是:POLLIN | POLLRDNORM    POLLOUT | POLLWRNORM等等。

poll和select实现功能差不多,但poll效率高,以后要多用poll
poll()接受一个指向结构'struct pollfd'列表的指针,其中包括了你想测试的文件描述符和事件。事件由一个在结构中事件域的比特掩码确定。当前的结构在调用后将被填写并在事件发生后返回。在SVR4(可能更早的一些版本)中的 "poll.h"文件中包含了用于确定事件的一些宏定义。事件的等待时间精确到毫秒 (但令人困惑的是等待时间的类型却是int),当等待时间为0时,poll()函数立即返回,-1则使poll()一直挂起直到一个指定事件发生。下面是pollfd的结构。
struct pollfd {
    int fd; /*文件描述符*/
    short events; /* 等待的需要测试事件 */
    short revents; /* 实际发生了的事件,也就是返回结果 */
};
与select()十分相似,当返回正值时,代表满足响应事件的文件描述符的个数,如果返回0则代表在规定时间内没有事件发生。如发现返回为负则应该立即查看 errno,因为这代表有错误发生。

 

poll函数可用的测试值
常量
说明
POLLIN
普通或优先级带数据可读
POLLRDNORM
普通数据可读
POLLRDBAND
优先级带数据可读
POLLPRI
高优先级数据可读
POLLOUT
普通数据可写
POLLWRNORM
普通数据可写
POLLWRBAND
优先级带数据可写
POLLERR
发生错误
POLLHUP
发生挂起
POLLNVAL
描述字不是一个打开的文件
例如fds[0].events = POLLIN; /*将测试条件设置成普通或优先级带数据可读*/
然后 int pollresult = poll(fds,xx,xx); //这样就可以监听fds里面文件描述符了,当满足特定条件就返回,并将结果保存在revents中。
 
---------------------------------------------------------------
下面举个例子: 使用socket进行进程间通讯
socket有一个fd,通过poll来监控这个fd。监控的线程在闲余时间会做其他的事情sleep()
//client.c
// run$ ./client.o 127.0.0.1
#include<stdio.h>  
#include<stdlib.h>  
#include<string.h>  
#include<errno.h>  
#include<sys/types.h>  
#include<sys/socket.h>  
#include<netinet/in.h>  
  
#define MAXLINE 4096  
  
int main(int argc, char** argv)  
{  
    int    sockfd, n,rec_len;  
    char    recvline[4096], sendline[4096];  
    char    buf[MAXLINE];  
    struct sockaddr_in    servaddr;  
  
  
    if (argc != 2) {  
        printf("usage: ./client <ipaddress>\n");  
        exit(0);  
    }    
  
    if ((sockfd = socket(AF_INET, SOCK_STREAM, 0)) < 0) {  
        printf("create socket error: %s(errno: %d)\n", strerror(errno),errno);  
        exit(0);  
    }  
  
    memset(&servaddr, 0, sizeof(servaddr));  
    servaddr.sin_family = AF_INET;  
    servaddr.sin_port = htons(8000);  
    if (inet_pton(AF_INET, argv[1], &servaddr.sin_addr) <= 0) {  
        printf("inet_pton error for %s\n",argv[1]);  
        exit(0);  
    }  
  
    if (connect(sockfd, (struct sockaddr*)&servaddr, sizeof(servaddr)) < 0) {  
        printf("connect error: %s(errno: %d)\n",strerror(errno),errno);  
        exit(0);  
    }  
  
    printf("send msg to server: \n");  
    fgets(sendline, 4096, stdin);  
    if (send(sockfd, sendline, strlen(sendline), 0) < 0) {  
        printf("send msg error: %s(errno: %d)\n", strerror(errno), errno);  
        exit(0);  
    }

    if ((rec_len = recv(sockfd, buf, MAXLINE,0)) == -1) {  
       perror("recv error");  
       exit(1);  
    }

    buf[rec_len]  = '\0';  
    printf("Received : %s ",buf);  
    close(sockfd);  
    exit(0);  
}  

 

//server.c
#include<stdio.h>  
#include<stdlib.h>  
#include<string.h>  
#include<errno.h>  
#include<sys/types.h>  
#include<sys/socket.h>  
#include<netinet/in.h>  
#include<poll.h>

#define DEFAULT_PORT 8000  
#define MAXLINE 4096  
#define IN_FILES 1 //我们这里poll机制只监听一个文件描述符

void socket_accept(int socket_fd) {
    int connect_fd;  
    char    buff[MAXLINE];  
    //阻塞直到有客户端连接,不然浪费CPU资源,但是显然这里通过poll机制是有可读的信息才会进入本函数,因此应该是不会阻塞的。
    if ((connect_fd = accept(socket_fd, (struct sockaddr*)NULL, NULL)) == -1) { 
        printf("accept socket error: %s(errno: %d)",strerror(errno),errno);
    }

    int n = recv(connect_fd, buff, MAXLINE, 0);
    buff[n] = '\0';
    printf("recv msg from client: %s\n", buff);

    if (send(connect_fd, "Hello,you are connected!\n", 26, 0) == -1) {
        perror("send error");
    }

    close(connect_fd);
}

int main(int argc, char** argv)  
{  
    int    socket_fd, connect_fd;  
    struct sockaddr_in     servaddr;  
    struct pollfd fds[IN_FILES];

    //初始化Socket  
    if ((socket_fd = socket(AF_INET, SOCK_STREAM, 0)) == -1) {  
        printf("create socket error: %s(errno: %d)\n",strerror(errno),errno);  
        exit(0);  
    }

    //初始化  
    memset(&servaddr, 0, sizeof(servaddr));  
    servaddr.sin_family = AF_INET;  
    servaddr.sin_addr.s_addr = htonl(INADDR_ANY);//IP地址设置成INADDR_ANY,让系统自动获取本机的IP地址。  
    servaddr.sin_port = htons(DEFAULT_PORT);//设置的端口为DEFAULT_PORT  
  
    //将本地地址绑定到所创建的套接字上  
    if (bind(socket_fd, (struct sockaddr*)&servaddr, sizeof(servaddr)) == -1) {  
        printf("bind socket error: %s(errno: %d)\n",strerror(errno),errno);  
        exit(0);  
    }  

    //开始监听是否有客户端连接  
    if (listen(socket_fd, 10) == -1) {  
        printf("listen socket error: %s(errno: %d)\n",strerror(errno),errno);  
        exit(0);  
    }  


    fds[0].fd = socket_fd;
    fds[0].events = POLLIN;
    int result;
    while(1){
        printf("begin to poll()\n");
        result = poll(fds, IN_FILES, 3000);  //It may block with timeout
        if (result == 0) {
            printf("Poll timeout\n");
        } else if (result > 0) {
            printf("Poll successfully\n");
        } else {
            printf("Poll error\n");
        } 

        if (fds[0].revents == POLLIN){ 
            printf("===receive a connetction====\n");
            socket_accept(socket_fd);
        }

        printf("do something else\n");
        sleep(1);
        printf("do something else end\n");
    }

    close(socket_fd);  
}

 Server:

~/c/socket$ ./server.o
begin to poll()
Poll timeout
do something else
do something else end
begin to poll()
Poll successfully
===receive a connetction====
recv msg from client: sdfsfsdsdrf455


do something else
do something else end
begin to poll()
Poll timeout
do something else

Client:

:~/c/socket$ ./client.o 127.0.0.1
send msg to server:
sdfsfsdsdrf455
Received : Hello,you are connected!
daichenghui@daichenghui-OptiPlex-3020:~/c/socket$

posted @ 2015-05-08 15:07  牧 天  阅读(2198)  评论(0)    收藏  举报