socket read 阻塞解决error:Error:read ECONNRESET.怎么回事

12226人阅读
socket错误码:
阻塞的操作被取消阻塞的调用打断。如设置了发送接收超时,就会遇到这种错误。
只能针对阻塞模式的socket。读,写阻塞的socket时,-1返回,错误号为INTR。另外,如果出现EINTR即errno为4,错误描述 Interrupted system call,操作也应该继续。如果recv的返回值为0,那表明连接已经断开,接收操作也应该结束。
ETIMEOUT:110
1、操作超时。一般设置了发送接收超时,遇到网络繁忙的情况,就会遇到这种错误。
2、服务器做了读数据做了超时限制,读时发生了超时。
3、错误被描述为“connect time out”,即“连接超时”,这种情况一般发生在服务器主机崩溃。此时客户 TCP 将在一定时间内(依具体实现)持续重发数据分节,试图从服务 TCP 获得一个 ACK 分节。当最终放弃尝试后(此时服务器未重新启动),内核将会向客户进程返回 ETIMEDOUT 错误。如果某个中间路由器判定该服务器主机已经不可达,则一般会响应“destination unreachable”-“目的地不可达”的ICMP消息,相应的客户进程返回的错误是 EHOSTUNREACH 或ENETUNREACH。当服务器重新启动后,由于
TCP 状态丢失,之前所有的连接信息也不存在了,此时对于客户端发来请求将回应 RST。如果客户进程对检测服务器主机是否崩溃很有必要,要求即使客户进程不主动发送数据也能检测出来,那么需要使用其它技术,如配置 SO_KEEPALIVE Socket 选项,或实现某些心跳函数。
1、Send返回值小于要发送的数据数目,会返回EAGAIN和EINTR。
2、recv 返回值小于请求的长度时说明缓冲区已经没有可读数据,但再读不一定会触发EAGAIN,有可能返回0表示TCP连接已被关闭。
3、当socket是非阻塞时,如返回此错误,表示写缓冲队列已满,可以做延时后再重试.
4、在Linux进行非阻塞的socket接收数据时经常出现Resource temporarily unavailable,errno代码为11(EAGAIN),表明在非阻塞模式下调用了阻塞操作,在该操作没有完成就返回这个错误,这个错误不会破坏 socket的同步,不用管它,下次循环接着recv就可以。对非阻塞socket而言,EAGAIN不是一种错误。
1、Socket 关闭,但是socket号并没有置-1。继续在此socket上进行send和recv,就会返回这种错误。这个错误会引发SIGPIPE信号,系统会将 产生此EPIPE错误的进程杀死。所以,一般在网络程序中,首先屏蔽此消息,以免发生不及时设置socket进程被杀死的情况。
2、write(..) on a socket that has been closed at the other end will cause a SIGPIPE.
3、错误被描述为“broken pipe”,即“管道破裂”,这种情况一般发生在客户进程不理会(或未及时处理)Socket 错误,继续向服务 TCP 写入更多数据时,内核将向客户进程发送 SIGPIPE 信号,该信号默认会使进程终止(此时该前台进程未进行 core dump)。结合上边的 ECONNRESET 错误可知,向一个 FIN_WAIT2 状态的服务 TCP(已 ACK 响应 FIN 分节)写入数据不成问题,但是写一个已接收了 RST 的 Socket 则是一个错误。
read(..) or write(..) on a locally closed socket will return EBADF
地址错误。
ECONNREFUSED:
1、拒绝连接。一般发生在连接建立时。
拔服务器端网线测试,客户端设置keep alive时,recv较快返回0, 先收到ECONNREFUSED (Connection refused)错误码,其后都是ETIMEOUT。
2、an error returned from connect(), so it can only occur in a client (if a client is defined as the party that initiates the connection
ECONNRESET:
1、在客户端服务器程序中,客户端异常退出,并没有回收关闭相关的资源,服务器端会先收到ECONNRESET错误,然后收到EPIPE错误。
2、连接被远程主机关闭。有以下几种原因:远程主机停止服务,重新启动;当在执行某些操作时遇到失败,因为设置了“keep alive”选项,连接被关闭,一般与ENETRESET一起出现。
3、远程端执行了一个“hard”或者“abortive”的关闭。应用程序应该关闭socket,因为它不再可用。当执行在一个UDP socket上时,这个错误表明前一个send操作返回一个ICMP“port unreachable”信息。
4、如果client关闭连接,server端的select并不出错(不返回-1,使用select对唯一一个socket进行non- blocking检测),但是写该socket就会出错,用的是send.错误号:ECONNRESET.读(recv)socket并没有返回错误。
5、该错误被描述为“connection reset by peer”,即“对方复位连接”,这种情况一般发生在服务进程较客户进程提前终止。当服务进程终止时会向客户 TCP 发送 FIN 分节,客户 TCP 回应 ACK,服务 TCP 将转入 FIN_WAIT2 状态。此时如果客户进程没有处理该 FIN (如阻塞在其它调用上而没有关闭 Socket 时),则客户 TCP 将处于 CLOSE_WAIT 状态。当客户进程再次向 FIN_WAIT2 状态的服务 TCP 发送数据时,则服务 TCP 将立刻响应 RST。一般来说,这种情况还可以会引发另外的应用程序异常,客户进程在发送完数据后,往往会等待从网络IO接收数据,很典型的如
read 或 readline 调用,此时由于执行时序的原因,如果该调用发生在 RST 分节收到前执行的话,那么结果是客户进程会得到一个非预期的 EOF 错误。此时一般会输出“server terminated prematurely”-“服务器过早终止”错误。
无效参数。提供的参数非法。有时也会与socket的当前状态相关,如一个socket并没有进入listening状态,此时调用accept,就会产生EINVAL错误。
打开了太多的socket。对进程或者线程而言,每种实现方法都有一个最大的可用socket数目处理,或者是全局的,或者是局部的。
EWOULDBLOCK:EAGAIN
资源暂时不可用。这个错误是从对非阻塞socket进行的不能立即结束的操作返回的,如当没有数据在队列中可以读时,调用recv。并不是 fatal错误,稍后操作可以被重复。调用在一个非阻塞的SOCK_STREAM socket 上调用connect时会产生这个错误,因为有时连接建立必须消耗一定的时间。
在一个没有建立连接的socket上,进行read,write操作会返回这个错误。出错的原因是socket没有标识地址。Setsoc也可能会出错。
ECONNRESET
&Connection reset by peer.
&连接被远程主机关闭。有以下几种原因:远程主机停止服务,重新启动;当在执行某些操作时遇到失败,因为设置了“keep alive”选项,连接被关闭,一般与ENETRESET一起出现。
ECONNABORTED
1、软件导致的连接取消。一个已经建立的连接被host方的软件取消,原因可能是数据传输超时或者是协议错误。
2、该错误被描述为“software caused connection abort”,即“软件引起的连接中止”。原因在于当服务和客户进程在完成用于 TCP 连接的“三次握手”后,客户 TCP 却发送了一个 RST (复位)分节,在服务进程看来,就在该连接已由 TCP 排队,等着服务进程调用 accept 的时候 RST 却到达了。POSIX 规定此时的 errno 值必须 ECONNABORTED。源自 Berkeley 的实现完全在内核中处理中止的连接,服务进程将永远不知道该中止的发生。服务器进程一般可以忽略该错误,直接再次调用accept。
& &当TCP协议接收到RST数据段,表示连接出现了某种错误,函数read将以错误返回,错误类型为ECONNERESET。并且以后所有在这个套接字上的读操作均返回错误。错误返回时返回值小于0。
ENETUNREACH
网络不可达。Socket试图操作一个不可达的网络。这意味着local的软件知道没有路由到达远程的host。
网络重置时丢失连接。
由于设置了&keep-alive&选项,探测到一个错误,连接被中断。在一个已经失败的连接上试图使用setsockopt操作,也会返回这个错误。
EINPROGRESS:
操作正在进行中。一个阻塞的操作正在执行。
ENOTSOCK:
在非socket上执行socket操作。
EDESTADDRREQ:
需要提供目的地址。
在一个socket上的操作需要提供地址。如往一个ADDR_ANY 地址上进行sendto操作会返回这个错误。
EMSGSIZE:
消息体太长。
发送到socket上的一个数据包大小比内部的消息缓冲区大,或者超过别的网络限制,或是用来接收数据包的缓冲区比数据包本身小。
EPROTOTYPE
协议类型错误。标识了协议的Socket函数在不支持的socket上进行操作。如ARPA Internet
UDP协议不能被标识为SOCK_STREAM socket类型。
ENOPROTOOPT
该错误不是一个 Socket 连接相关的错误。errno 给出该值可能由于,通过 getsockopt 系统调用来获得一个套接字的当前选项状态时,如果发现了系统不支持的选项参数就会引发该错误。
EPROTONOSUPPORT
不支持的协议。系统中没有安装标识的协议,或者是没有实现。如函数需要SOCK_DGRAM socket,但是标识了stream protocol.。
ESOCKTNOSUPPORT
Socket类型不支持。指定的socket类型在其address family中不支持。如可选选中选项SOCK_RAW,但实现并不支持SOCK_RAW sockets。
EOPNOTSUPP
&Operation not supported.
& The attempted operation is not supported for the type of object referenced. Usually this occurs when a socket descriptor to a socket that cannot support this operation, for example, trying to accept a connection on a datagram socket.
EPFNOSUPPORT
&Protocol family not supported.
&The protocol family has not been configured into the system or no implementation for it exists. Has a slightly different meaning to EAFNOSUPPORT, but is interchangeable in most cases, and all Windows Sockets functions that return one of these specify EAFNOSUPPORT.
EAFNOSUPPORT
&Address family not supported by protocol family.
&An address incompatible with the requested protocol was used. All sockets are created with an associated &address family& (i.e. AF_INET for Internet Protocols) and a generic protocol type (i.e. SOCK_STREAM). This error will be returned if an incorrect protocol
is explicitly requested in the socket call, or if an address of the wrong family is used for a socket, e.g. in sendto.
EADDRINUSE
&Address already in use.
&Only one usage of each socket address (protocol/IP address/port) is normally permitted. This error occurs if an application attempts to bind a socket to an IP address/port that has already been used for an existing socket, or a socket that wasn't closed properly,
or one that is still in the process of closing. For server applications that need to bind multiple sockets to the same port number, consider using setsockopt(SO_REUSEADDR). Client applications usually need not call bind at all - connect will choose an unused
port automatically. When bind is called with a wild-card address (involving ADDR_ANY), a EADDRINUSE error could be delayed until the specific address is &committed.& This could happen with a call to other function later, including connect, listen, Connect
or JoinLeaf.
EADDRNOTAVAIL
&Cannot assign requested address.
&The requested address is not valid in its context. Normally results from an attempt to bind to an address that is not valid for the local machine. This can also result from connect, sendto, Connect, JoinLeaf, or SendTo when the remote address or port is not
valid for a remote machine (e.g. address or port 0).
&Network is down.
&A socket operation encountered a dead network. This could indicate a serious failure of the network system (i.e. the protocol stack that the WinSock DLL runs over), the network interface, or the local network itself.
&No buffer space available.
&An operation on a socket could not be performed because the system lacked sufficient buffer space or because a queue was full.
&Socket is already connected.
&A connect request was made on an already connected socket. Some implementations also return this error if sendto is called on a connected SOCK_DGRAM socket (For SOCK_STREAM sockets, the to parameter in sendto is ignored), although other implementations treat
this as a legal occurrence.
连接过程可能出现的错误情况有:
(1) 如果客户机TCP协议没有接收到对它的SYN数据段的确认,函数以错误返回,错误类型为ETIMEOUT。通常TCP协议在发送SYN数据段失败之后,会多次发送SYN数据段,在所有的发送都高中失败之后,函数以错误返回。
注:SYN(synchronize)位:请求连接。TCP用这种数据段向对方TCP协议请求建立连接。在这个数据段中,TCP协议将它选择的初始 序列号通知对方,并且与对方协议协商最大数据段大小。SYN数据段的序列号为初始序列号,这个SYN数据段能够被确认。当协议接收到对这个数据段的确认之 后,建立TCP连接。
(2) 如果远程TCP协议返回一个RST数据段,函数立即以错误返回,错误类型为ECONNREFUSED。当远程机器在SYN数据段指定的目的端口号处没有服 务进程在等待连接时,远程机器的TCP协议将发送一个RST数据段,向客户机报告这个错误。客户机的TCP协议在接收到RST数据段后不再继续发送SYN 数据段,函数立即以错误返回。
注:RST(reset)位:表示请求重置连接。当TCP协议接收到一个不能处理的数据段时,向对方TCP协议发送这种数据段,表示这个数据段所标 识的连接出现了某种错误,请求TCP协议将这个连接清除。有3种情况可能导致TCP协议发送RST数据段:(1)SYN数据段指定的目的端口处没有接收进 程在等待;(2)TCP协议想放弃一个已经存在的连接;(3)TCP接收到一个数据段,但是这个数据段所标识的连接不存在。接收到RST数据段的TCP协 议立即将这条连接非正常地断开,并向应用程序报告错误。
(3) 如果客户机的SYN数据段导致某个路由器产生“目的地不可到达”类型的ICMP消息,函数以错误返回,错误类型为EHOSTUNREACH或 ENETUNREACH。通常TCP协议在接收到这个ICMP消息之后,记录这个消息,然后继续几次发送SYN数据段,在所有的发送都告失败之后,TCP 协议检查这个ICMP消息,函数以错误返回。
注:ICMP:Internet 消息控制协议。Internet的运行主要是由Internet的路由器来控制,路由器完成IP数据包的发送和接收,如果发送数据包时发生错误,路由器使 用 ICMP协议来报告这些错误。ICMP数据包是封装在IP数据包的数据部分中进行传输的,其格式如下:
0 8 16 24 31
类型:指出ICMP数据包的类型。
代码:提供ICMP数据包的进一步信息。
校验和:提供了对整个ICMP数据包内容的校验和。
ICMP数据包主要有以下类型:
(1) 目的地不可到达:A、目的主机未运行;B、目的地址不存在;C、路由表中没有目的地址对应的条目,因而路由器无法找到去往目的主机的路由。
(2) 超时:路由器将接收到的IP数据包的生存时间(TTL)域减1,如果这个域的值变为0,路由器丢弃这个IP数据包,并且发送这种ICMP消息。
(3) 参数出错:当IP数据包中有无效域时发送。
(4) 重定向:将一条新的路径通知主机。
(5) ECHO请求、ECHO回答:这两条消息用语测试目的主机是否可以到达。请求者向目的主机发送ECHO请求ICMP数据包,目的主机在接收到这个ICMP数据包之后,返回ECHO回答ICMP数据包。
(6) 时戳请求、时戳回答:ICMP协议使用这两种消息从其他机器处获得其时钟的当前时间。
&调用函数connect的过程中,当客户机TCP协议发送了SYN数据段的确认之后,TCP状态由CLOSED状态转为SYN_SENT状态,在接 收到对 SYN数据段的确认之后,TCP状态转换成ESTABLISHED状态,函数成功返回。如果调用函数connect失败,应该用close关闭这个套接字 描述符,不能再次使用这个套接字描述符来调用函数connect。
connect函数的出错处理:
(1)ETIMEOUT-connection timed out 目的主机不存在,没有返回任何相应,例如主机关闭
(2)ECONNREFUSED-connection refused(硬错)到达目的主机后,由于各种原因建立不了连接,主机返回RST(复位)响应,例如主机监听进程未启用,tcp取消连接等
(3)EHOSTTUNREACH-no route to host(软错)路由上引发了一个目的地不可达的ICMP错误
& 其中(1)(3),客户端会进行定时多次重试,一定次数后才返回错误。另外,当connect连接失败时,sockfd套接口不可用,必须关闭后重新socket分配才行。
getsockopt 和 setsockopt 还可能引发以下错误:
getsockopt/setsockopt(2) man page 写道
The getsockopt() and setsockopt() system calls will succeed unless:
[EBADF] The argument socket is not a valid file descriptor.
[EFAULT] The address pointed to by option_value is not in a valid part of the process dress space. For getsockopt(), this error may also be returned if option_len is not in a valid part of the process address space.
[EINVAL] The option is invalid at the level indicated.
[ENOBUFS]Insufficient memory buffers are available.
[ENOPROTOOPT] The option is unknown at the level indicated.
[ENOTSOCK] The argument socket is not a socket (e.g., a plain file).
The setsockopt() system call will succeed unless:
[EDOM] The argument option_value is out of bounds.
[EISCONN]socket is already connected and a specified option cannot be set while this is the case.
&&相关文章推荐
参考知识库
* 以上用户言论只代表其个人观点,不代表CSDN网站的观点或立场
访问:75967次
积分:1075
积分:1075
排名:千里之外
原创:31篇
转载:13篇
(3)(5)(1)(4)(7)(4)(1)(5)(1)(6)(1)(3)(3)我认为,想要熟练掌握Linux下的TCP/IP网络编程,至少有三个层面的知识需要熟悉:
1. TCP/IP协议(如连接的建立和终止、重传和确认、滑动窗口和拥塞控制等等)
2. Socket I/O系统调用(重点如read/write),这是TCP/IP协议在应用层表现出来的行为。
3. 编写Performant, Scalable的服务器程序。包括多线程、IO Multiplexing、非阻塞、异步等各种技术。
关于TCP/IP协议,建议参考Richard Stevens的《TCP/IP Illustrated,vol1》(TCP/IP详解卷1)。
关于第二层面,依然建议Richard&Stevens的《Unix network proggramming,vol1》(Unix网络编程卷1),这两本书公认是Unix网络编程的圣经。
至于第三个层面,UNP的书中有所提及,也有著名的,业界也有各种各样的框架和解决方案,本人才疏学浅,在这里就不一一敷述。
本文的重点在于第二个层面,主要总结一下Linux下TCP/IP网络编程中的read/write系统调用的行为,知识来源于自己网络编程的粗浅经验和对《Unix网络编程卷1》相关章节的总结。由于本人接触Linux下网络编程时间不长,错误和疏漏再所难免,望看官不吝赐教。
一. read/write的语义:为什么会阻塞?
先从write说起:
#include &unistd.h&ssize_t write(int fd, const void *buf, size_t count);
首先,write成功返回,只是buf中的数据被复制到了kernel中的TCP发送缓冲区。至于数据什么时候被发往网络,什么时候被对方主机接收,什么时候被对方进程读取,系统调用层面不会给予任何保证和通知。
write在什么情况下会阻塞?当kernel的该socket的发送缓冲区已满时。对于每个socket,拥有自己的send buffer和receive buffer。从Linux 2.6开始,两个缓冲区大小都由系统来自动调节(autotuning),但一般在default和max之间浮动。
# 获取socket的发送/接受缓冲区的大小:(后面的值是在我在Linux 2.6.38 x86_64上测试的结果)
sysctl net.core.wmem_default
#126976sysctl net.core.wmem_max    
#131071sysctl net.core.wmem_default
#126976sysctl net.core.wmem_max
已经发送到网络的数据依然需要暂存在send buffer中,只有收到对方的ack后,kernel才从buffer中清除这一部分数据,为后续发送数据腾出空间。接收端将收到的数据暂存在receive buffer中,自动进行确认。但如果socket所在的进程不及时将数据从receive buffer中取出,最终导致receive buffer填满,由于TCP的滑动窗口和拥塞控制,接收端会阻止发送端向其发送数据。这些控制皆发生在TCP/IP栈中,对应用程序是透明的,应用程序继续发送数据,最终导致send buffer填满,write调用阻塞。
一般来说,由于接收端进程从socket读数据的速度跟不上发送端进程向socket写数据的速度,最终导致发送端write调用阻塞。
而read调用的行为相对容易理解,从socket的receive buffer中拷贝数据到应用程序的buffer中。read调用阻塞,通常是发送端的数据没有到达。
二. blocking(默认)和nonblock模式下read/write行为的区别:
将socket fd设置为nonblock(非阻塞)是在服务器编程中常见的做法,采用blocking IO并为每一个client创建一个线程的模式开销巨大且可扩展性不佳(带来大量的切换开销),更为通用的做法是采用线程池+Nonblock I/O+Multiplexing(select/poll,以及Linux上特有的epoll)。
int&set_nonblocking(int&fd)
&&&&if&((flags = fcntl(fd, F_GETFL, 0)) == -1)
&&&&&&&&flags = 0;
&&&&return&fcntl(fd, F_SETFL, flags | O_NONBLOCK);
几个重要的结论:
1. read总是在接收缓冲区有数据时立即返回,而不是等到给定的read buffer填满时返回。
只有当receive buffer为空时,blocking模式才会等待,而nonblock模式下会立即返回-1(errno = EAGAIN或EWOULDBLOCK)
2. blocking的write只有在缓冲区足以放下整个buffer时才返回(与blocking read并不相同)
nonblock write则是返回能够放下的字节数,之后调用则返回-1(errno = EAGAIN或EWOULDBLOCK)
&对于blocking的write有个特例:当write正阻塞等待时对面关闭了socket,则write则会立即将剩余缓冲区填满并返回所写的字节数,再次调用则write失败(connection reset by peer),这正是下个小节要提到的:
三. read/write对连接异常的反馈行为:
对应用程序来说,与另一进程的TCP通信其实是完全异步的过程:
1. 我并不知道对面什么时候、能否收到我的数据
2. 我不知道什么时候能够收到对面的数据
3. 我不知道什么时候通信结束(主动退出或是异常退出、机器故障、网络故障等等)
对于1和2,采用write() -& read() -& write() -& read() -&...的序列,通过blocking read或者nonblock read+轮询的方式,应用程序基于可以保证正确的处理流程。
对于3,kernel将这些事件的“通知”通过read/write的结果返回给应用层。
假设A机器上的一个进程a正在和B机器上的进程b通信:某一时刻a正阻塞在socket的read调用上(或者在nonblock下轮询socket)
当b进程终止时,无论应用程序是否显式关闭了socket(OS会负责在进程结束时关闭所有的文件描述符,对于socket,则会发送一个FIN包到对面)。
”同步通知“:进程a对已经收到FIN的socket调用read,如果已经读完了receive buffer的剩余字节,则会返回EOF:0
”异步通知“:如果进程a正阻塞在read调用上(前面已经提到,此时receive buffer一定为空,因为read在receive buffer有内容时就会返回),则read调用立即返回EOF,进程a被唤醒。
socket在收到FIN后,虽然调用read会返回EOF,但进程a依然可以其调用write,因为根据TCP协议,收到对方的FIN包只意味着对方不会再发送任何消息。 在一个双方正常关闭的流程中,收到FIN包的一端将剩余数据发送给对面(通过一次或多次write),然后关闭socket。
但是事情远远没有想象中简单。优雅地(gracefully)关闭一个TCP连接,不仅仅需要双方的应用程序遵守约定,中间还不能出任何差错。
假如b进程是异常终止的,发送FIN包是OS代劳的,b进程已经不复存在,当机器再次收到该socket的消息时,会回应RST(因为拥有该socket的进程已经终止)。a进程对收到RST的socket调用write时,操作系统会给a进程发送SIGPIPE,默认处理动作是终止进程,知道你的进程为什么毫无征兆地死亡了吧:)
from 《Unix Network programming, vol1》 3rd Edition:
"It is okay to write to a socket that has received a FIN, but it is an error to write to a socket that has received an RST."
通过以上的叙述,内核通过socket的read/write将双方的连接异常通知到应用层,虽然很不直观,似乎也够用。
这里说一句题外话:
不知道有没有同学会和我有一样的感慨:在写TCP/IP通信时,似乎没怎么考虑连接的终止或错误,只是在read/write错误返回时关闭socket,程序似乎也能正常运行,但某些情况下总是会出奇怪的问题。想完美处理各种错误,却发现怎么也做不对。
原因之一是:socket(或者说TCP/IP栈本身)对错误的反馈能力是有限的。
考虑这样的错误情况:
不同于b进程退出(此时OS会负责为所有打开的socket发送FIN包),当B机器的OS崩溃(注意不同于人为关机,因为关机时所有进程的退出动作依然能够得到保证)/主机断电/网络不可达时,a进程根本不会收到FIN包作为连接终止的提示。
如果a进程阻塞在read上,那么结果只能是永远的等待。
如果a进程先write然后阻塞在read,由于收不到B机器TCP/IP栈的ack,TCP会持续重传12次(时间跨度大约为9分钟),然后在阻塞的read调用上返回错误:ETIMEDOUT/EHOSTUNREACH/ENETUNREACH
假如B机器恰好在某个时候恢复和A机器的通路,并收到a某个重传的pack,因为不能识别所以会返回一个RST,此时a进程上阻塞的read调用会返回错误ECONNREST
恩,socket对这些错误还是有一定的反馈能力的,前提是在对面不可达时你依然做了一次write调用,而不是轮询或是阻塞在read上,那么总是会在重传的周期内检测出错误。如果没有那次write调用,应用层永远不会收到连接错误的通知。
write的错误最终通过read来通知应用层,有点阴差阳错?
四. 还需要做什么?
至此,我们知道了仅仅通过read/write来检测异常情况是不靠谱的,还需要一些额外的工作:
1. 使用TCP的KEEPALIVE功能?
cat /proc/sys/net/ipv4/tcp_keepalive_time7200cat /proc/sys/net/ipv4/tcp_keepalive_intvl75cat /proc/sys/net/ipv4/tcp_keepalive_probes9
以上参数的大致意思是:keepalive routine每2小时(7200秒)启动一次,发送第一个probe(探测包),如果在75秒内没有收到对方应答则重发probe,当连续9个probe没有被应答时,认为连接已断。(此时read调用应该能够返回错误,待测试)
但在我印象中keepalive不太好用,默认的时间间隔太长,又是整个TCP/IP栈的全局参数:修改会影响其他进程,Linux的下似乎可以修改per socket的keepalive参数?(希望有使用经验的人能够指点一下),但是这些方法不是portable的。
2. 进行应用层的心跳
严格的网络程序中,应用层的心跳协议是必不可少的。虽然比TCP自带的keep alive要麻烦不少(怎样正确地实现应用层的心跳,我或许会用一篇专门的文章来谈一谈),但有其最大的优点:可控。
当然,也可以简单一点,针对连接做timeout,关闭一段时间没有通信的”空闲“连接。这里可以参考一篇文章:
参考资料:
《TCP/IP Illustrated, vol 1》 by Richard Stevens
《Unix Network Programming, vol 1》(3rd Edition) by Richard Stevens
(墙裂推荐)参考资料:
/zhang0j_21/blog/static/0/
/promise6522/archive//2377935.html
http://www.oschina.net/translate/tcp-keepalive-with-golang
/question/
/3workman/GoServer
https://my.oschina.net/yunfound/blog/141222
/golang-tcp-socket-adhere/
阅读(...) 评论()

我要回帖

更多关于 socket read 阻塞解决 的文章

 

随机推荐