I/O复用的 select poll和epoll的简单实现

来源:互联网 发布:淘宝pu女包 编辑:程序博客网 时间:2024/06/02 04:15

http://www.cnblogs.com/wj9012/p/3876734.html

一个tcp的客户端服务器程序

服务器端不变,客户端通过I/O复用轮询键盘输入与socket输入(接收客户端的信息)

服务器端:

复制代码
 1 /*服务器: 2 1.客户端关闭后,服务器再向客户端发送信息,第一次会收到一个RST复位报文,第二次会收到SIGPIPE信号,导致服务器关闭,必须对这个信号进行处理: 3     1.在服务器对read返回值为0的情况进行处理,不向客户端发送信息 4     2.signal函数: signal(SIGPIPE, handle) 或者直接忽略signal(SIGPIPE, SIG_IGN) 5 */ 6 #include <stdio.h> 7 #include <stdlib.h> 8 #include <string.h> 9 #include <unistd.h>10 #include <errno.h>11 #include <sys/types.h>12 #include <sys/stat.h>13 #include <sys/socket.h>14 #include <arpa/inet.h>15 #include <signal.h>16 17 #define ERR_EXIT(m) \18     do { \19         perror(m);\20         exit(EXIT_FAILURE);\21     }while(0)//宏定义的错误处理22 23 static void do_service(int fd)24 {25     char recvbuf[1024] = {0};26     int ret;27     while(1)28     {29         memset(recvbuf, 0, sizeof recvbuf);30         ret = read(&rt, recvbuf, 1024);31         sleep(3);32         if(ret == 0)//关闭客户端时会导致ret == 033             printf("no message\n");34         else if(ret == -1)35         {36             if(errno == EINTR)37                 continue;38             return ;39         }   40         rio_writen(fd, recvbuf, 5);41     }42 }43 44 void handle(int signum)//SIGPIPE信号处理函数45 {46 printf("hello\n");47 }48 49 int main(int argc, const char *argv[])50 {51     if(signal(SIGPIPE, handle) == SIG_ERR)52         ERR_EXIT("signal");    53 54     int listenfd = socket(AF_INET, SOCK_STREAM, 0);//准备一个socketfd55     if(listenfd == -1 )56         ERR_EXIT("listen");57 58     int on = 1;59     if(setsockopt(listenfd, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on)) == -1)//setsockopt设置端口复用60     {61         close(listenfd);62         ERR_EXIT("setsockopt");63     }64 65     struct sockaddr_in seraddr;66     seraddr.sin_family = AF_INET;67     seraddr.sin_port = htons(8888);68     seraddr.sin_addr.s_addr = htonl(INADDR_ANY);69     socklen_t len = sizeof(seraddr);70     if(bind(listenfd, (struct sockaddr*)&seraddr, len) == -1)//监听socket端口,71     {72         close(listenfd);73         ERR_EXIT("bind");74     }75 //上述过程可以封装到起来,不用全部写入main76     if(listen(listenfd, 6) == -1)77     {78         close(listenfd);79         ERR_EXIT("listen");80     }81 82     struct sockaddr_in cliaddr;83     bzero(&cliaddr, sizeof(cliaddr));84     socklen_t cli_len = sizeof cliaddr;85 86     int clientfd;87     clientfd = accept(listenfd, (struct sockaddr*)&cliaddr, &cli_len);88 89     if(clientfd == -1)  90     {91         close(listenfd);92         ERR_EXIT("accept");93     }94     do_service(clientfd);95 96     close(clientfd);97     close(listenfd);98     return 0;99 }
复制代码

 

1.select客户端: 

