在Unix下进行网络编程时,因为网络并不是彻底可靠,会遇到各类协议主流程外发生的各类错误。而健壮的程序必须考虑到这些错误并正确处理,所以这里总结网络编程中可能发生的常见错误。html
在握手,挥手以及消息传递的状态下,若当前发送的报文期待一个应答报文。在规定时间应答报文没有到达,发送方会重发两次报文(报文重发间隔可设置)。若重发三次后依旧没有收到应答,则向应用返回ETIMEOUT编程
若报文在传送的过程当中,由于找不到路由路径,报文没法到达等引起了ICMP错误,发送方会按照上述的方式重发次报文。若重发结束后依旧没有收到应答,则向应用返回EHOSTUNREACH或者ENETUNREACHapi
在IPV4中,报文超过了MTU长度会致使分片,而其存在DF(Don't Fragment)标识,表示禁止分片。同时IPV6禁止路由器分片,所以在传送的过程当中隐含DF位。在传送过程当中设置了DF位而超过了MTU,则会向应用返回EMSGSIZE缓存
在系统执行慢系统调用(可能被永远阻塞的系统调用)时阻塞,此时捕获到某个信号并进行了处理(系统对某些信号有默认处理方式),在没有设置自动重启的状况下,会向应用返回EINTR。服务器
通常应对EINTR的方式是简单的从新调用,但在connect返回EINTR时不能这么作,由于connect涉及三次握手的过程,须要使用getsockopt获取链接状态。网络
在调用read等阻塞时接收到对端RST信号时,会返回ECONNREST。同时对发送方断开的套接字写时也会返回ECONNRESETsocket
当进程向收到RST的套接字执行写操做的时候,内核向该进程发送一个SIGPIPE信号,该信号的默认行为是终止进程,所以进程必须捕获它以避免不情愿的被终止async
不论进程是捕捉了该信号并从信号处理函数中返回,仍是简单忽略该信号,写操做都讲返回EPIPE错误函数
在客户端第一次握手时,若服务端返回RST报文,当即向应用返回ECONNREFUSEDspa
在较为繁忙的服务器中,可能出现上图客户端刚经历三次握手后随机发送RST报文的状况,posix指出这种状况errno设置为ECONNABORTED,只须要再次调用accept便可。
而在Berkeley的实现中,返回EPROTO错误,表明协议错误,是一种致命错误,由内核把该链接从已完成链接套接字队列中释放,若再次调用accept,则不会处理到本次请求,可能致使阻塞。
因为客户端没法收到服务端的任何回应,会重发处理,最终向应用返回的状况可能为应答超时或目的不可达。
若想尽快的检测出主机崩溃,不主动发送数据也可作到,即套接字选项的SO_KEEPALIVE(相似心跳机制)
服务端因为进程崩溃或者手动kill后,进程终止关闭全部打开的描述符。这致使了其向客户端发送了一个FIN,客户端则响应了一个ack,TCP挥手的前半部分完成,服务端不在发送数据。
可是此时客户端并不知道服务器端已经终止了。当客户端向服务器写数据的时候,因为服务器进程终止,因此响应了RST。
这种状况下能够由select或者poll检测到服务端的终止。
因为重启后已经丢失了套接字链接信息,服务端对客户端的报文响应RST。若此时客户端读,则会返回ECONNRESET
errno | 含义 | 可能状况 | 致命 | 解决 |
---|---|---|---|---|
EACCES | 权限不足 | √ | ||
EAFNOSUPPORT | 地址族不支持 | 参数错误 | √ | |
EINVAL | 参数错误 | 未知协议或协议族不可用 | √ | |
EMFILE | 打开的文件过多 | 进程级打开的fd达上限 | ulimit -n 调整或等待资源释放 | |
ENFILE | 打开的文件过多 | 系统级打开的fd达上限 | 等待资源释放 | |
ENOBUFS/ENOMEM | 内存不足 | Buffer或文件表没法建立 | 等待资源释放 |
errno | 含义 | 可能状况 | 致命 | 解决 |
---|---|---|---|---|
EACCES | 权限不足 | 申请保留端口且非root运行 | √ | |
EADDRINUSE | 地址已被使用 | 绑定已使用地址或申请临时端口时已占满 | 临时端口已满时重试 | |
EBADF | fd不可用 | fd已关闭或参数非法 | √ | |
EINVAL | 参数错误 | 套接字重复绑定或参数错误 | √ | |
ENOTSOCK | 非套接字 | fd参数错误 | √ |
errno | 含义 | 可能状况 | 致命 | 解决 |
---|---|---|---|---|
EADDRINUSE | 地址已被使用 | 监听已监听端口或未bind至肯定端口而申请临时端口时已占满 | 临时端口已满时重试 | |
EBADF | fd不可用 | fd已关闭或参数非法 | √ | |
ENOTSOCK | 非套接字 | fd参数错误 | √ | |
EOPNOTSUPP | 操做不支持 | 只支持SOCK_STREAM类套接字(TCP) | √ |
errno | 含义 | 可能状况 | 致命 | 解决 |
---|---|---|---|---|
EWOULDBLOCK/EAGAIN | 资源暂时不可用 | (非阻塞)队列中无完成握手的链接 | 重试或处理其余事务 | |
EBADF | fd不可用 | fd已关闭或参数非法 | √ | |
ECONNABORTED | 链接已中断 | 见握手结束客户端RST | 重试或处理其余事务 | |
EFAULT | 地址错误 | addr参数没有指向用户可写空间 | √ | |
EINTR | 调用中断 | 调用被信号中断 | 重试或处理其余事务 | |
EINVAL | 参数错误 | 套接字未在监听态或addrlen/flags错误 | √ | |
EMFILE | 打开的文件过多 | 进程级打开的fd达上限 | ulimit -n 调整或等待资源释放 | |
ENFILE | 打开的文件过多 | 系统级打开的fd达上限 | 等待资源释放 | |
ENOMEM/ENOBUFS | 内存不足 | 套接字buffer内存不足而非系统内存不足 | 等待资源释放 |
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.
errno | 含义 | 可能状况 | 致命 | 解决 |
---|---|---|---|---|
EAGAIN/EWOULDBLOCK | 操做将会阻塞 | (非阻塞)Buffer空 | 重试 | |
EBADF | fd不可用 | fd已关闭或参数非法 | √ | |
EFAULT | 地址错误 | buffer超出用户空间 | √ | |
EINTR | 调用中断 | 调用被信号中断 | 重试 | |
EINVAL | 参数错误 | fd不可读 | √ |
errno | 含义 | 可能状况 | 致命 | 解决 |
---|---|---|---|---|
EAGAIN/EWOULDBLOCK | 操做将会阻塞 | (非阻塞)Buffer已满或空闲空间不足 | 重试 | |
EBADF | fd不可用 | fd已关闭或参数非法 | √ | |
EDESTADDRREQ | 须要目的地址 | 套接字未链接 | √ | |
EFAULT | 地址错误 | buffer超出用户空间 | √ | |
EINTR | 调用中断 | 调用被信号中断 | 重试 | |
EINVAL | 参数错误 | √ | ||
EPIPE | 管道错误 | 向读关闭的一端写,见写RST套接字 | √ |