1. 程式人生 > >Linux下網路socket程式設計——實現伺服器(select)與多個客戶端通訊

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被耗盡。

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>