复制代码
 1 #include <stdio.h> 2 #include <stdlib.h> 3 #include <string.h> 4 #include <errno.h> 5 #include <sys/types.h> 6 #include <sys/socket.h> 7 #include <netinet/in.h> 8 #include <arpa/inet.h> 9 #include <sys/select.h>10 11 #define ERR_EXIT(m) \12     do { \13         perror(m);\14         exit(EXIT_FAILURE);\15     }while(0)16 17 static void do_client(int fd)18 {19     char recvbuf[MAXLINE + 1] = {0};20     char sendbuf[MAXLINE + 1] = {0};21 22     fd_set reade, ready;//将ready集合放入select轮询,每次轮询前将reade赋值给ready23     FD_ZERO(&reade);//清空reade集合24     int fd_stdin = fileno(stdin);25     FD_SET(fd_stdin, &reade);26     FD_SET(fd, &reade);//将需要监听的文件描述符加入集合27     int fd_max = (fd_stdin > fd) ? fd_stdin : fd;//select轮询的最大文件描述符28 29     int ret;30     while(1)31     {32         ready = reade;33         ret = select( fd_max+1, &ready, NULL, NULL, NULL);//轮询,最后一个参数struct *timeval设置为NULL表示即时轮询(每次轮询间没时间间隔)34         if(ret < 0)35         {36             if(errno == EINTR)37                 continue;38             ERR_EXIT("select");39         }else if(ret ==  0)//监听的文件描述符状态未发生变化40         {41             continue;42         }43 44         if(FD_ISSET(fd_stdin, &ready))45         {46             if(fgets(sendbuf, sizeof(sendbuf), stdin) == NULL)47             {48                 shutdown(fd, SHUT_WR);//关闭fd的写端,close关闭时关闭整个描述符,若再写的话会导致write返回-1,系统会向客户端进程发送一个SIGPIPE信号,
导致关闭,可以对信号进行signal(SIGPIPE, SIG_IGN)处理,也可将fd_stdin从监听队列删除
49 // close(fd);50 exit(EXIT_SUCCESS);51  //break;
52 }else53 write(fd, sendbuf, strlen(sendbuf)); 54 }55 56 if(FD_ISSET(fd, &ready))57 {58 int nread = read(fd, recvbuf, MAXLINE);59 if(nread < 0)60 ERR_EXIT("read");61 if(nread == 0)//如果没接收到消息,打印关闭描述符,退出循环62 {63 fprintf(stdout, "fd close\n");64 break;65 }66 fprintf(stdout, "receive:%s\n", recvbuf);//注意要刷新67 }68 memset(recvbuf, 0, sizeof recvbuf);69 memset(sendbuf, 0, sizeof sendbuf);70 }71 }72 73 int main(int argc, const char *argv[])74 {75 int fd = socket(AF_INET, SOCK_STREAM, 0);76 if(fd < 0)77 ERR_EXIT("socket");78 79 struct sockaddr_in cliaddr;80 cliaddr.sin_family = AF_INET;81 cliaddr.sin_port = htons(8888);82 cliaddr.sin_addr.s_addr = inet_addr("127.0.0.1");83 socklen_t len = sizeof cliaddr;84 85 int ret ;86 if((ret = connect(fd, (struct sockaddr*)&cliaddr, len)) == -1)87 {88 close(fd);89 ERR_EXIT("connect");90 }91 do_client(fd);92 close(fd);93 printf("123\n");94 return 0;95 }
复制代码


2.poll客户端:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
static void do_client(int fd)
{
    char recvbuf[MAXLINE + 1] = {0};
    char sendbuf[MAXLINE + 1] = {0};
 
     struct pollfd pfd[2];//struct pollfd数组,里面存放监听描述符相关信息,供poll轮询使用
     pfd[0].fd = fileno(stdin);
     pfd[0].events = POLLIN;
     pfd[1].fd = fd;
     pfd[1].events = POLLIN;
 
     int ret;
    while(1)
    {
        ret = poll( pfd, 2, -1);//轮询,-1和select中的时间结构体指针为NULL的效果一样
        if(ret < 0)
        {
            if(errno == EINTR)
                continue;
            ERR_EXIT("select");
        }else if(ret ==  0)
        {
            continue;
        }
 
        if(pfd[0].revents & POLLIN)
        {
            if(fgets(sendbuf, sizeof(sendbuf), stdin) == NULL)
            {
                shutdown(fd, SHUT_WR);//关闭fd的写端
                pfd[0].fd = -1;
            }else
                write(fd, sendbuf, strlen(sendbuf));          
        }
 
        if(pfd[1].revents & POLLIN)
        {
            int nread = read(fd, recvbuf, MAXLINE);
            if(nread < 0)
                ERR_EXIT("read");
            if(nread == 0)//如果没接收到消息,打印关闭描述符,退出循环
            {
                fprintf(stdout, "fd close\n");
                break;
            }
            fprintf(stdout, "receive:%s\n", recvbuf);//注意要刷新
        }
        memset(recvbuf, 0, sizeof recvbuf);
        memset(sendbuf, 0, sizeof sendbuf);
    }
}

//其余部分和select中一样

3.epoll客户端 

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
static void do_client(int fd)
{
    char recvbuf[MAXLINE + 1] = {0};
    char sendbuf[MAXLINE + 1] = {0};
 
    int epollfd = epoll_create(2);//返回值也是一个文件描述符,记得要关闭,不然会导致fd耗尽
    if(epollfd == -1)
        ERR_EXIT("epoll_create");
 
    struct epoll_event events[2];
    struct epoll_event ev;
 
    int ret;
    ev.data.fd = fd;
    ev.events = EPOLLIN;
    ret = epoll_ctl(epollfd, EPOLL_CTL_ADD, fd, &ev);//注册fd
    if(ret == -1)
        ERR_EXIT("epoll_ctl");
 
    ev.data.fd = fileno(stdin);
    ev.events = EPOLLIN;
    ret = epoll_ctl(epollfd, EPOLL_CTL_ADD, STDIN_FILENO, &ev);//注册fd
    if(ret == -1)
        ERR_EXIT("epoll_ctl");
 
    int nready;
 
    while(1)
    {
        nready = epoll_wait(epollfd, events, 2, -1);
        if(nready < 0)
        {
            if(errno == EINTR)
                continue;
            ERR_EXIT("select");
        }else if(nready  ==  0)
        {
            continue;
        }
 
        int i;
        for(i = 0; i < nready; i++)   
        {
            int nfd = events[i].data.fd;
            if(nfd == STDIN_FILENO)
            {
                if(fgets(sendbuf, 1024, stdin) == NULL)
                {
                    shutdown(fd, SHUT_WR);
                    struct epoll_event ee;
                    ee.data.fd = STDIN_FILENO;
                    if(epoll_ctl(epollfd, EPOLL_CTL_DEL, STDIN_FILENO, &ee) == -1)
                        ERR_EXIT("epoll_ctl");
                }else
                    write(fd, sendbuf, strlen(sendbuf));
            }
            if(nfd == fd)
            {
                int ret = read(fd, recvbuf, 1024);
                if(ret == -1)
                    ERR_EXIT("readline");
                else if(ret == -1)
                {
                    close(fd);
                    printf("fd close\n");
                    exit(EXIT_SUCCESS);
                }
                printf("recv data :%s\n", recvbuf);
            }
        }
    }
    memset(recvbuf, 0, sizeof recvbuf);
    memset(sendbuf, 0, sizeof sendbuf);<br>  }<br>close(epollfd);
}

 


