epoll的ET和LT模式下,accept,recv,send寫法

版權聲明:本文爲博主原創文章,遵循 CC 4.0 BY-SA 版權協議,轉載請附上原文出處連接和本聲明。
本文連接:https://blog.csdn.net/peng314899581/article/details/78066374
epoll的ET和LT模式觸發場景linux

accept的寫法
while (true)
{
    struct sockaddr_in addr;
    socklen_t addr_len = sizeof(addr);
    evutil_socket_t fd = accept(fd, (struct sockaddr *)&addr, &addr_len);
    if (fd == -1)
    {
        break;
    }
    log(LOG_DEBUG, "New accept ip:%s socket:%d", inet_ntoa(addr.sin_addr), fd);
    evutil_make_socket_nonblocking(fd);
    int optval = 1;
    setsockopt(fd, SOL_SOCKET, TCP_NODELAY, &optval, sizeof(optval));
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
爲何要用while?當epoll的讀事件觸發以後,咱們要判斷這個套接字是否是監聽套接字,由於accept也是一個讀事件。好了,當這裏是一個accept事件時,咱們最好用while包起來。由於在大併發的狀況下,同時向服務器發起多個鏈接是很正常的,那麼若是觸發一次accept事件,你只接收一個鏈接,那麼須要屢次accept事件輪詢觸發你才能夠鏈接完全部,你能夠理解爲讓後面一些鏈接有」延誤「。若是使用while,咱們觸發了accept事件就直接一直不停的accept直到accept返回-1,也就是沒有新鏈接來了,這樣效率更高。
2. recv的寫法編程

struct evbuffer* input = evbuffer_new();//數據緩衝
while (true)
{
    char buffer[1024] = { '\0' };
    int ret = recv(fd, buffer, 1024, 0);
    //從接收緩衝取數據成功
    if (ret > 0)
    {
        evbuffer_add(input, buffer, ret);
        //不足1024,說明取完了
        if (ret < 1024)
        {
            break; 
        }
    }
    //發生錯誤
    if (ret == -1)
    {
        //EAGAIN/EWOULDBLOCK提示你的應用程序如今沒有數據可讀請稍後再試
        //EINTR指操做被中斷喚醒,須要從新讀
        if ((errno == EAGAIN) || (errno == EWOULDBLOCK))
        {
            break;
        }
        else if (errno == EINTR)
        {
            continue;
        }
        //異常斷開狀況
        else
        {
            close(fd);
            break;
        }
    }
    //接收到主動關閉請求
    if (ret == 0)
    {
        close(fd);
        break;
    }
}
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
因爲tcp是流,咱們也不知道接收緩衝有多少個包,長度多少,因此咱們能夠用一個固定長度1024,每次取1k數據,直到把數據取完。邊緣觸發只能使用這種寫法!
當使用水平觸發模式的時候,咱們讀事件觸發,能夠取8k數據就走,不要在這一個讀事件這裏過久,不耽誤後面事件的觸發,反正讀事件會後面一直觸發,下次再取8k繼續就好。可是若是是邊緣模式,那麼讀事件觸發,你必定要一次把數據所有取完,由於它只觸發一次,這就是區別。
3. send的寫法安全

string response;//發送的數據
unsigned int pos = 0;
do 
{
    int ret = send(fd, response.data.c_str()+pos, response.data.length()-pos, 0);
    //發生錯誤
    if (-1 == ret)
    {
        if ((errno == EAGAIN) || (errno == EWOULDBLOCK) || (errno == EINTR))
        {
            ret = 0;
            continue;
        }
        break;
    }
    pos += ret;
} while (pos < response.data.length());
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
關於寫事件,咱們不多狀況下會使用,通常的網絡編程模型,都是主線程一個事件循環用來監聽鏈接,接收鏈接以後,會把鏈接對象丟給IO線程池,這個IO線程池通常線程數量等於cpu核數,這個IO線程池每一個人都有本身的epoll,來監聽鏈接對象的可讀事件,(多線程使用epoll的安全緣由個人另一篇博客有寫,從epoll源碼分析它的使用)IO線程讀完數據,會進行tcp的粘包,半包處理,而後封裝成task,最後丟給一個工做線程池,工做線程池處理task,最後處理完task須要回覆,就給一個專門的發送線程,這個發送線程統一發送數據,因此寫事件用的少。優化方案的發送是這樣的,先用一個單獨的發送線程發送數據,若是發現數據失敗,纔會把這個socket添加到epoll關心一下寫事件,等可寫再發,這樣優化發送線程的發送問題。
————————————————
版權聲明:本文爲CSDN博主「linux_c_coding_man」的原創文章,遵循 CC 4.0 BY-SA 版權協議,轉載請附上原文出處連接及本聲明。
原文連接:https://blog.csdn.net/peng314899581/article/details/78066374服務器

相關文章
相關標籤/搜索