在Unix下进行网络编程时,因为网络并非齐全牢靠,会遇到各种协定主流程外产生的各种谬误。

而强壮的程序必须思考到这些谬误并正确处理,因而这里总结网络编程中可能产生的常见谬误。

TCP异样流程

总体

应答超时

在握手,挥手以及消息传递的状态下,若以后发送的报文期待一个应答报文。在规定工夫应答报文没有达到,发送方会重发两次报文(报文重发距离可设置)。若重发三次后仍旧没有收到应答,则向利用返回ETIMEOUT

目标不可达

若报文在传送的过程中,因为找不到路由门路,报文无奈达到等引发了ICMP谬误,发送方会依照上述的形式重发次报文。若重发完结后仍旧没有收到应答,则向利用返回EHOSTUNREACH或者ENETUNREACH

禁止分片

在IPV4中,报文超过了MTU长度会导致分片,而其存在DF(Don't Fragment)标识,示意禁止分片。同时IPV6禁止路由器分片,因而在传送的过程中隐含DF位。在传送过程中设置了DF位而超过了MTU,则会向利用返回EMSGSIZE

阻塞时中断

在零碎执行慢零碎调用(可能被永远阻塞的零碎调用)时阻塞,此时捕捉到某个信号并进行了解决(系统对某些信号有默认解决形式),在没有设置主动重启的状况下,会向利用返回EINTR

个别应答EINTR的形式是简略的从新调用,但在connect返回EINTR时不能这么做,因为connect波及三次握手的过程,须要应用getsockopt获取连贯状态

读写时RST

在调用read等阻塞时接管到对端RST信号时,会返回ECONNREST。同时对发送方断开的套接字写时也会返回ECONNRESET

写RST套接字

当过程向收到RST的套接字执行写操作的时候,内核向该过程发送一个SIGPIPE信号,该信号的默认行为是终止过程,因而过程必须捕捉它免得不愿意的被终止

不管过程是捕获了该信号并从信号处理函数中返回,还是简略疏忽该信号,写操作都讲返回EPIPE谬误

握手局部

首次握手服务端RST

在客户端第一次握手时,若服务端返回RST报文,立刻向利用返回ECONNREFUSED

握手完结客户端RST

在较为忙碌的服务器中,可能呈现上图客户端刚经验三次握手后随机发送RST报文的状况,posix指出这种状况errno设置为ECONNABORTED,只须要再次调用accept即可。

而在Berkeley的实现中,返回EPROTO谬误,代表协定谬误,是一种致命谬误,由内核把该连贯从已实现连贯套接字队列中开释,若再次调用accept,则不会解决到本次申请,可能导致阻塞

数据传送局部

服务端主机解体

因为客户端无奈收到服务端的任何回应,会重发解决,最终向利用返回的状况可能为应答超时或目标不可达

若想尽快的检测出主机解体,不被动发送数据也可做到,即套接字选项的SO_KEEPALIVE(相似心跳机制)

挥手局部

服务端过程终止或关机

服务端因为过程解体或者手动kill后,过程终止敞开所有关上的描述符。这导致了其向客户端发送了一个FIN,客户端则响应了一个ack,TCP挥手的前半部分实现,服务端不在发送数据。

然而此时客户端并不知道服务器端曾经终止了。当客户端向服务器写数据的时候,因为服务器过程终止,所以响应了RST

这种状况下能够由select或者poll检测到服务端的终止。

  • 如果对端TCP发送数据,套接字可读,并且read返回一个大于0的值(读入字节数)
  • 如果对端TCP发送了FIN(对端过程终止),套接字可读,并且read返回0(EOF)
  • 如果对端TCP发送RST(对端解体并重启),套接字可读,并且read返回-1,errno中含有确切错误码

服务端终止后重启

因为重启后曾经失落了套接字连贯信息,服务端对客户端的报文响应RST。若此时客户端读,则会返回ECONNRESET

套接字api异常情况

socket

errno含意可能状况致命解决
EACCES权限有余
EAFNOSUPPORT地址族不反对参数谬误
EINVAL参数谬误未知协定或协定族不可用
EMFILE关上的文件过多过程级关上的fd达下限ulimit -n 调整或期待资源开释
ENFILE关上的文件过多零碎级关上的fd达下限期待资源开释
ENOBUFS/ENOMEM内存不足Buffer或文件表无奈创立期待资源开释

bind

errno含意可能状况致命解决
EACCES权限有余申请保留端口且非root运行
EADDRINUSE地址已被应用绑定已应用地址或申请长期端口时已占满长期端口已满时重试
EBADFfd不可用fd已敞开或参数非法
EINVAL参数谬误套接字反复绑定或参数谬误
ENOTSOCK非套接字fd参数谬误

listen

errno含意可能状况致命解决
EADDRINUSE地址已被应用监听已监听端口或未bind至确定端口而申请长期端口时已占满长期端口已满时重试
EBADFfd不可用fd已敞开或参数非法
ENOTSOCK非套接字fd参数谬误
EOPNOTSUPP操作不反对只反对SOCK_STREAM类套接字(TCP)

accept

errno含意可能状况致命解决
EWOULDBLOCK/EAGAIN资源临时不可用(非阻塞)队列中无实现握手的连贯重试或解决其余事务
EBADFfd不可用fd已敞开或参数非法
ECONNABORTED连贯已中断握手完结客户端RST重试或解决其余事务
EFAULT地址谬误addr参数没有指向用户可写空间
EINTR调用中断调用被信号中断重试或解决其余事务
EINVAL参数谬误套接字未在监听态或addrlen/flags谬误
EMFILE关上的文件过多过程级关上的fd达下限ulimit -n 调整或期待资源开释
ENFILE关上的文件过多零碎级关上的fd达下限期待资源开释
ENOMEM/ENOBUFS内存不足套接字buffer内存不足而非零碎内存不足期待资源开释

connect

errno含意可能状况致命解决
EACCES/EPERM权限有余/操作未容许未设置播送标记但连贯播送地址或防火墙禁止连贯
EADDRINUSE本地地址已被应用重试,期待资源开释
EADDRNOTAVAIL地址不可用未bind至确定端口而申请长期端口时已占满重试,期待资源开释
EAFNOSUPPORT地址族谬误
EAGAIN资源临时不可用无可用本地端口或路由缓存中无记录(?)重试,期待资源开释
EALREADY连贯正在解决(非阻塞)上一个连贯还未完结解决,可能是对返回EINPROGRESS从新调用
EBADFfd不可用fd已敞开或参数非法
ECONNREFUSED连贯回绝给定远端未监听,或见首次握手服务端RST重试
EFAULT地址谬误地址指针超出用户空间
EINPROGRESS操作正在进行(非阻塞)connect无奈立刻实现应用getsockopt判断连贯是出错还是已实现
EINTR调用中断调用被信号中断应用getsockopt判断连贯是出错还是已实现
EISCONN套接字已连贯应用getsockopt判断连贯是出错还是已实现
ENETUNREACH网络不可达目标不可达重试
ENOTSOCK非套接字fd参数谬误
EPROTOTYPE套接字协定谬误
ETIMEDOUT连贯超时远端无应答,可能因为零碎忙碌重试

另附: 对connect下几种无奈判断连贯是否胜利建设的解决计划的探讨http://www.madore.org/~david/...

EINTR/EINPROGRESS/EALREADY代表的状况

If connect() is interrupted by a signal that is caught while blocked waiting to establish a connection, connect() shall fail and set connect() to [EINTR], but the connection request shall not be aborted, and the connection shall be established asynchronously.

If the connection cannot be established immediately and O_NONBLOCK is set for the file descriptor for the socket, connect() shall fail and set errno to [EINPROGRESS], but the connection request shall not be aborted, and the connection shall be established asynchronously. Subsequent calls to connect() for the same socket, before the connection is established, shall fail and set errno to [EALREADY].

When the connection has been established asynchronously, select() and poll() shall indicate that the file descriptor for the socket is ready for writing.

read(只波及套接字)

errno含意可能状况致命解决
EAGAIN/EWOULDBLOCK操作将会阻塞(非阻塞)Buffer空重试
EBADFfd不可用fd已敞开或参数非法
EFAULT地址谬误buffer超出用户空间
EINTR调用中断调用被信号中断重试
EINVAL参数谬误fd不可读

write(只波及套接字)

errno含意可能状况致命解决
EAGAIN/EWOULDBLOCK操作将会阻塞(非阻塞)Buffer已满或闲暇空间有余重试
EBADFfd不可用fd已敞开或参数非法
EDESTADDRREQ须要目标地址套接字未连贯
EFAULT地址谬误buffer超出用户空间
EINTR调用中断调用被信号中断重试
EINVAL参数谬误
EPIPE管道谬误向读敞开的一端写,见写RST套接字