网络总结(一)

来源:互联网 发布:孙尚香 知乎 编辑:程序博客网 时间:2024/06/07 06:28

Nagle算法

                      Nagle算法用于对缓冲区内一定数量的消息进行自动连接,该处理过程称为Nagling,通过减少必须发送的封包的数量,提高网络程序的效率,但会引入一定的延迟。

                      默认情况下,发送数据采用Nagle算法。指发送方发送的数据不会立即发出,先放在缓冲区,等缓冲区满了以后再发出。发送完一批数据后,会等待接收方对这批数据的回应,然后再发送下一批数据。适用于发送方需要发送大批数据,并且对方接受会及时作出回应的场合。这种算法通过减少传输数据的次数来提高通信效率。如果发送方持续发送小批量的数据,并且接收方不一定会立即发送响应数据,Nagle算法会是发送方运行很慢。

                     TCP_NODELAY禁用Nagle算法

           void set_nodelay(int fd, bool enabled)           {          int flag = enabled;          setsockopt(fd, IPPROTO_TCP, TCP_NODELAY, (char*)&flag, sizeof(int));           }

非阻塞I/O

                      系统分为低速系统调用和其他系统调用两类。低速系统调用是可能会使进程永远阻塞的一类系统调用,包括:
                      1. 如果某些文件类型(如管道,终端设备,网络设备)的数据不存在,则读操作可能会使调用者永远阻塞
                      2. 如果数据不能被上述同样类型的文件接受,则写操作也会使调用者永远阻塞
                      3. 在某种条件之前,打开某些类型文件会被阻塞
                      4. 对已经加上强制性记录锁的文件进行读写
                      5. 某些ioctl操作
                      6. 某些进程间通信
                      非阻塞I/O使调用open,write,read时不会一直阻塞
                     指定非阻塞I/O的方法:   1)如果open获得描述符,可指定O_NONBLOCK标志
                                                               2)对于已经打开的文件描述符,调用fcntl,由该函数打开O_NONBLOCK标志

           void set_nonblocking(int fd)           {          int old_flags = fcntl(fd, F_GETFL, 0);          fcntl(fd, F_SETFL, old_flags | O_NONBLOCK);           }
                     对于非阻塞描述符的操作不能无阻塞的完成,errno会被设置成EAGAIN/EWOULDBLOCK

close-on-exec

                      对文件描述符设置FD_CLOEXEC,使用exec系列执行的程序里,此文件描述符被关闭,不能在使用,但在使用fork产生的进程中,此文件描述符并不关闭,可以继续使用。                

           void set_cloexec(int fd)          {         int old_flags = fcntl(fd, F_GETFL, 0);         fcntl(fd, F_SETFL, old_flags | FD_CLOEXEC);           }

epoll

                     epoll是linux内核为处理大批量句柄而改进了poll。  

                     优点:1)epoll没有进程打开的文件描述符的限制,上限是可以打开文件的数目。(select有文件描述符的限制)
                                 2)epoll只会管“活跃”的连接,对活跃的连接进行处理。因为在内核实现中epoll是根据每个fd上面的callback函数实现的。(select、poll都会轮询完所有的fd,才进行处理)
                                 3)内存拷贝方式较快,使用mmap加速内核和用户空间的消息传递

                     epoll的两种工作方式:
                                  LT(level triggered)是缺省工作方式,并且同时支持block和no-block socket。内核告诉你一个文件描述符是否就绪了,你就可以对就绪的fd进行IO操作。如果不进行任何操作,内核还是会继续通知你。传统的select、poll是这种模型的代表。
                                  ET(edge-triggered)是高速工作方式,只支持no-block socket。在这种模式下。当描述符从未就绪变为就绪时,内核就通过epoll告诉你。然后假设你知道文件描述符已经就绪,并且不会再为那个文件描述符发送更多的就绪通知,直到你做了某些操作导致文件描述符不在为就绪状态。只会通知一次。
                     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);
                                  epoll的事件注册函数,它不同与select()是在监听事件时告诉内核要监听什么类型的事件,而是在这里先注册要监听的事件类型。第一个参数是epoll_create()的返回值,第二个参数表示动作,用三个宏来表示:
                                  EPOLL_CTL_ADD:注册新的fd到epfd中;
                                  EPOLL_CTL_MOD:修改已经注册的fd的监听事件;
                                  EPOLL_CTL_DEL:从epfd中删除一个fd;
                                  第三个参数是需要监听的fd,第四个参数是告诉内核需要监听什么事,struct epoll_event结构如下:

                 struct epoll_event {                        __uint32_t events;  /* Epoll events */                        epoll_data_t data;  /* User data variable */                 };                 typedef union epoll_data {                         void *ptr;                         int fd;                         __uint32_t u32;                         __uint64_t u64;                 } epoll_data_t;
                                events可以是以下几个宏的集合:
                                        EPOLLIN :表示对应的文件描述符可以读(包括对端SOCKET正常关闭);
                                        EPOLLOUT:表示对应的文件描述符可以写;
                                        EPOLLPRI:表示对应的文件描述符有紧急的数据可读(这里应该表示有带外数据到来);
                                        EPOLLERR:表示对应的文件描述符发生错误;
                                        EPOLLHUP:表示对应的文件描述符被挂断;
                                        EPOLLET: 将EPOLL设为边缘触发(Edge 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表示已超时。

常见错误

                    EWOULDBLOCK       用于非阻塞模式。不需要重新读或写
                    EAGAIN                       不用管,继续处理
                    EINTR                          系统中断,继续处理
                    ECONNABORTED      该错误被描述为“software caused connection abort”,即“软件引起的连接中止”。原因在于当服务和客户进程在完成用于 TCP 连接的“三次握手”后,客户 TCP 却发送了一个 RST (复位)分节,在服务进程看来,就在该连接已由 TCP 排队,等着服务进程调用 accept 的时候 RST 却到达了。POSIX 规定此时的 errno 值必须 ECONNABORTED。源自 Berkeley 的实现完全在内核中处理中止的连接,服务进程将永远不知道该中止的发生。服务器进程一般可以忽略该错误,直接再次调用accept。
                    ENFILE                        文件表溢出
                    EMFILE                       打开文件太多