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

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 == 0
33             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);//准备一个socketfd
55     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 //上述过程可以封装到起来,不用全部写入main
76     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赋值给ready
23     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 }else 53 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);
}

 


本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/383954.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

TCP相关代码

TCP 基础代码 //tcp_server.c #include<stdio.h> #include<error.h> #include<sys/types.h> #include<string.h> #include<unistd.h> #include<sys/socket.h> #include<netinet/in.h> #include <arpa/inet.h> #include<st…

几种并发服务器模型的实现:多线程,多进程,select,poll,epoll

http://www.cnblogs.com/wj9012/p/3879605.html 客户端使用select模型&#xff1a; 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 …

linux进程通信---几个发送信号的函数(kill,raise,alarm,pause)

http://blog.csdn.net/zzyoucan/article/details/9235685 信号&#xff1a;信号是unix中最古老的进程通信的一种方式&#xff0c;他是软件层次上对中断机制的模拟&#xff0c;是一种异步通信方式&#xff0c;信号可以实现用户空间进程和内核空间进程的交互&#xff0c;内核进程…

Linux epoll模型

http://www.cnblogs.com/venow/archive/2012/11/30/2790031.html 定义&#xff1a; epoll是Linux内核为处理大批句柄而作改进的poll&#xff0c;是Linux下多路复用IO接口select/poll的增强版本&#xff0c;它能显著的减少程序在大量并发连接中只有少量活跃的情况下的系统CPU利…

Linux IO模式及 select、poll、epoll详解

https://segmentfault.com/a/1190000003063859 同步IO和异步IO&#xff0c;阻塞IO和非阻塞IO分别是什么&#xff0c;到底有什么区别&#xff1f;不同的人在不同的上下文下给出的答案是不同的。所以先限定一下本文的上下文。 本文讨论的背景是Linux环境下的network IO。一 概念…

mysql思维导图

后期会不断进行更新

C++第一节课

C数据类型 几个概念 命名空间是C标准库引入的,其中命名空间可以解决变量冲突问题,当出现局部变量和全局变量同名的时候, 局部变量优先被访问.同时命名空间的格式如同一下代码 namespace name1 { int a 0; }namespace name2 { int a 2; } 注意C中的所有组件都是在一个叫做s…

【C/C++】关键字static

http://blog.csdn.net/woxiaohahaa/article/details/51014224 参考自&#xff1a;http://www.cnblogs.com/biyeymyhjob/archive/2012/07/19/2598815.html &#xff08;华山大师兄&#xff09; 这里我们只讨论了C语言的static 首先我们回顾一下各种变量在内存中的位置&#xff1…

IO多路复用之epoll总结

http://www.cnblogs.com/Anker/p/3263780.html 1、基本知识 epoll是在2.6内核中提出的&#xff0c;是之前的select和poll的增强版本。相对于select和poll来说&#xff0c;epoll更加灵活&#xff0c;没有描述符限制。epoll使用一个文件描述符管理多个描述符&#xff0c;将用户关…

3_V1-类和对象 -- 默认成员函数

定义一个日期类 #include <iostream> #include <assert.h> using namespace std;class Date { public:void Display(); private:int _year;int _month;int _day; }; 注意: 在定义一个类的时候往往会将其成员变量定义为私有,成员函数定义为公有.这是为了达到软件…

HDU - 2973威尔逊定理

核心问题就是那个等式 我们观察到等式可以写成(n-1)!-1/n-[(n-1)!/n]的形式&#xff0c;这样就应该联想到威尔逊定理了。 回顾一下威尔逊定理的内容&#xff1a;当且仅当n为素数的时候n|(n-1)!-1&#xff0c;n为合数且大于4的时候n|(n-1)!【参见威尔逊定理的证明】 对于这个…

linux网络编程之posix 线程(四):posix 条件变量与互斥锁 示例生产者--消费者问题

http://blog.csdn.net/jnu_simba/article/details/9129939 一、posix 条件变量 一种线程间同步的情形&#xff1a;线程A需要等某个条件成立才能继续往下执行&#xff0c;现在这个条件不成立&#xff0c;线程A就阻塞等待&#xff0c;而线程B在执行过程中使这个条件成立了&#x…

3-V2-类和对象 -- const内联 静态成员 友元

const修饰成员函数 在成员函数后面加一个const, const修饰this指针指向的对象, 保证调用这个const成员函数的对象在函数内不会被改变 注意:成员函数如果不修改成员变量,则加上const,成员函数如果要修改成员变量,此时就不能给其加上const修饰了 1.const对象不能调用非const…

C语言 二级指针内存模型混合实战

http://www.cnblogs.com/zhanggaofeng/p/5485833.html //二级指针内存模型混合实战 #define _CRT_SECURE_NO_WARNINGS #include <stdio.h> #include <stdlib.h> #include <string.h>//将内存模型①和内存模型②的数据拷贝到内存模型③ char ** threemodel(ch…

Linux 网络编程八(epoll应用--大并发处理)

http://www.cnblogs.com/zhanggaofeng/p/5901316.html //头文件 pub.h #ifndef _vsucess#define _vsucess#ifdef __cplusplus extern "C" {#endif //服务器创建socket int server_socket(int port);//设置非阻塞 int setnonblock(int st);//接收客户端socket int ser…

【数据结构与算法】内部排序之三:堆排序(含完整源码)

转载请注明出处&#xff1a;http://blog.csdn.net/ns_code/article/details/20227303 前言 堆排序、快速排序、归并排序&#xff08;下篇会写这两种排序算法&#xff09;的平均时间复杂度都为O&#xff08;n*logn&#xff09;。要弄清楚堆排序&#xff0c;就要先了解下二叉堆这…

模线性方程(中国剩余定理+扩展中国剩余定理)

已知一系列除数和模数,求最小的满足条件的数 我们先考虑一般的情况&#xff0c;即模数不互质。&#xff08;扩展中国剩余定理&#xff09; 我们考虑两个方程的情况 x%MR xk1∗MRxk1 * MRxk1∗MR x%mr xk2∗mrxk2 * mrxk2∗mr 所以k1∗MRk2∗mrk1 * MRk2 * mrk1∗MRk2∗mr 即…

【数据结构】(面试题)使用两个栈实现一个队列(详细介绍)

http://blog.csdn.net/hanjing_1995/article/details/51539578 使用两个栈实现一个队列 思路一&#xff1a; 我们设定s1是入栈的&#xff0c;s2是出栈的。 入队列&#xff0c;直接压到s1即可 出队列&#xff0c;先把s1中的元素倒入到s2中&#xff0c;弹出s2中的栈顶元素&#x…

C++::探索对象模型

前面我们已经知道, 在没有虚函数的时候, 对象的大小就是对应的成员变量的大小, 而成员函数不会占用对象的空间, 今天我们来讨论一下, 当类中定义了虚函数的时候, 此时对象的大小以及对象模型 非继承下的对象模型 class Base { public:virtual void func1(){cout << &qu…

软件测试相关概念

什么叫软件测试 软件测试就是测试产品没有错误,同时又证明软件是可以正确运行的 测试和调试的区别 调试一般都在开发期间 ,测试是伴随着整个软件的生命周期, 调试是发现程序中问题并且解决问题, 测试是发现程序中的缺陷 软件测试的目的和原则 目的:验证软件有没有问题 原…