阅读全文
'); })();
0 0
原创粉丝点击
热门IT博客
热门问题 老师的惩罚 人脸识别 我在镇武司摸鱼那些年 重生之率土为王 我在大康的咸鱼生活 盘龙之生命进化 天生仙种 凡人之先天五行 春回大明朝 姑娘不必设防,我是瞎子 拉花怎么拉 barista 咖啡拉花图片 咖啡如何拉花 意式咖啡拉花图案 汽车拉花图片 咖啡拉花图片教程 汽车拉花 彩带拉花的做法图解 拉花的做法 拉花咖啡图片 拉花咖啡的做法 咖啡拉花图 手工拉花的做法 咖啡怎样拉花 咖啡怎么拉花 结婚拉花 咖啡拉花怎么做 拉花图片 咖啡拉花教程 拿铁拉花 咖啡拉花的做法 怎么做拉花 咖啡拉花技巧 怎么做咖啡拉花 剪纸拉花的做法图解 拉花锯 怎么拉花 ktv果盘西瓜皮拉花 新房拉花 汽车拉花定制 装饰拉花 西瓜皮拉花 咖啡奶泡拉花 拉花咖啡培训 果盘西瓜皮拉花图解 结婚拉花怎么挂 拉花装饰 拉花教学 意式咖啡拉花 室内拉花