Linux下网络socket编程——实现服务器(select)与多个客户端通信

Linux下网络socket编程——实现服务器(select)与多个客户端通信

置顶 2017年06月23日 14:44:37 阅读数:3225 标签: socket编程服务器与多个客户端通epoll多路复用C语言网络编程 更多

个人分类: socket编程

一、关于socket通信

通信流程图

服务器端工作流程:

  • 调用 socket() 函数创建套接字 用 bind() 函数将创建的套接字与服务端IP地址绑定
  • 调用listen()函数监听socket() 函数创建的套接字,等待客户端连接 当客户端请求到来之后
  • 调用 accept()函数接受连接请求,返回一个对应于此连接的新的套接字,做好通信准备
  • 调用 write()/read() 函数和 send()/recv()函数进行数据的读写,通过 accept() 返回的套接字和客户端进行通信 关闭socket(close)

客户端工作流程:

  • 调用 socket() 函数创建套接字
  • 调用 connect() 函数连接服务端
  • 调用write()/read() 函数或者 send()/recv() 函数进行数据的读写
  • 关闭socket(close)

二、用select实现服务器端编程:

select函数楼主在之前文章中(select函数用法)已经提及,不在多做缀述。下面贴上服务器端代码servce.c

<span style="color:#000000"><code><span style="color:#009900">#include <stdio.h></span>
<span style="color:#009900">#include <netinet/in.h>   <span style="color:#009900">//for souockaddr_in</span></span>
<span style="color:#009900">#include <sys/types.h>      </span>
<span style="color:#009900">#include <sys/socket.h></span>
<span style="color:#009900">#include <errno.h></span>
<span style="color:#009900">#include <stdlib.h></span>

<span style="color:#009900">#include <arpa/inet.h></span>

<span style="color:#880000">//for select</span>
<span style="color:#009900">#include <sys/time.h></span>
<span style="color:#009900">#include <sys/types.h></span>
<span style="color:#009900">#include <unistd.h></span>
<span style="color:#009900">#include <sys/select.h></span>

<span style="color:#009900">#include <strings.h>   <span style="color:#009900">//for bzero</span></span>
<span style="color:#009900">#include <string.h></span>

<span style="color:#009900">#define BUFF_SIZE 1024</span>
<span style="color:#009900">#define backlog 7</span>
<span style="color:#009900">#define ser_port 11277</span>
<span style="color:#009900">#define CLI_NUM 3</span>


<span style="color:#000088">int</span> client_fds[CLI_NUM];

