在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 | 地址已被应用 | 绑定已应用地址或申请长期端口时已占满 | 长期端口已满时重试 | |
EBADF | fd不可用 | fd已敞开或参数非法 | √ | |
EINVAL | 参数谬误 | 套接字反复绑定或参数谬误 | √ | |
ENOTSOCK | 非套接字 | fd参数谬误 | √ |
listen
errno | 含意 | 可能状况 | 致命 | 解决 |
---|---|---|---|---|
EADDRINUSE | 地址已被应用 | 监听已监听端口或未bind至确定端口而申请长期端口时已占满 | 长期端口已满时重试 | |
EBADF | fd不可用 | fd已敞开或参数非法 | √ | |
ENOTSOCK | 非套接字 | fd参数谬误 | √ | |
EOPNOTSUPP | 操作不反对 | 只反对SOCK_STREAM类套接字(TCP) | √ |
accept
errno | 含意 | 可能状况 | 致命 | 解决 |
---|---|---|---|---|
EWOULDBLOCK/EAGAIN | 资源临时不可用 | (非阻塞)队列中无实现握手的连贯 | 重试或解决其余事务 | |
EBADF | fd不可用 | 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从新调用 | √ | |
EBADF | fd不可用 | 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
代表的状况
Ifconnect()
is interrupted by a signal that is caught while blocked waiting to establish a connection,connect()
shall fail and setconnect()
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 seterrno
to [EINPROGRESS
], but the connection request shall not be aborted, and the connection shall be established asynchronously. Subsequent calls toconnect()
for the same socket, before the connection is established, shall fail and seterrno
to [EALREADY
].When the connection has been established asynchronously,
select()
andpoll()
shall indicate that the file descriptor for the socket is ready for writing.
read(只波及套接字)
errno | 含意 | 可能状况 | 致命 | 解决 |
---|---|---|---|---|
EAGAIN/EWOULDBLOCK | 操作将会阻塞 | (非阻塞)Buffer空 | 重试 | |
EBADF | fd不可用 | fd已敞开或参数非法 | √ | |
EFAULT | 地址谬误 | buffer超出用户空间 | √ | |
EINTR | 调用中断 | 调用被信号中断 | 重试 | |
EINVAL | 参数谬误 | fd不可读 | √ |
write(只波及套接字)
errno | 含意 | 可能状况 | 致命 | 解决 |
---|---|---|---|---|
EAGAIN/EWOULDBLOCK | 操作将会阻塞 | (非阻塞)Buffer已满或闲暇空间有余 | 重试 | |
EBADF | fd不可用 | fd已敞开或参数非法 | √ | |
EDESTADDRREQ | 须要目标地址 | 套接字未连贯 | √ | |
EFAULT | 地址谬误 | buffer超出用户空间 | √ | |
EINTR | 调用中断 | 调用被信号中断 | 重试 | |
EINVAL | 参数谬误 | √ | ||
EPIPE | 管道谬误 | 向读敞开的一端写,见写RST套接字 | √ |