关于tcp:TCPsocket异常情况

13次阅读

共计 4084 个字符,预计需要花费 11 分钟才能阅读完成。

在 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代表的状况

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 空 重试
EBADF fd 不可用 fd 已敞开或参数非法
EFAULT 地址谬误 buffer 超出用户空间
EINTR 调用中断 调用被信号中断 重试
EINVAL 参数谬误 fd 不可读

write(只波及套接字)

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