<span style="color:#000088">int</span> main(<span style="color:#000088">int</span> agrc,<span style="color:#000088">char</span> **argv)
{
    <span style="color:#000088">int</span> ser_souck_fd;
    <span style="color:#000088">int</span> i;   
    <span style="color:#000088">char</span> input_message[BUFF_SIZE];
    <span style="color:#000088">char</span> resv_message[BUFF_SIZE];


    <span style="color:#000088">struct</span> sockaddr_in ser_addr;
    ser_addr.sin_family= AF_INET;    <span style="color:#880000">//IPV4</span>
    ser_addr.sin_port = htons(ser_port); 
    ser_addr.sin_addr.s_addr = INADDR_ANY;  <span style="color:#880000">//指定的是所有地址</span>

    <span style="color:#880000">//creat socket</span>
    <span style="color:#000088">if</span>( (ser_souck_fd = socket(AF_INET,SOCK_STREAM,<span style="color:#006666">0</span>)) < <span style="color:#006666">0</span> )
    {
        perror(<span style="color:#009900">"creat failure"</span>);
        <span style="color:#000088">return</span> -<span style="color:#006666">1</span>;
    } 

    <span style="color:#880000">//bind soucket</span>
    <span style="color:#000088">if</span>(bind(ser_souck_fd, (<span style="color:#000088">const</span> <span style="color:#000088">struct</span> sockaddr *)&ser_addr,<span style="color:#000088">sizeof</span>(ser_addr)) < <span style="color:#006666">0</span>)
    {
        perror(<span style="color:#009900">"bind failure"</span>);
        <span style="color:#000088">return</span> -<span style="color:#006666">1</span>;
    }

    <span style="color:#880000">//listen</span>
    <span style="color:#000088">if</span>(listen(ser_souck_fd, backlog) < <span style="color:#006666">0</span>) 
    {
        perror(<span style="color:#009900">"listen failure"</span>); 
        <span style="color:#000088">return</span> -<span style="color:#006666">1</span>;
    }


    <span style="color:#880000">//fd_set</span>
    fd_set ser_fdset;
    <span style="color:#000088">int</span> max_fd=<span style="color:#006666">1</span>;
    <span style="color:#000088">struct</span> timeval mytime;
    <span style="color:#4f4f4f">printf</span>(<span style="color:#009900">"wait for client connnect!\n"</span>);

    <span style="color:#000088">while</span>(<span style="color:#006666">1</span>)
    {
        mytime.tv_sec=<span style="color:#006666">27</span>;
        mytime.tv_usec=<span style="color:#006666">0</span>;

        FD_ZERO(&ser_fdset);

        <span style="color:#880000">//add standard input</span>
        FD_SET(<span style="color:#006666">0</span>,&ser_fdset);
        <span style="color:#000088">if</span>(max_fd < <span style="color:#006666">0</span>)
        {
            max_fd=<span style="color:#006666">0</span>; 
        }

        <span style="color:#880000">//add serverce</span>
        FD_SET(ser_souck_fd,&ser_fdset);
        <span style="color:#000088">if</span>(max_fd < ser_souck_fd)
        {
            max_fd = ser_souck_fd;
        }

        <span style="color:#880000">//add client </span>
        <span style="color:#000088">for</span>(i=<span style="color:#006666">0</span>;i<CLI_NUM;i++)  <span style="color:#880000">//用数组定义多个客户端fd</span>
        {
            <span style="color:#000088">if</span>(client_fds[i]!=<span style="color:#006666">0</span>) 
            {
                FD_SET(client_fds[i],&ser_fdset);
                <span style="color:#000088">if</span>(max_fd < client_fds[i])
                {
                    max_fd = client_fds[i]; 
                }
            }
        }

        <span style="color:#880000">//select多路复用</span>
        <span style="color:#000088">int</span> ret = select(max_fd + <span style="color:#006666">1</span>, &ser_fdset, NULL, NULL, &mytime);

        <span style="color:#000088">if</span>(ret < <span style="color:#006666">0</span>)    
        {    
            perror(<span style="color:#009900">"select failure\n"</span>);    
            <span style="color:#000088">continue</span>;    
        }    

        <span style="color:#000088">else</span> <span style="color:#000088">if</span>(ret == <span style="color:#006666">0</span>)
        {
            <span style="color:#4f4f4f">printf</span>(<span style="color:#009900">"time out!"</span>);
            <span style="color:#000088">continue</span>;
        }

        <span style="color:#000088">else</span>
        {
            <span style="color:#000088">if</span>(FD_ISSET(<span style="color:#006666">0</span>,&ser_fdset)) <span style="color:#880000">//标准输入是否存在于ser_fdset集合中(也就是说,检测到输入时,做如下事情)</span>
            {
                <span style="color:#4f4f4f">printf</span>(<span style="color:#009900">"send message to"</span>);
                bzero(input_message,BUFF_SIZE);
                fgets(input_message,BUFF_SIZE,stdin);

                <span style="color:#000088">for</span>(i=<span style="color:#006666">0</span>;i<CLI_NUM;i++)
                {
                    <span style="color:#000088">if</span>(client_fds[i] != <span style="color:#006666">0</span>)
                    {
                        <span style="color:#4f4f4f">printf</span>(<span style="color:#009900">"client_fds[%d]=%d\n"</span>, i, client_fds[i]);
                        send(client_fds[i], input_message, BUFF_SIZE, <span style="color:#006666">0</span>);
                    }
                }

            }

            <span style="color:#000088">if</span>(FD_ISSET(ser_souck_fd, &ser_fdset)) 
            {
                <span style="color:#000088">struct</span> sockaddr_in client_address;
                socklen_t address_len;
                <span style="color:#000088">int</span> client_sock_fd = accept(ser_souck_fd,(<span style="color:#000088">struct</span> sockaddr *)&client_address, &address_len);
                <span style="color:#000088">if</span>(client_sock_fd > <span style="color:#006666">0</span>)
                {
                    <span style="color:#000088">int</span> flags=-<span style="color:#006666">1</span>;
                    <span style="color:#880000">//一个客户端到来分配一个fd,CLI_NUM=3,则最多只能有三个客户端,超过4以后跳出for循环,flags重新被赋值为-1</span>
                    <span style="color:#000088">for</span>(i=<span style="color:#006666">0</span>;i<CLI_NUM;i++)
                    {
                        <span style="color:#000088">if</span>(client_fds[i] == <span style="color:#006666">0</span>)
                        {
                            flags=i; 
                            client_fds[i] = client_sock_fd;
                            <span style="color:#000088">break</span>;
                        }
                    }


                    <span style="color:#000088">if</span> (flags >= <span style="color:#006666">0</span>)
                    {
                        <span style="color:#4f4f4f">printf</span>(<span style="color:#009900">"new user client[%d] add sucessfully!\n"</span>,flags);

                    }

                    <span style="color:#000088">else</span> <span style="color:#880000">//flags=-1</span>
                    {   
                        <span style="color:#000088">char</span> full_message[]=<span style="color:#009900">"the client is full!can't join!\n"</span>;
                        bzero(input_message,BUFF_SIZE);
                        <span style="color:#4f4f4f">strncpy</span>(input_message, full_message,<span style="color:#006666">100</span>);
                        send(client_sock_fd, input_message, BUFF_SIZE, <span style="color:#006666">0</span>);

                    }
                }    
            }

        }

        <span style="color:#880000">//deal with the message</span>

        <span style="color:#000088">for</span>(i=<span style="color:#006666">0</span>; i<CLI_NUM; i++)
        {
            <span style="color:#000088">if</span>(client_fds[i] != <span style="color:#006666">0</span>)
            {
                <span style="color:#000088">if</span>(FD_ISSET(client_fds[i],&ser_fdset))
                {
                    bzero(resv_message,BUFF_SIZE);
                    <span style="color:#000088">int</span> byte_num=read(client_fds[i],resv_message,BUFF_SIZE);
                    <span style="color:#000088">if</span>(byte_num > <span style="color:#006666">0</span>)
                    {
                        <span style="color:#4f4f4f">printf</span>(<span style="color:#009900">"message form client[%d]:%s\n"</span>, i, resv_message);
                    }
                    <span style="color:#000088">else</span> <span style="color:#000088">if</span>(byte_num < <span style="color:#006666">0</span>)
                    {
                        <span style="color:#4f4f4f">printf</span>(<span style="color:#009900">"rescessed error!"</span>);
                    }

                    <span style="color:#880000">//某个客户端退出</span>
                    <span style="color:#000088">else</span>  <span style="color:#880000">//cancel fdset and set fd=0</span>
                    {
                        <span style="color:#4f4f4f">printf</span>(<span style="color:#009900">"clien[%d] exit!\n"</span>,i);
                        FD_CLR(client_fds[i], &ser_fdset);
                        client_fds[i] = <span style="color:#006666">0</span>;
                       <span style="color:#880000">// printf("clien[%d] exit!\n",i);</span>
                        <span style="color:#000088">continue</span>;  <span style="color:#880000">//这里如果用break的话一个客户端退出会造成服务器也退出。  </span>
                    }
                }
            }
        }    
    }
    <span style="color:#000088">return</span> <span style="color:#006666">0</span>;
}
</code></span>
  • 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
  • 75
  • 76
  • 77
  • 78
  • 79
  • 80
  • 81
  • 82
  • 83
  • 84
  • 85
  • 86
  • 87
  • 88
  • 89
  • 90
  • 91
  • 92
  • 93
  • 94
  • 95
  • 96
  • 97
  • 98
  • 99
  • 100
  • 101
  • 102
  • 103
  • 104
  • 105
  • 106
  • 107
  • 108
  • 109
  • 110
  • 111
  • 112
  • 113
  • 114
  • 115
  • 116
  • 117
  • 118
  • 119
  • 120
  • 121
  • 122
  • 123
  • 124
  • 125
  • 126
  • 127
  • 128
  • 129
  • 130
  • 131
  • 132
  • 133
  • 134
  • 135
  • 136
  • 137
  • 138
  • 139
  • 140
  • 141
  • 142
  • 143
  • 144
  • 145
  • 146
  • 147
  • 148
  • 149
  • 150
  • 151
  • 152
  • 153
  • 154
  • 155
  • 156
  • 157
  • 158
  • 159
  • 160
  • 161
  • 162
  • 163
  • 164
  • 165
  • 166
  • 167
  • 168
  • 169
  • 170
  • 171
  • 172
  • 173
  • 174
  • 175
  • 176
  • 177
  • 178
  • 179
  • 180
  • 181
  • 182
  • 183
  • 184
  • 185
  • 186
  • 187
  • 188
  • 189
  • 190
  • 191
  • 192
  • 193
  • 194
  • 195
  • 196
  • 197
  • 198
  • 199
  • 200
  • 201
  • 202
  • 203
  • 204
  • 205
  • 206
  • 207
  • 208
  • 209

