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服务器

相关文章
相关标签/搜索