1.errno套接字编程中errno是一个属于进程的全局变量所有线程共享表示最后一次系统调用失败的错误代码。使用errno需要引用头文件#include errno.hstrerror(errno) 可以将 errno 转换为对应的错误信息字符串。perror() 会自动打印 errno 的错误信息到标准错误输出。它通常比 strerror更方便因为它同时打印了错误信息和调用的出错函数名。2.socketint socket(int domain, int type, int protocol);domain指定套接字通信域IPv4为AF_INET。type指定协议类型TCP为SOCK_STREAMUDP为SOCK_DGRAM。protocol指定协议层次应用层为0。成功则返回值0表示关联的文件描述符失败则返回-1并设置errno为ENOMEM。socket用来创建访问协议栈资源的标识。int fd socket(AF_INET, SOCK_STREAM, 0);3.bindint bind(int sockfd, const struct sockaddr *addr, socklen_t len);成功则返回0失败则返回-1并设置errno。bind用来设置本地IP和端口。服务器端使用bind来为监听套接字绑定IP和端口。若服务器端使用INADDR_ANY绑定IP可接收主机所有网卡的数据。客户端可不必调用bind若未调用则系统在connectTCP或首次sendtoUDP时会自动选择一个可用的IP和端口。客户端使用INADDR_ANY绑定IP协议栈会自动选择一个可用的IP。客户端调用bind是为了明确指定IP或端口如在接收UDP广播时需要绑定端口。bind失败可能设置的errno有1EADDRINUSE表示IP和端口已被占用。2EADDRNOTAVAIL表示地址无效。3EACCES表示权限不足低于1024的端口需要超级用户权限。4EINVAL表示参数不正确参数结构体可能不完整。4.listenint listen(int sockfd, int backlog);成功则返回0失败则返回-1并设置errno。listen用来告知协议栈应用层允许的全连接队列的最大数量即协议栈已完成连接建立正在等待应用层调用accept取出的连接数。Linux协议栈实际允许的全连接队列最大数量是backlog和net.core.somaxconn两个数中的最小值。内核参数net.core.somaxconn的默认值为Linux 5.4128Linux ≥5.44096。监听套接字的状态只会在LISTEN和CLOSED之间转换调用listen后处于LISTEN状态调用close后转为CLOSED状态。监听套接字只用来接收和处理SYN和ACK报文不与客户端建立连接不分配数据收发缓存不参与应用层数据传输。listen失败可能设置的errno有1EBADF表示指定的监听套接字不是有效的文件描述符。5.connectint connect(int sockfd, const struct sockaddr *addr, socklen_t len);成功则返回0连接建立失败则返回-1并设置errno。非阻塞套接字调用connect会立即返回-1并设置errno为EINPROGRESS。TCP客户端调用connect向指定IP和端口发起连接。TCP客户端调用connect建立连接失败可能会阻塞最大阻塞时间时TCP连接建立的超时时间大约1分钟。UDP套接字调用connect只是设置数据发送的默认IP和端口并不发起连接。connect失败可能设置的errno有1EINPROGRESS表示非阻塞connect连接的三次握手在后台进行。2ETIMEDOUT表示连接超时。3EHOSTUNREACH表示目标主机不可达。4ENETUNREACH表示目标网络不可达。5EADDRINUSE表示IP和端口已被占用。6EADDRNOTAVAIL表示地址无效。7ECONNREFUSED表示目标拒绝连接可能是目标端口未开放或应用层未运行。6.acceptint accept(int listen_sockfd, struct sockaddr *addr, socklen_t *addrlen);成功则返回值0表示获取连接的文件描述符失败则返回-1并设置errno。返回-1可能表示accpet出错也可能表示非阻塞accept全连接队列为空需要根据errno具体处理。TCP服务器端调用accept从全连接队列中取出连接。若套接字为阻塞式全连接队列为空则一直阻塞直到出错返回-1。若套接字为非阻塞式全连接队列为空则立即返回-1并设置errno为EAGAIN或EWOULDBLOCK。监听套接字绑定固定IP时accept返回的所有连接都使用与监听套接字相同的本地IP和端口对端的IP和端口不同服务器端依靠对端IP和端口信息区分不同连接。服务器端有多个网卡监听套接字绑定INADDR_ANY时accept返回的连接可能使用主机任意可用本地IP本地端口是固定的与监听套接字相同对端的IP和端口不同。accept失败可能设置的errno有1EAGAIN或EWOULDBLOCK表示非阻塞套接字全连接队列为空。2EINTR表示操作被信号中断。3ENOMEM表示系统内存不足。4EMFILE表示进程的文件描述符数量已达到上限。5ENFILE表示系统文件描述符数量已达到上限。6EBADF表示指定的监听套接字不是有效的文件描述符。7ECONNABORTED表示连接被对方重置。7.sendssize_t send(int sockfd, const void *buf, size_t len, int flags);返回值0表示成功写入发送缓存的字节数返回-1表示出错。send用于向连接的发送缓存写入数据成功写入的字节数可能小于len小于len说明连接的发送缓存空间不足。阻塞式send可能会阻塞直到将要发送的数据全部写入缓存或者出错返会-1。非阻塞式send在数据写完或缓存写满后立即返回数据未写完则返回-1设置errno为EAGAIN或EWOULDBLOCK。flags可控制发送行为多种标志可以组合使用10默认行为阻塞式写入。2MSG_DONTWAIT本次写入为非阻塞即使套接字为阻塞模式也立即返回。3MSG_EOF发送数据后关闭连接的发送端。4MSG_DONTROUTE不将数据包路由出本地网络。5MSG_OOB发送带外数据仅部分实现支持通常只最后1字节有效。6MSG_NOSIGNAL对端关闭连接时不触发SIGPIPE信号仅设置errno为EPIPE。7MSG_MORELinux提示内核后续还有数据可延迟合并小包优化Nagle算法8MSG_CONFIRMLinux主要用于UDP/链路层确认对端可达性TCP一般不使用。send失败可能设置的errno有1EAGAIN或EWOULDBLOCK表示非阻塞式写入数据未写完。2EINTR表示操作被信号中断。3EPIPE表示连接已正常关闭在应用层未处理SIGPIPE信号时触发。4ECONNRESET表示连接已异常关闭对端发送RST报文重置连接。5ENOTCONN表示从未建立过连接。6EMSGSIZE表示消息太大主要用于UDPTCP一般不报此错除非分片受限。7ENOMEM或ENOBUFS表示系统内存不足。8.recvssize_t recv(int sockfd, void *buf, size_t len, int flags);返回0表示收到字节数返回0表示连接已正常关闭返回-1表示出错。recv用于从连接的接收缓存读取数据成功读取的字节数可能小于len小于len说明连接的接收缓存中数据比预期少。recv返回值大于0表示读取成功即使返回值小于len也是成功不会设置errno。recv返回值等于0也是读取成功不会设置errno表示连接已正常关闭注意是正常关闭而不是异常关闭异常关闭会返回-1并设置errno为ECONNRESET。本端主动调用close关闭后再调用 recv返回-1并设置errno为EBADF。若传入的参数len0则recv会返回0但是并不表示连接正常关闭应用层需避免。flags可控制读取行为多种标志可以组合使用10默认行为阻塞式读取。2MSG_DONTWAIT本次读取为非阻塞即使套接字为阻塞模式也立即返回无数据时设置errno为EAGAIN/EWOULDBLOCK。3MSG_PEEK窥探数据读取但不从接收队列移除后续recv仍可读到相同数据。4MSG_OOB读取带外数据需对端用MSG_OOB发送且本端启用SO_OOBINLINE才有效。5MSG_WAITALL阻塞直到读满len字节连接出错或者关闭时会退出阻塞。6MSG_TRUNC若数据被截断返回实际总长度通常用于UDP/RAWTCP一般不截断此标志在TCP中无实质作用。7MSG_CTRUNC控制消息被截断时设标志TCP 基本不用。recv失败可能设置的errno有1EAGAIN或EWOULDBLOCK表示非阻塞式读取SO_RCVTIMEO设置的时间超时未读到数据。2EINTR表示操作被信号中断。3ECONNRESET表示连接异常关闭对端发送RST报文重置连接。4ENOTCONN表示从未建立过连接。5EFAULT传入的缓冲区地址参数无效。6EINVAL参数非法如flags含无效位。7EBADF无效的文件描述符。9.closeint close(int sockfd);应用层主动调用close说明其已不再发送数据也不再继续接收数据协议栈会丢弃接收缓存中的数据但是会尽量确保发送缓存中的数据能够正常发出。发送缓存中的数据发送完毕后协议栈会按正常流程关闭连接。close不会阻塞发送缓存中的数据发送失败连接关闭失败应用层都无法感知。如果一个连接关联多个文件描述符close其中一个文件描述符并不会关闭连接系统会在所有文件描述符都关闭之后才调用协议栈执行连接关闭流程。10.shutdownint shutdown(int sockfd, int how);how的不同取值含义如下1SHUT_WR使用SHUT_WR调用shutdown表示应用层已不再发送数据协议栈会尽量确保发送缓存中的数据能够正常发出。发送缓存中的数据发送完毕后协议栈会发送FIN报文给对端收到对端的ACK报文后不会启动第三次挥手的FIN报文超时机制双方可以一直维持单向通信。应用层使用SHUT_WR调用shutdown后对端recv会返回0。应用层使用SHUT_WR调用shutdown后若想完全关闭连接应调用close协议栈会根据连接状态判断如何执行四次挥手流程。调用close比使用SHUT_RDWR调用shutdown好因为shutdown不会关闭文件描述符。2SHUT_RDSHUT_RD在TCP套接字中是没有实际意义的操作在不同系统上会产生不同的结果应避免使用。3SHUT_RDWR使用SHUT_RDWR调用shutdown表示应用层已不再接收和发送数据协议栈的处理与close相同。协议栈会根据连接最新的状态执行四次挥手流程若之前已使用SHUT_WR调用shutdown协议栈不会重发FIN而是直接启动第三次挥手的FIN报文超时机制。调用shutdown与close的区别1shutdown不会关闭文件描述符即使用SHUT_RDWR调用也一样文件描述符需要另外调用close关闭。1shutdown会直接关闭连接。如果一个连接关联多个文件描述符shutdown会改变所有文件描述符的属性因为shutdown改变的是文件描述符背后的文件实例。close只对其操作的文件描述符起效。11.sendtossize_t sendto(int sockfd, const void *buf, size_t len, int flags,const struct sockaddr *destaddr, socklen_t destlen);若成功则返回写入发送缓存的字节数若失败则返回-1并设置errno。阻塞式sendto实际很少阻塞因为UDP不保证数据可靠送达协议栈会不断发送缓存中的数据缓存空间不足也只是暂时的阻塞一般持续时间很短。非阻塞recvfrom若没有写入数据不会返回0而是返回-1并设置errno为EAGAIN/EWOULDBLOCK。flags可控制发送行为多种标志可以组合使用10默认行为阻塞式写入。2MSG_DONTWAIT本次写入为非阻塞即使套接字为阻塞模式也立即返回。3MSG_CONFIRM确认对端可达性。sendto失败可能设置的errno有1EAGAIN 或 EWOULDBLOCK非阻塞模式写入发送缓存没有空间可写。2EBADF无效的文件描述符套接字文件描述符未初始化。3EINTR操作被信号中断。4EMSGSIZE消息太长超过了套接字支持的最大数据包大小MTU。5ENOBUFS或ENOMEM系统内存不足无法为数据包分配足够的内存。12.recvfromssize_t recvfrom(int sockfd, void *restrict buf, size_t len, int flags,struct sockaddr *restrict addr, socklen_t *restrict addrlen);若成功则返回从接收缓存读取的字节数若失败则返回-1并设置errno。非阻塞recvfrom若没有读到数据不会返回0而是返回-1并设置errno为EAGAIN/EWOULDBLOCK。flags可控制读取行为多种标志可以组合使用10默认行为阻塞式读取。2MSG_DONTWAIT本次读取为非阻塞即使套接字为阻塞模式也立即返回。3MSG_PEEK窥探数据读取但不从接收队列移除后续仍可读到相同数据。4MSG_WAITALL阻塞直到读满len字节出错时会退出阻塞。5MSG_TRUNC若数据被截断返回实际总长度。recvfrom失败可能设置的errno有1EAGAIN 或 EWOULDBLOCK非阻塞式读取接收缓存没有数据。2EBADF无效的文件描述符套接字文件描述符未初始化。3EINTR操作被信号中断。4EFAULT缓冲区参数指向内存区域不允许访问。13.read和writessize_t read(int sockfd, void *buf, size_t len);ssize_t write(int sockfd, const void *buf, size_t len);read和write的返回值含义与recv和send相同只是调用时不能设置标志。非阻塞read和write需要在套接字选项中设置。14.selectint select(int nfds, fd_set *readfds, fd_set *writefds, fd_set *exceptfds,struct timeval *timeout);参数nfds为最大的文件描述符的值加1。select会从集合中读取要监控的文件描述符然后清空集合再把就绪的文件描述符放入集合。select采用线性扫描的方式遍历所有待监控的文件描述符在处理大量文件描述符时效率较低。select函数成功则返回所有就绪描述符集合读、写、异常中的描述符总数超时则返回0不会设置errno。如果发生错误则返回-1并设置errno。select返回-1表示的错误只是select操作自身产生的错误并不是连接产生的错误连接产生的错误需要根据exceptfds再结合连接的套接字操作检查。14.1 select查询到套接字异常就绪原因可能有多种(1) 有带外数据到达。(2) 连接发生致命错误如收到对端重置连接的RST报文协议栈检测到不可恢复的错误。(3) accept时监听套接字发生错误。(4) 使用非阻塞connect建立连接失败如收到RST或连接建立超时。14.2 与select有关的结构和宏typedef struct fd_set {unsigned int fd_count; //文件描述符的数量int fd_array[FD_SETSIZE]; //文件描述符数组} fd_set;fd_set可以容纳的文件描述符数量通常是有限的Linux系统FD_SETSIZE的默认值是1024。FD_ZERO(fd_set *fdset)清空集合fdset中的所有文件描述符。FD_SET(int fd, fd_set *fdset)把文件描述符fd放入集合fdset。FD_CLR(int fd, fd_set *fdset)把文件描述符fd从集合fdset中删除。FD_ISSET(int fd, fd_set *fdset)检查文件描述符fd是否在集合fdset中即检查fd是否就绪如果fd是集合的成员则返回非零值否则返回零。struct timeval{long tv_sec; // 秒数long tv_usec; // 微秒数};永远等待仅在一个描述符准备好I/O时才返回为此我们可以把该参数置为空指针等待一段固定时间在有一个描述符准备好I/O时返回但是不超过由该参数所指向的timeval结构中指定的秒数和微秒数根本不等待检查描述符后立即返回这称为轮询。该参数必须指向一个timeval结构并且其中的定时器值秒数和微秒数必须为0.14.3 select失败可能设置的errno有1EINTR调用被信号中断2EINVALnfds为负数。15.pollint poll(struct pollfd *fds, nfds_t nfds, int timeout);参数含义fds数组地址。nfds数组元素个数。timeout超时时间单位是毫秒。struct pollfd {int fd; //要监控的文件描述符。short events; //请求监控的事件由应用层设置。short revents; //实际发生的事件由内核设置。};常用的poll事件有1POLLIN: 数据可读包括普通数据和监听套接字上的新连接。2POLLOUT: 数据可写发送缓冲区有空闲。3POLLERR: 错误发生这个事件通常会被自动监控不必在events中设置。4POLLHUP: 连接挂起对端关闭了写。5POLLNVAL: 文件描述符未打开无效的 fd。6POLLPRI紧急或带外数据可读。7POLLRDBAND优先级带数据可读带外数据较少用。8POLLWRBAND优先级带数据可写带外数据较少用。9POLLRDNORM普通可读与POLLIN等价。10POLLWRNORM普通可写与POLLOUT等价。16.epollint epoll_create(int size);参数含义size创建的红黑树的监听节点数量仅供内核参考在最新的内核版本中已不再使用通常设置为1。int epoll_create1(int flags);int epoll_ctl(int epfd, int op, int fd, struct epoll_event *event);参数含义epfdepoll实例的文件描述符。op操作类型如EPOLL_CTL_ADD, EPOLL_CTL_DEL, EPOLL_CTL_MOD。fd要监视的文件描述符。event指定监视的事件类型。int epoll_wait(int epfd, struct epoll_event *events, int maxevents, int timeout);参数含义epfdepoll实例的文件描述符。events返回发生的事件的数组。maxeventsevents数组的大小。timeout超时时间单位是毫秒。typedef union epoll_data {void *ptr;int fd;uint32_t u32;uint64_t u64;} epoll_data_t;struct epoll_event {uint32_t events; // 监听的事件掩码epoll_data_t data; // 用户自定义数据常用 fd 或 ptr 关联业务上下文};events指定要监听的事件和监听模式组合使用可监听fd多个事件含义如下1EPOLLOUT写就绪2EPOLLRDHUP对端正常关闭或半关闭。3EPOLLERR发生错误自动触发无需显式指定。4EPOLLHUP套接字挂断如对端异常断开。5EPOLLET默认为水平触发LT模式加此标志变为边缘触发ET模式。17.套接字选项SO_KEEPALIVE 是套接字属性属性只对之后建立的 TCP 连接生效连接三次握手完成后再开 keepalive对这条已建好的连接无效。套接字是连接的标识套接字选项必须在连接建立之前设置才能生效在连接建立之后设置无效。IP选项TCP选项非阻塞套接字与非阻塞flags默认的超时时间。listen 前设置后续所有 accept 出来的新连接默认继承这个 keepalive 属性。 listen 之后再设置只影响未来新建 socket已经 accept 的老连接不受影响。18.TCP和UDP收发缓存大小TCP发送缓存默认大小为16384字节接收缓存默认大小为87380字节。UDP发送和接收缓存默认大小都是110592字节。TCP和UDP收发缓存的最大为131017。TCP和UDP接收缓存的最小值为256字节由内核的宏决定。TCP和UDP发送缓存的最小值为2048字节由内核的宏决定。
套接字总结
1.errno套接字编程中errno是一个属于进程的全局变量所有线程共享表示最后一次系统调用失败的错误代码。使用errno需要引用头文件#include errno.hstrerror(errno) 可以将 errno 转换为对应的错误信息字符串。perror() 会自动打印 errno 的错误信息到标准错误输出。它通常比 strerror更方便因为它同时打印了错误信息和调用的出错函数名。2.socketint socket(int domain, int type, int protocol);domain指定套接字通信域IPv4为AF_INET。type指定协议类型TCP为SOCK_STREAMUDP为SOCK_DGRAM。protocol指定协议层次应用层为0。成功则返回值0表示关联的文件描述符失败则返回-1并设置errno为ENOMEM。socket用来创建访问协议栈资源的标识。int fd socket(AF_INET, SOCK_STREAM, 0);3.bindint bind(int sockfd, const struct sockaddr *addr, socklen_t len);成功则返回0失败则返回-1并设置errno。bind用来设置本地IP和端口。服务器端使用bind来为监听套接字绑定IP和端口。若服务器端使用INADDR_ANY绑定IP可接收主机所有网卡的数据。客户端可不必调用bind若未调用则系统在connectTCP或首次sendtoUDP时会自动选择一个可用的IP和端口。客户端使用INADDR_ANY绑定IP协议栈会自动选择一个可用的IP。客户端调用bind是为了明确指定IP或端口如在接收UDP广播时需要绑定端口。bind失败可能设置的errno有1EADDRINUSE表示IP和端口已被占用。2EADDRNOTAVAIL表示地址无效。3EACCES表示权限不足低于1024的端口需要超级用户权限。4EINVAL表示参数不正确参数结构体可能不完整。4.listenint listen(int sockfd, int backlog);成功则返回0失败则返回-1并设置errno。listen用来告知协议栈应用层允许的全连接队列的最大数量即协议栈已完成连接建立正在等待应用层调用accept取出的连接数。Linux协议栈实际允许的全连接队列最大数量是backlog和net.core.somaxconn两个数中的最小值。内核参数net.core.somaxconn的默认值为Linux 5.4128Linux ≥5.44096。监听套接字的状态只会在LISTEN和CLOSED之间转换调用listen后处于LISTEN状态调用close后转为CLOSED状态。监听套接字只用来接收和处理SYN和ACK报文不与客户端建立连接不分配数据收发缓存不参与应用层数据传输。listen失败可能设置的errno有1EBADF表示指定的监听套接字不是有效的文件描述符。5.connectint connect(int sockfd, const struct sockaddr *addr, socklen_t len);成功则返回0连接建立失败则返回-1并设置errno。非阻塞套接字调用connect会立即返回-1并设置errno为EINPROGRESS。TCP客户端调用connect向指定IP和端口发起连接。TCP客户端调用connect建立连接失败可能会阻塞最大阻塞时间时TCP连接建立的超时时间大约1分钟。UDP套接字调用connect只是设置数据发送的默认IP和端口并不发起连接。connect失败可能设置的errno有1EINPROGRESS表示非阻塞connect连接的三次握手在后台进行。2ETIMEDOUT表示连接超时。3EHOSTUNREACH表示目标主机不可达。4ENETUNREACH表示目标网络不可达。5EADDRINUSE表示IP和端口已被占用。6EADDRNOTAVAIL表示地址无效。7ECONNREFUSED表示目标拒绝连接可能是目标端口未开放或应用层未运行。6.acceptint accept(int listen_sockfd, struct sockaddr *addr, socklen_t *addrlen);成功则返回值0表示获取连接的文件描述符失败则返回-1并设置errno。返回-1可能表示accpet出错也可能表示非阻塞accept全连接队列为空需要根据errno具体处理。TCP服务器端调用accept从全连接队列中取出连接。若套接字为阻塞式全连接队列为空则一直阻塞直到出错返回-1。若套接字为非阻塞式全连接队列为空则立即返回-1并设置errno为EAGAIN或EWOULDBLOCK。监听套接字绑定固定IP时accept返回的所有连接都使用与监听套接字相同的本地IP和端口对端的IP和端口不同服务器端依靠对端IP和端口信息区分不同连接。服务器端有多个网卡监听套接字绑定INADDR_ANY时accept返回的连接可能使用主机任意可用本地IP本地端口是固定的与监听套接字相同对端的IP和端口不同。accept失败可能设置的errno有1EAGAIN或EWOULDBLOCK表示非阻塞套接字全连接队列为空。2EINTR表示操作被信号中断。3ENOMEM表示系统内存不足。4EMFILE表示进程的文件描述符数量已达到上限。5ENFILE表示系统文件描述符数量已达到上限。6EBADF表示指定的监听套接字不是有效的文件描述符。7ECONNABORTED表示连接被对方重置。7.sendssize_t send(int sockfd, const void *buf, size_t len, int flags);返回值0表示成功写入发送缓存的字节数返回-1表示出错。send用于向连接的发送缓存写入数据成功写入的字节数可能小于len小于len说明连接的发送缓存空间不足。阻塞式send可能会阻塞直到将要发送的数据全部写入缓存或者出错返会-1。非阻塞式send在数据写完或缓存写满后立即返回数据未写完则返回-1设置errno为EAGAIN或EWOULDBLOCK。flags可控制发送行为多种标志可以组合使用10默认行为阻塞式写入。2MSG_DONTWAIT本次写入为非阻塞即使套接字为阻塞模式也立即返回。3MSG_EOF发送数据后关闭连接的发送端。4MSG_DONTROUTE不将数据包路由出本地网络。5MSG_OOB发送带外数据仅部分实现支持通常只最后1字节有效。6MSG_NOSIGNAL对端关闭连接时不触发SIGPIPE信号仅设置errno为EPIPE。7MSG_MORELinux提示内核后续还有数据可延迟合并小包优化Nagle算法8MSG_CONFIRMLinux主要用于UDP/链路层确认对端可达性TCP一般不使用。send失败可能设置的errno有1EAGAIN或EWOULDBLOCK表示非阻塞式写入数据未写完。2EINTR表示操作被信号中断。3EPIPE表示连接已正常关闭在应用层未处理SIGPIPE信号时触发。4ECONNRESET表示连接已异常关闭对端发送RST报文重置连接。5ENOTCONN表示从未建立过连接。6EMSGSIZE表示消息太大主要用于UDPTCP一般不报此错除非分片受限。7ENOMEM或ENOBUFS表示系统内存不足。8.recvssize_t recv(int sockfd, void *buf, size_t len, int flags);返回0表示收到字节数返回0表示连接已正常关闭返回-1表示出错。recv用于从连接的接收缓存读取数据成功读取的字节数可能小于len小于len说明连接的接收缓存中数据比预期少。recv返回值大于0表示读取成功即使返回值小于len也是成功不会设置errno。recv返回值等于0也是读取成功不会设置errno表示连接已正常关闭注意是正常关闭而不是异常关闭异常关闭会返回-1并设置errno为ECONNRESET。本端主动调用close关闭后再调用 recv返回-1并设置errno为EBADF。若传入的参数len0则recv会返回0但是并不表示连接正常关闭应用层需避免。flags可控制读取行为多种标志可以组合使用10默认行为阻塞式读取。2MSG_DONTWAIT本次读取为非阻塞即使套接字为阻塞模式也立即返回无数据时设置errno为EAGAIN/EWOULDBLOCK。3MSG_PEEK窥探数据读取但不从接收队列移除后续recv仍可读到相同数据。4MSG_OOB读取带外数据需对端用MSG_OOB发送且本端启用SO_OOBINLINE才有效。5MSG_WAITALL阻塞直到读满len字节连接出错或者关闭时会退出阻塞。6MSG_TRUNC若数据被截断返回实际总长度通常用于UDP/RAWTCP一般不截断此标志在TCP中无实质作用。7MSG_CTRUNC控制消息被截断时设标志TCP 基本不用。recv失败可能设置的errno有1EAGAIN或EWOULDBLOCK表示非阻塞式读取SO_RCVTIMEO设置的时间超时未读到数据。2EINTR表示操作被信号中断。3ECONNRESET表示连接异常关闭对端发送RST报文重置连接。4ENOTCONN表示从未建立过连接。5EFAULT传入的缓冲区地址参数无效。6EINVAL参数非法如flags含无效位。7EBADF无效的文件描述符。9.closeint close(int sockfd);应用层主动调用close说明其已不再发送数据也不再继续接收数据协议栈会丢弃接收缓存中的数据但是会尽量确保发送缓存中的数据能够正常发出。发送缓存中的数据发送完毕后协议栈会按正常流程关闭连接。close不会阻塞发送缓存中的数据发送失败连接关闭失败应用层都无法感知。如果一个连接关联多个文件描述符close其中一个文件描述符并不会关闭连接系统会在所有文件描述符都关闭之后才调用协议栈执行连接关闭流程。10.shutdownint shutdown(int sockfd, int how);how的不同取值含义如下1SHUT_WR使用SHUT_WR调用shutdown表示应用层已不再发送数据协议栈会尽量确保发送缓存中的数据能够正常发出。发送缓存中的数据发送完毕后协议栈会发送FIN报文给对端收到对端的ACK报文后不会启动第三次挥手的FIN报文超时机制双方可以一直维持单向通信。应用层使用SHUT_WR调用shutdown后对端recv会返回0。应用层使用SHUT_WR调用shutdown后若想完全关闭连接应调用close协议栈会根据连接状态判断如何执行四次挥手流程。调用close比使用SHUT_RDWR调用shutdown好因为shutdown不会关闭文件描述符。2SHUT_RDSHUT_RD在TCP套接字中是没有实际意义的操作在不同系统上会产生不同的结果应避免使用。3SHUT_RDWR使用SHUT_RDWR调用shutdown表示应用层已不再接收和发送数据协议栈的处理与close相同。协议栈会根据连接最新的状态执行四次挥手流程若之前已使用SHUT_WR调用shutdown协议栈不会重发FIN而是直接启动第三次挥手的FIN报文超时机制。调用shutdown与close的区别1shutdown不会关闭文件描述符即使用SHUT_RDWR调用也一样文件描述符需要另外调用close关闭。1shutdown会直接关闭连接。如果一个连接关联多个文件描述符shutdown会改变所有文件描述符的属性因为shutdown改变的是文件描述符背后的文件实例。close只对其操作的文件描述符起效。11.sendtossize_t sendto(int sockfd, const void *buf, size_t len, int flags,const struct sockaddr *destaddr, socklen_t destlen);若成功则返回写入发送缓存的字节数若失败则返回-1并设置errno。阻塞式sendto实际很少阻塞因为UDP不保证数据可靠送达协议栈会不断发送缓存中的数据缓存空间不足也只是暂时的阻塞一般持续时间很短。非阻塞recvfrom若没有写入数据不会返回0而是返回-1并设置errno为EAGAIN/EWOULDBLOCK。flags可控制发送行为多种标志可以组合使用10默认行为阻塞式写入。2MSG_DONTWAIT本次写入为非阻塞即使套接字为阻塞模式也立即返回。3MSG_CONFIRM确认对端可达性。sendto失败可能设置的errno有1EAGAIN 或 EWOULDBLOCK非阻塞模式写入发送缓存没有空间可写。2EBADF无效的文件描述符套接字文件描述符未初始化。3EINTR操作被信号中断。4EMSGSIZE消息太长超过了套接字支持的最大数据包大小MTU。5ENOBUFS或ENOMEM系统内存不足无法为数据包分配足够的内存。12.recvfromssize_t recvfrom(int sockfd, void *restrict buf, size_t len, int flags,struct sockaddr *restrict addr, socklen_t *restrict addrlen);若成功则返回从接收缓存读取的字节数若失败则返回-1并设置errno。非阻塞recvfrom若没有读到数据不会返回0而是返回-1并设置errno为EAGAIN/EWOULDBLOCK。flags可控制读取行为多种标志可以组合使用10默认行为阻塞式读取。2MSG_DONTWAIT本次读取为非阻塞即使套接字为阻塞模式也立即返回。3MSG_PEEK窥探数据读取但不从接收队列移除后续仍可读到相同数据。4MSG_WAITALL阻塞直到读满len字节出错时会退出阻塞。5MSG_TRUNC若数据被截断返回实际总长度。recvfrom失败可能设置的errno有1EAGAIN 或 EWOULDBLOCK非阻塞式读取接收缓存没有数据。2EBADF无效的文件描述符套接字文件描述符未初始化。3EINTR操作被信号中断。4EFAULT缓冲区参数指向内存区域不允许访问。13.read和writessize_t read(int sockfd, void *buf, size_t len);ssize_t write(int sockfd, const void *buf, size_t len);read和write的返回值含义与recv和send相同只是调用时不能设置标志。非阻塞read和write需要在套接字选项中设置。14.selectint select(int nfds, fd_set *readfds, fd_set *writefds, fd_set *exceptfds,struct timeval *timeout);参数nfds为最大的文件描述符的值加1。select会从集合中读取要监控的文件描述符然后清空集合再把就绪的文件描述符放入集合。select采用线性扫描的方式遍历所有待监控的文件描述符在处理大量文件描述符时效率较低。select函数成功则返回所有就绪描述符集合读、写、异常中的描述符总数超时则返回0不会设置errno。如果发生错误则返回-1并设置errno。select返回-1表示的错误只是select操作自身产生的错误并不是连接产生的错误连接产生的错误需要根据exceptfds再结合连接的套接字操作检查。14.1 select查询到套接字异常就绪原因可能有多种(1) 有带外数据到达。(2) 连接发生致命错误如收到对端重置连接的RST报文协议栈检测到不可恢复的错误。(3) accept时监听套接字发生错误。(4) 使用非阻塞connect建立连接失败如收到RST或连接建立超时。14.2 与select有关的结构和宏typedef struct fd_set {unsigned int fd_count; //文件描述符的数量int fd_array[FD_SETSIZE]; //文件描述符数组} fd_set;fd_set可以容纳的文件描述符数量通常是有限的Linux系统FD_SETSIZE的默认值是1024。FD_ZERO(fd_set *fdset)清空集合fdset中的所有文件描述符。FD_SET(int fd, fd_set *fdset)把文件描述符fd放入集合fdset。FD_CLR(int fd, fd_set *fdset)把文件描述符fd从集合fdset中删除。FD_ISSET(int fd, fd_set *fdset)检查文件描述符fd是否在集合fdset中即检查fd是否就绪如果fd是集合的成员则返回非零值否则返回零。struct timeval{long tv_sec; // 秒数long tv_usec; // 微秒数};永远等待仅在一个描述符准备好I/O时才返回为此我们可以把该参数置为空指针等待一段固定时间在有一个描述符准备好I/O时返回但是不超过由该参数所指向的timeval结构中指定的秒数和微秒数根本不等待检查描述符后立即返回这称为轮询。该参数必须指向一个timeval结构并且其中的定时器值秒数和微秒数必须为0.14.3 select失败可能设置的errno有1EINTR调用被信号中断2EINVALnfds为负数。15.pollint poll(struct pollfd *fds, nfds_t nfds, int timeout);参数含义fds数组地址。nfds数组元素个数。timeout超时时间单位是毫秒。struct pollfd {int fd; //要监控的文件描述符。short events; //请求监控的事件由应用层设置。short revents; //实际发生的事件由内核设置。};常用的poll事件有1POLLIN: 数据可读包括普通数据和监听套接字上的新连接。2POLLOUT: 数据可写发送缓冲区有空闲。3POLLERR: 错误发生这个事件通常会被自动监控不必在events中设置。4POLLHUP: 连接挂起对端关闭了写。5POLLNVAL: 文件描述符未打开无效的 fd。6POLLPRI紧急或带外数据可读。7POLLRDBAND优先级带数据可读带外数据较少用。8POLLWRBAND优先级带数据可写带外数据较少用。9POLLRDNORM普通可读与POLLIN等价。10POLLWRNORM普通可写与POLLOUT等价。16.epollint epoll_create(int size);参数含义size创建的红黑树的监听节点数量仅供内核参考在最新的内核版本中已不再使用通常设置为1。int epoll_create1(int flags);int epoll_ctl(int epfd, int op, int fd, struct epoll_event *event);参数含义epfdepoll实例的文件描述符。op操作类型如EPOLL_CTL_ADD, EPOLL_CTL_DEL, EPOLL_CTL_MOD。fd要监视的文件描述符。event指定监视的事件类型。int epoll_wait(int epfd, struct epoll_event *events, int maxevents, int timeout);参数含义epfdepoll实例的文件描述符。events返回发生的事件的数组。maxeventsevents数组的大小。timeout超时时间单位是毫秒。typedef union epoll_data {void *ptr;int fd;uint32_t u32;uint64_t u64;} epoll_data_t;struct epoll_event {uint32_t events; // 监听的事件掩码epoll_data_t data; // 用户自定义数据常用 fd 或 ptr 关联业务上下文};events指定要监听的事件和监听模式组合使用可监听fd多个事件含义如下1EPOLLOUT写就绪2EPOLLRDHUP对端正常关闭或半关闭。3EPOLLERR发生错误自动触发无需显式指定。4EPOLLHUP套接字挂断如对端异常断开。5EPOLLET默认为水平触发LT模式加此标志变为边缘触发ET模式。17.套接字选项SO_KEEPALIVE 是套接字属性属性只对之后建立的 TCP 连接生效连接三次握手完成后再开 keepalive对这条已建好的连接无效。套接字是连接的标识套接字选项必须在连接建立之前设置才能生效在连接建立之后设置无效。IP选项TCP选项非阻塞套接字与非阻塞flags默认的超时时间。listen 前设置后续所有 accept 出来的新连接默认继承这个 keepalive 属性。 listen 之后再设置只影响未来新建 socket已经 accept 的老连接不受影响。18.TCP和UDP收发缓存大小TCP发送缓存默认大小为16384字节接收缓存默认大小为87380字节。UDP发送和接收缓存默认大小都是110592字节。TCP和UDP收发缓存的最大为131017。TCP和UDP接收缓存的最小值为256字节由内核的宏决定。TCP和UDP发送缓存的最小值为2048字节由内核的宏决定。