select实现多路复用,多路复用,顾名思义,就是说各做各的事,标准输入事件到来,有相关函数处理。服务器处理服务器的事件,客户端到来时有相关函数对其进行处理,通过select遍历各fd的读写情况,就不用担心阻塞了。


三、用epoll实现客户端编程:

1、客户端程序(epoll_client.c):

<span style="color:#000000"><code><span style="color:#009900">#include<stdio.h>    </span>
<span style="color:#009900">#include<stdlib.h>    </span>
<span style="color:#009900">#include<netinet/in.h>    </span>
<span style="color:#009900">#include<sys/socket.h>    </span>
<span style="color:#009900">#include<arpa/inet.h>    </span>
<span style="color:#009900">#include<string.h>    </span>
<span style="color:#009900">#include<unistd.h>    </span>

<span style="color:#009900">#include <sys/epoll.h></span>
<span style="color:#009900">#include <errno.h></span>
<span style="color:#009900">#include <fcntl.h></span>

<span style="color:#009900">#define BUFFER_SIZE 1024    </span>

<span style="color:#000088">int</span> main(<span style="color:#000088">int</span> argc, <span style="color:#000088">const</span> <span style="color:#000088">char</span> * argv[])    
{   
    <span style="color:#000088">int</span> i,n;
    <span style="color:#000088">int</span> connfd,sockfd;
    <span style="color:#000088">struct</span> epoll_event ev,events[<span style="color:#006666">20</span>]; <span style="color:#880000">//ev用于注册事件,数组用于回传要处理的事件</span>
    <span style="color:#000088">int</span> epfd=epoll_create(<span style="color:#006666">256</span>);<span style="color:#880000">//创建一个epoll的句柄,其中256为你epoll所支持的最大句柄数</span>

    <span style="color:#000088">struct</span> sockaddr_in client_addr;
    <span style="color:#000088">struct</span> sockaddr_in server_addr;    

    server_addr.sin_family = AF_INET;    
    server_addr.sin_port = htons(<span style="color:#006666">11277</span>);    
    server_addr.sin_addr.s_addr =INADDR_ANY;    
    bzero(&(server_addr.sin_zero), <span style="color:#006666">8</span>);    

    <span style="color:#000088">int</span> server_sock_fd = socket(AF_INET, SOCK_STREAM, <span style="color:#006666">0</span>);  

    ev.data.fd=server_sock_fd;<span style="color:#880000">//设置与要处理的事件相关的文件描述符</span>
    ev.events=EPOLLIN|EPOLLET;<span style="color:#880000">//设置要处理的事件类型</span>
    epoll_ctl(epfd,EPOLL_CTL_ADD,server_sock_fd,&ev);<span style="color:#880000">//注册epoll事件</span>

    <span style="color:#000088">if</span>(server_sock_fd == -<span style="color:#006666">1</span>)    
    {    
        perror(<span style="color:#009900">"socket error"</span>);    
        <span style="color:#000088">return</span> <span style="color:#006666">1</span>;    
    }    

    <span style="color:#000088">char</span> recv_msg[BUFFER_SIZE];    
    <span style="color:#000088">char</span> input_msg[BUFFER_SIZE];    

    <span style="color:#000088">if</span>(connect(server_sock_fd, (<span style="color:#000088">struct</span> sockaddr *)&server_addr, <span style="color:#000088">sizeof</span>(<span style="color:#000088">struct</span> sockaddr_in)) == <span style="color:#006666">0</span>)    
    {    
        <span style="color:#000088">for</span>(;;)
        {
            <span style="color:#000088">int</span> nfds=epoll_wait(epfd,events,<span style="color:#006666">20</span>,<span style="color:#006666">500</span>);<span style="color:#880000">//等待epoll事件的发生</span>
            <span style="color:#000088">for</span>(i=<span style="color:#006666">0</span>;i<nfds;++i)
            {    
                <span style="color:#000088">if</span>(events[i].events&EPOLLOUT) <span style="color:#880000">//有数据发送,写socket</span>
                {
                    bzero(input_msg, BUFFER_SIZE);    
                    fgets(input_msg, BUFFER_SIZE, stdin);    

                    sockfd = events[i].data.fd;
                    write(sockfd, recv_msg, n);

                    ev.data.fd=sockfd;
                    ev.events=EPOLLIN|EPOLLET;
                    epoll_ctl(epfd,EPOLL_CTL_ADD,sockfd,&ev);
                }   

                <span style="color:#000088">else</span> <span style="color:#000088">if</span>(events[i].events&EPOLLIN)<span style="color:#880000">//有数据到来,读socket</span>
                {
                    bzero(recv_msg, BUFFER_SIZE);
                    <span style="color:#000088">if</span>((n = read(server_sock_fd, recv_msg, BUFFER_SIZE)) <<span style="color:#006666">0</span> )
                    {
                        <span style="color:#4f4f4f">printf</span>(<span style="color:#009900">"read error!"</span>);
                    }

                    ev.data.fd=server_sock_fd;
                    ev.events=EPOLLOUT|EPOLLET;
                    <span style="color:#4f4f4f">printf</span>(<span style="color:#009900">"%s\n"</span>,recv_msg);
                }

            }        
        }
    }    
    <span style="color:#000088">return</span> <span style="color:#006666">0</span>;    
}   </code></span>
  • 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
  • 75
  • 76
  • 77
  • 78
  • 79
  • 80
  • 81
  • 82

2、关于epoll函数:

相比于select,epoll最大的好处在于它不会随着监听fd数目的增长而降低效率。因为在内核中的select实现中,它是采用轮询来处理的,轮询的fd数目越多,自然耗时越多。并且,在linux/posix_types.h头文件有这样的声明: 
#define __FD_SETSIZE 1024 
表示select最多同时监听1024个fd

一共三个函数:

1、 int epoll_create (int size); 
创建一个epoll的句柄

*size用来告诉内核这个监听的数目一共有多大。这个参数不同于select()中的第一个参数,给出最大监听的fd+1的值。需要注意的是,当创建好epoll句柄后,它就是会占用一个fd值,在linux下如果查看/proc/进程id/fd/,是能够看到这个fd的,所以在使用完epoll后,必须调用close()关闭,否则可能导致fd被耗尽。

扫描二维码关注公众号,回复: 4074714 查看本文章

2、 int epoll_ctl (int epfd , int op, int fd, struct epoll_event *event);

<span style="color:#000000"><code>     epoll的事件注册函数,它不同与select()是在监听事件时告诉内核要监听什么类型的事件,而是在这里先注册要监听的事件类型。第一个参数是epoll_create()的返回值,第二个参数表示动作,用三个宏来表示:
</code></span>
  • 1
  • 2
  • EPOLL_CTL_ADD:注册新的fd到epfd中;
  • EPOLL_CTL_MOD:修改已经注册的fd的监听事件;
  • EPOLL_CTL_DEL:从epfd中删除一个fd;

第三个参数是需要监听的fd

第四个参数是告诉内核需要监听什么事,struct epoll_event结构如下:

<span style="color:#000000"><code><span style="color:#000088">struct</span> epoll_event {
    __uint32_t events;    <span style="color:#880000">/* Epoll events */</span>
    epoll_data_t data;     <span style="color:#880000">/* User data variable */</span>
};</code></span>
  • 1
  • 2
  • 3
  • 4
<span style="color:#000000"><code><span style="color:#000088">typedef</span> <span style="color:#000088">union</span> epoll_data {
    <span style="color:#000088">void</span> *ptr;
    <span style="color:#000088">int</span> fd;
    __uint32_t u32;
    __uint64_t u64;
} epoll_data_t;</code></span>
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6

events可以是以下几个宏的集合:

  • EPOLLIN :表示对应的文件描述符可以读(包括对端SOCKET正常关闭);
  • EPOLLOUT:表示对应的文件描述符可以写;
  • EPOLLPRI:表示对应的文件描述符有紧急的数据可读(这里应该表示有带外数据到来);
  • EPOLLERR:表示对应的文件描述符发生错误;
  • EPOLLHUP:表示对应的文件描述符被挂断;
  • EPOLLET: 将EPOLL设为边缘触发(Edge Triggered)模式,这是相对于水平触发(Level Triggered)来说的。
  • EPOLLONESHOT:只监听一次事件,当监听完这次事件之后,如果还需要继续监听这个socket的话,需要再次把这个socket加入到EPOLL队列里

3、 int epoll_wait(int epfd, struct epoll_event * events, int maxevents, int timeout);

  • 等待事件的产生,类似于select()调用。
  • 参数events用来从内核得到事件的集合,
  • maxevents告之内核这个events有多大,这个 maxevents的值不能大于创建epoll_create()时的size,
  • 参数timeout是超时时间(毫秒,0会立即返回,-1将不确定,也有说法说是永久阻塞)。该函数返回需要处理的事件数目,如返回0表示已超时。

使用步骤:

<span style="color:#000000"><code> <1>首先通过create_epoll(int maxfds)来创建一个epoll的句柄,其中maxfds为你epoll所支持的最大句柄数。这个函数会返回一个新的epoll句柄,之后的所有操作将通过这个句柄来进行操作。在用完之后,记得用close()来关闭这个创建出来的epoll句柄。 

 <2>然后每一帧的调用epoll_wait (int epfd, epoll_event events, int  max  events,  int  timeout) 来查询所有的网络接口。

 <3>kdpfd为用epoll_create创建之后的句柄,events是一个epoll_event*的指针,当epoll_wait这个函数操作成功之后,epoll_events里面将储存所有的读写事件。max_events是当前需要监听的所有socket句柄数。最后一个timeout是 epoll_wait的超时,为0的时候表示马上返回,为-1的时候表示一直等下去,直到有事件范围,为任意正整数的时候表示等这么长的时间,如果一直没有事件,则返回。一般如果网络主循环是单独的线程的话,可以用-1来等,这样可以保证一些效率,如果是和主逻辑在同一个线程的话,则可以用0来保证主循环的效率。
  epoll_wait返回之后应该是一个循环,遍历所有的事件。
</code></span>
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7

基本上都是如下的框架:

<span style="color:#000000"><code><span style="color:#000088">for</span>( ; ; )
    {
        nfds = epoll_wait(epfd,events,<span style="color:#006666">20</span>,<span style="color:#006666">500</span>);
        <span style="color:#000088">for</span>(i=<span style="color:#006666">0</span>;i<nfds;++i)
        {
            <span style="color:#000088">if</span>(events[i].data.fd==listenfd) <span style="color:#880000">//有新的连接</span>
            {
                     connfd = accept(listenfd,(sockaddr *)&clientaddr, &clilen);    <span style="color:#880000">//accept这个连接</span>
                     ev.data.fd=connfd;
                     ev.events=EPOLLIN|EPOLLET;
                     epoll_ctl(epfd,EPOLL_CTL_ADD,connfd,&ev); <span style="color:#880000">//将新的fd添加到epoll的监听队列中</span>
            }

           <span style="color:#000088">else</span> <span style="color:#000088">if</span>( events[i].events&EPOLLIN ) <span style="color:#880000">//接收到数据,读socket</span>
            {
                     n = read(sockfd, line, MAXLINE)) < <span style="color:#006666">0</span>    <span style="color:#880000">//读</span>
                     ev.data.ptr = md;     <span style="color:#880000">//md为自定义类型,添加数据</span>
                     ev.events=EPOLLOUT|EPOLLET;
                     epoll_ctl(epfd,EPOLL_CTL_MOD,sockfd,&ev);<span style="color:#880000">//修改标识符,等待下一个循环时发送数据,异步处理的精髓</span>
            }
            <span style="color:#000088">else</span> <span style="color:#000088">if</span>(events[i].events&EPOLLOUT) <span style="color:#880000">//有数据待发送,写socket</span>
            {
                     <span style="color:#000088">struct</span> myepoll_data* md = (myepoll_data*)events[i].data.ptr;    <span style="color:#880000">//取数据</span>
                     sockfd = md->fd;
                     send( sockfd, md->ptr, <span style="color:#4f4f4f">strlen</span>((<span style="color:#000088">char</span>*)md->ptr), <span style="color:#006666">0</span> );        <span style="color:#880000">//发送数据</span>
                     ev.data.fd=sockfd;
                     ev.events=EPOLLIN|EPOLLET;
                     epoll_ctl(epfd,EPOLL_CTL_MOD,sockfd,&ev); <span style="color:#880000">//修改标识符,等待下一个循环时接收数据</span>
            }
            <span style="color:#000088">else</span>
            {
                     <span style="color:#880000">//其他的处理</span>
            }
        }
    }</code></span>

猜你喜欢

转载自blog.csdn.net/xswy1/article/details/82629448