赞
踩
错误号 | 错误 | 可能的原因 |
EAGAIN | Try again | 在读数据的时候,没有数据在底层缓冲的时候会遇到,一般的处理是循环进行读操作,异步模式还会等待读事件的发生再读 |
EWOULDBLOCK | Operation would block | 在我们的环境中和EAGAIN是一个值, 一般情况下只关心EAGAIN就可以了 |
EPIPE | Broken pipe | 接收端关闭(缓冲中没有多余的数据),但是发送端还在write. |
ECONNRESET | Connection reset by peer | 收到RST包 可能是 接收到数据后不进行读取或者没有读取完毕直接close,另一端再调用write或者read操作,这个时候需要检查一下是否存在脏数据或者一端某些情况下断开的情况. 另外 使用了SO_LINGER后close连接,另一端也会收到这个错误. 另外在epoll中一般也是可能返回EPOLLHUP事件。 连接的时候也可能出现这样的错误,这个参考后面的 "listen的时候的backlog有什么影响 "中的说明 |
EINTR | Interrupted system call | 被其他的系统调用中断了, 对于句柄进行操作比较容易出现,一般裸用recv都是需要判断的, 处理也很简单, 再进行一次操作就可以了 |
ETIMEDOUT | Connection timed out | 连接超时, 但是在我们ul_sread_xxx或者ul_swrite_xx系列中也被我们用来表示读写超时 |
ECONNREFUSED | Connection refused | 拒绝连接, 一般在机器存在但是相应的端口上没有数据的时候出现 |
ENETUNREACH | Network is unreachable | 网络不可达,可能是由于路器的限制不能访问,需要检查网络 |
EADDRNOTAVAIL | Cannot assign requested address | 不能分配本地地址,一般在端口不够用的时候会出现,很可能是短连接的TIME_WAIT问题造成 |
EADDRINUSE | Address already in use | 地址已经被使用, 已经有相应的服务程序占用了这个端口, 或者占用端口的程序退出了但没有设置端口复用 |
ENOTCONN | Transport endpoint is not connected | 连接没有链上。 在一个socket出来还没有accept或者connenct, 还有一种情况就是收到对方发送过来的RST包,系统已经确认连接被断开了 |
上面两者模型本质都是同步的处理业务逻辑,在一个线程中处理了读请求,业务逻辑和写回响应三个过程(很多业务更复杂,但是都是可以做相应的拆封的), 但是读和写这两个IO的处理往往需要阻塞等待, 这样造成了线程被阻塞, 如果要应付慢连接(比如外围抓取等待的时间是秒级的甚至更多), 在等待的时候其实CPU没有干多少事情, 这个时候就造成了浪费. 一种考虑是增加线程数,通过提高并发来解决这个问题, 但是我们目前的线程数还是有限的,不可能无限增加. 而且线程的增加会带来cpu对于上下文切换的代价,另一方面多个线程从一个队列中获取可用连接, 这里存在互斥线程多的时候会导致性能下降,当然这里可以通过把一个队列改多队列减少互斥来实现.
引入异步化的处理, 就是把对于IO的等待采用IO复用的方式,专门放入到一个或者若干个线程中去, 处理主逻辑的程序可以被释放出来, 只有在IO处理完毕才进行处理, 这样可以提高CPU的使用率,减少等待的时间. 一般情况下几个线程(一般和CPU的核数相当)可以应付很大的流量请求
对于有数据的活动连接放到异步队列中, 其他线程竞争这个队列获取句柄然后进行相关的操作. 由于accept是专门的线程进行处理, 出现被handle的情况比较少,不容易出现连接失败的情况.在大流量的情况下有一定的缓冲,虽然有些请求会出现延时,但只要在可以接受的范围内,服务还是可以正常进行. 一般来说队列的长度主要是考虑可以接受的延时程度.
这种模式也是我们现在许多服务比较常用的模型.可以不用关心客户端和服务的线程数对应关系,业务逻辑上也是比较简单的。
但这种模式在编程的时候,对于长连接有一个陷阱,判断句柄是否可读写以前一般采用的是select, 如果长连接的连接数比工作线程还少,当所有的连接都被处理了,有连接需要放回pool中,而这个时候如果正常建立连接的监听线程正好处于select状态,这个时候必须要等到 select超时才能重新将连接放入select中进行监听,因为这之前被放入select进行监听的处理socket为空,不会有响应,这个时候由于时间的浪费造成l长连接的性能下降。一般来说某个连接数少,某个连接特别活跃就可能造成问题. 过去的一些做法是控制连接数和服务端的工作线程数以及通过监听一个管道fd,在工作线程结束每次都激活这个fd跳出这次select来控制。现在的2.6内核中的epoll在判断可读写的时候不会存在这个问题(epoll在进行监听的时候,其它线程放入或者更改, 在epoll_wait的时候是可以马上激活的), 我们现在的服务多采用epoll代替select来解决这个, 但是主要的逻辑没有变化. ub_server中epool和public/ependingpool都是采用种模式。
同时启动多个线程, 每个线程都采用accept的方式进行阻塞获取连接(具体实现上一般是先select在accept, 一方面规避低内核的惊群效应,另一方面可以做到优雅退出).
多个线程竞争一个连接, 拿到连接的线程就进行自己的逻辑处理, 包括读写IO全部都在一个线程中进行. 短连接每次重新accept, 长连接,第一次的时候accept然后反复使用. 一般来说在总连接数很少的情况下效果会比较好,相对适用于少量短连接(可以允许比线程数多一些)和不超过线程总数的长连接(超过的那些连接,除非accept的连接断开,否则不可能会有线程对它进行accept).
但如果同一时候连接数过多会造成没有工作线程与客户端进行连接,客户端会出现大量的连接失败, 因为这个时候线程可能存在不能及时accept造成超时问题, 在有重试机制的情况下可能导致问题更糟糕. 有些程序在出现几次超时之后会长时间一直有连接超时往往就是在这种情况下发生的.
这种模型的最大优点在于编写简单, 在正常情况下工作效果不错。
在我们的环境中当网络触发broken pipe (一般情况是write的时候,没有write完毕, 接受端异常断开了), 系统默认的行为是直接退出。在我们的程序中一般都要在启动的时候加上 signal(SIGPIPE, SIG_IGN); 来强制忽略这种错误
严格来说, 交互的两端, 一端write调用write出去的长度, 接收端是不知道具体要读多长的. 这里有几个方面的问题
所以对于网络传输中 我们不能通过简单的read调用知道发送端在这次交互中实际传了多少数据. 一般来说对于具体的交互我们一般采取下面的方式来保证交互的正确.
总的来说read读数据的时候不能只通过read的返回值来判断到底需要读多少数据, 我们需要额外的约定来支持, 当这种约定存在错误的时候我们就可以认为已经出现了问题.
另外对于write数据来说, 如果相应的数据都是已经准备好了那这个时候也是可以把数据一次性发送出去,不需要调用了多次write. 一般来说write次数过多也会对性能产生影响,另一个问题就是多次连续可能会产生延时问题,这个参看下面有关长连接延时的部分问题.
小提示
上面提到的都是TCP的情况, 不一定适合其他网络协议. 比如在UDP中 接收到连续2个UDP包, 需要分别读来次才读的出来, 不能像TCP那样,一个read可能就可以成功(假设buff长度都是足够的)
另外可以采用valgrind来检查, valgrind参数中加上 --track-fds = yes 就可以看到最后退出的时候没有被关闭的句柄,以及打开句柄的位置
首先采用recv检查连接的是基于我们目前的一个请求一个应答的情况 对于客户端的请求,逻辑一般是这样 建立连接->发起请求->接受应答->长连接继续发请求
recv检查一般是这样采用下面的方式:
ret = recv(sock, buf, sizeof(buf), MSG_DONTWAIT);
通过判断ret 是否为-1并且errno是EAGAIN
在非堵塞方式下如果这个时候网络没有收到数据, 这个时候认为网络是正常的
这是由于在网络交换模式下 我们作为一个客户端在发起请求前, 网络中是不应该存在上一次请求留下来的脏数据或者被服务端主动断开(服务端主动断开会收到FIN包,这个时候是recv返回值为0), 异常断开会返回错误. 当然这种方式来判断连接是否存在并不是非常完善,在特殊的交互模式(比如异步全双工模式)或者延时比较大的网络中都是存在问题的,不过对于我们目前内网中的交互模式还是基本适用的.
这种方式和socket写错误并不矛盾, 写数据超时可能是由于网慢或者数据量太大等问题, 这时候并不能说明socket有错误, recv检查完全可能会是正确的.
一般来说遇到socket错误,无论是写错误还读错误都是需要关闭重连.
这个是正常现象, write数据成功不能表示数据已经被接收端接收导致,只能表示数据已经被复制到系统底层的缓冲(不一定发出), 这个时候的网络异常都是会造成接收端接收失败的.
在一些长连接的条件下, 发送一个小的数据包,结果会发现从数据write成功到接收端需要等待一定的时间后才能接收到, 而改成短连接这个现象就消失了(如果没有消失,那么可能网络本身确实存在延时的问题,特别是跨机房的情况下)
在长连接的处理中出现了延时,而且时间固定,基本都是40ms, 出现40ms延时最大的可能就是由于没有设置TCP_NODELAY
在长连接的交互中,有些时候一个发送的数据包非常的小,加上一个数据包的头部就会导致浪费,而且由于传输的数据多了,就可能会造成网络拥塞的情况, 在系统底层默认采用了Nagle算法,可以把连续发送的多个小包组装为一个更大的数据包然后再进行发送. 但是对于我们交互性的应用程序意义就不大了,在这种情况下我们发送一个小数据包的请求,就会立刻进行等待,不会还有后面的数据包一起发送, 这个时候Nagle算法就会产生负作用,在我们的环境下会产生40ms的延时,这样就会导致客户端的处理等待时间过长, 导致程序压力无法上去. 在代码中无论是服务端还是客户端都是建议设置这个选项,避免某一端造成延时
所以对于长连接的情况我们建议都需要设置TCP_NODELAY, 在我们的ub框架下这个选项是默认设置的.
小提示:
对于服务端程序而言, 采用的模式一般是
bind-> listen -> accept, 这个时候accept出来的句柄的各项属性其实是从listen的句柄中继承, 所以对于多数服务端程序只需要对于listen进行监听的句柄设置一次TCP_NODELAY就可以了,不需要每次都accept一次.
设置了NODELAY选项但还是时不时出现10ms(或者某个固定值)的延时
这种情况最有可能的就是服务端程序存在长连接处理的缺陷. 这种情况一般会发生在使用我们的pendingpool模型(ub中的cpool)情况下,在 模型的说明中有提到. 由于select没有及时跳出导致一直在浪费时间进行等待.
上面的2个问题都处理了,还是发现了40ms延时?
协议栈在发送包的时候,其实不仅受到TCP_NODELAY的影响,还受到协议栈里面拥塞窗口大小的影响. 在连接发送多个小数据包的时候会导致数据没有及时发送出去.
这里的40ms延时其实是两方面的问题:
对于发送端, 由于拥塞窗口的存在,在TCP_NODELAY的情况,如果存在多个数据包,后面的数据包可能会有延时发出的问题. 这个时候可以采用 TCP_CORK参数,
TCP_CORK 需要在数据write前设置,并且在write完之后取消,这样可以把write的数据发送出去( 要注意设置TCP_CORK的时候不能与TCP_NODELAY混用,要么不设置TCP_NODELAY要么就先取消TCP_NODELAY)
但是在做了上面的设置后可能还是会导致40ms的延时, 这个时候如果采用tcpdump查看可以注意是发送端在发送了数据包后,需要等待服务端的一个ack后才会再次发送下一个数据包,这个时候服务端出现了延时返回的问题.对于这个问题可以通过设置server端TCP_QUICKACK选项来解决. TCP_QUICKACK可以让服务端尽快的响应这个ack包.
这个问题的主要原因比较复杂,主要有下面几个方面
当TCP协议栈收到数据的时候, 是否进行ACK响应(没有响应是不会发下一个包的),在我们linux上返回ack包是下面这些条件中的一个
如果都不满足上面的条件,接收方会延时40ms再发送ACK, 这个时候就造成了延时。
但是对于上面的情况即使是采用TCP_QUICKACK,服务端也不能保证可以及时返回ack包,因为快速回复模式在一些情况下是会失效(只能通过修改内核来实现)
目前的解决方案只能是通过修改内核来解决这个问题,STL的同学在 内核中增加了参数可以控制这个问题。
会出现这种情况的主要是连接发送多个小数据包或者采用了一些异步双工的编程模式,主要的解决方案有下面几种
对于TIME_WAIT的出现具体可以参考<<UNIX网络编程>>中的章节,总的来说对于一个已经建立的连接如果是主动close, 那么这个连接的端口(注意:不是socket)就会进入到TIME_WAIT状态,在我们的机器上需要等待60s的时间(有些书上可能提到的是2MSL,1MSL为1分钟,但我们的linux实现是按照1分钟的). 在这一段时间内,这个端口将不会被释放,新建立的连接就无法使用这个端口(连接的时候会报Cannot assign requested address的错误).可以通过/proc/sys/net/ipv4/ip_local_port_range看到 可用端口的范围,我们的机器上一般是 32768 61000, 不足3W个,这样的结果就是导致如果出现500/s的短连接请求,就会导致端口不够用连接不上。 这种情况一般修改系统参数tcp_tw_reuse或者在句柄关闭前设置SO_LINGER选项来解决,也可以通过增大ip_local_port_range来缓解, 设置SO_LINGER后句柄会被系统立刻关闭,不会进入TIME_WAIT状态,不过在一些大压力的情况还是有可能出现连接的替身,导致数据包丢失。 系统参数/proc/sys/net/ipv4/tcp_tw_reuse设为1 会复用TIME_WAIT状态socket,如果开启,客户端在调用connect调用时,会自动复用TIME_WAIT状态的端口,相比SO_LINGER选项更加安全。 一般情况下还可以考虑打开/proc/sys/net/ipv4/tcp_tw_recycle, tcp_tw_recycle打开会去计算和动态改变每次TIME_WAIT的时间,而不是采用固定值,这样可以让一下处于TIME_WAIT的端口提前释放,不过在有些情况下会出现问题
对于服务器端如果出现TIME_WAIT状态,是不会产生端口不够用的情况,所有的连接都是用同一个端口的,从一个端口上分配出多个fd给程序accept出来使用。但是TIME_WAIT过多在服务器端还是会占用一定的内存资源, 在/proc/sys/net/ipv4/tcp_max_xxx 中我们可以系统默认情况下的所允许的最大TIME_WAIT的个数,一般机器上都是180000, 这个对于应付一般程序已经足够了.但对于一些压力非常大的程序而言,这个时候系统会不主动进入TIME_WAIT状态而且是直接跳过, 这个时候如果去看dmsg中的信息会看到 "TCP: time wait bucket table overflow" , 一般来说这种情况是不会产生太多的负面影响, 这种情况下后来的socket在关闭时不会进入TIME_WAIT状态,而是直接发RST包, 并且关闭socket. 不过还是需要关注为什么会短时间内出现这么大量的请求.
小提示: 如果需要设置SO_LINGER选项, 需要在FD连接上之后设置才有效果
一般来说,连接的一端在被动关闭的情况下,已经接收到FIN包(对端调用close)后,这个时候如果接收到FIN包的一端没有主动close就会出现CLOSE_WAIT的情况。 一般来说,对于普通正常的交互,处于CLOSE_WAIT的时间很短,一般的逻辑是检测到网络出错,马上关闭。
但是在一些情况下会出现大量的CLOS_WAIT, 有的甚至维持很长的时间, 这个主要有几个原因:
网络压力大的情况下,有时候会出现,发送端是按照顺序发送, 但是接收端接收的时候顺序不对.
一般来说在正常情况下是不会出现数据顺序错误的情况, 但某些异常情况还是有可能导致的.
在我们的协议栈中,服务端每次建立连接其实都是从accpet所在的队列中取出一个已经建立的fd, 但是在一些异常情况下,可能会出现短时间内建立大量连接的情况, accept的队列长度是有限制, 这里其实有两个队列,一个完成队列另一个是未完成队列,只有完成了三次握手的连接会放到完成队列中。如果在短时间内accept中的fd没有被取出导致队列变满,但未完成队列未满, 这个时候连接会在未完成队列中,对于发起连接的一端来说表现的情况是连接已经成功,但实际上连接本身并没有完成,但这个时候我们依然可以发起写操作并且成功, 只是在进行读操作的时候,由于对端没有响应会造成读超时。对于超时的情况我们一般就把连接直接close关闭了, 但是句柄虽然被关闭了,但是由于TIME_WAIT状态的存在, TCP还是会进行重传。在重传的时候,如果完成队列有句柄被处理,那么此时会完成三次握手建立连接,这个时候服务端照样会进行正常的处理(不过在写响应的时候可能会发生错误)。从接收上看,由于重传成功的情况我们不能控制,对于接收端来说就可能出现乱序的情况。 完成队列的长度和未完成队列的长度由listen时候的baklog决定((ullib库中ul_tcplisten的最后一个参数),在我们的linux环境中baklog是完成队列的长度,baklog * 1.5是两个队列的总长度(与一些书上所说的两个队列长度不超过baklog有出入). 两个队列的总长度最大值限制是128, 既使设置的结果超过了128也会被自动改为128。128这个限制可以通过系统参数 /proc/sys/net/core/somaxconn 来更改, 在我们 5-6-0-0 内核版本以后,STL将其提高到2048. 另外客户端也可以考虑使用SO_LINGER参数通过强制关闭连接来处理这个问题,这样在close以后就不启用重传机制。另外的考虑就是对重试机制根据业务逻辑进行改进。
主要几个方面的可能
当然还是有可能是由于网络异常或者跨机房耗时特别多产生的, 这些就不是用户态程序可以控制的。
另外还有发现有些程序采用epoll的单线模式, 但是IO并没有异步化,而是阻塞IO,导致了处理不及时.
backlog代表连接的队列, 这里对于内核中其实会维护2个队列
在我们的linux环境中backlog 一般是被定义为已完成队列的长度, 为完成队列一般是按照以完成队列长度的一半来取, backlog为5, 那么已完成队列为5,未完成队列为3, 总共是8个。 如果这里的8个都被占满了,那么后面的连接就会失败,这里的行为可以由 /proc/sys/net/ipv4/tcp_abort_on_overflow 参数控制, 这个参数打开后队列满了会发送RST包给client端,client端会看到Connection reset by peer的错误(线上部分内核打开了这个参数), 如果是关闭的话, 服务端会丢弃这次握手, 需要等待TCP的自动重连, 这个时间一般比较长, 默认情况下第一次需要3秒钟, 由于我们的连接超时一般都是很小的, client采用ullib库中的超时连接函数, 那么会发现这个时候连接超时了.
可能出现的问题:
只要有一端采用了短连接,那么就可以认为总体是短连接模式。
服务端长连接, 客户端短连接
客户端主动关闭, 服务端需要接收到close的FIN包, read返回0 后才知道客户端已经被关闭。在这一段时间内其实服务端多维护了一个没有必要连接的状态。在同步模式(pendingpool,ub-xpool, ub-cpool, ub-epool)中由于read是在工作线程中,这个连接相当于线程多做了一次处理,浪费了系统资源。如果是IO异步模式(ub/apool或者使用ependingpool读回调)则可以马上发现,不需要再让工作线程进行处理
服务端如果采用普通线程模型(ub-xpool)那么在异常情况下FIN包如果没有及时到达,在这一小段时间内这个处理线程不能处理业务逻辑。如果出现问题的地方比较多这个时候可能会有连锁反应短时间内不能相应。
服务端为长连接,对于服务提供者来说可能早期测试也是采用长连接来进行测试,这个时候accept的baklog可能设置的很小,也不会出现问题。 但是一旦被大量短连接服务访问就可能出现问题。所以建议listen的时候baklog都设置为128, 我们现在的系统支持这么大的baklog没有什么问题。
每次总是客户端主动断开,这导致客户端出现了TIME_WIAT的状态,在没有设置SO_LINGER或者改变系统参数的情况下,比较容易出现客户端端口不够用的情况。
服务端短连接,客户端长连接
这个时候的问题相对比较少, 但是如果客户端在发送数据前(或者收完数据后)没有对脏数据进行检查,在写的时候都会出现大量写错误或者读错误,做一次无用的操作,浪费系统资源
一般的建议是采用长连接还是短连接,两端保持一致, 但采用配置的方式并不合适,这个需要在上线的时候检查这些问题。比较好的方式是把采用长连接还是短连接放到数据包头部中。客户端发送的时候标记自己是采用短连接还是长连接,服务端接收到后按照客户端的情况采取相应的措施,并且告知客户端。特别的如果服务端不支持长连接,也可以告知客户端,服务采用了短连接
要注意的是,如果采用了一些框架或者库, 在read到0的情况下可能会多打日志,这个对性能的影响可能会比较大。
select默认情况下可以支持句柄数是1024, 这个可以看/usr/include/bits/typesizes.h 中的__FD_SETSIZE, 在我们的编译机(不是开发机,是SCMPF平台的机器)这个值已经被修改为51200, 如果select在处理fd超过1024的情况下出现问题可用检查一下编译程序的机器上__FD_SETSIZE是否正确.
epoll在句柄数的限制没有像select那样需要通过改变系统环境中的宏来实现对更多句柄的支持
另外我们发现有些程序在使用epoll的时候打开了边缘触发模式(EPOLLET), 采用边缘触发其实是存在风险的,在代码中需要很小心,避免由于连接两次数据到达,而被只读出一部分的数据. EPOLLET的本意是在数据情况发生变化的时候激活(比如不可读进入可读状态), 但问题是这个时候如果在一次处理完毕后不能保证fd已经进入了不可读状态(一般来说是读到EAGIN的情况), 后续可能就一直不会被激活. 一般情况下建议使用EPOLLET模式.一个最典型的问题就是监听的句柄被设置为EPOLLET, 当同时多个连接建立的时候, 我们只accept出一个连接进行处理, 这样就可能导致后来的连接不能被及时处理,要等到下一次连接才会被激活.
小提示: ullib 中常用的ul_sreado_ms_ex,ul_swriteo_ms_ex内部是采用select的机制,即使是在scmpf平台上编译出来也还是受到51200的限制,可用ul_sreado_ms_ex2,和ul_swriteo_ms_ex2这个两个接口来规避这个问题,他们内部不是采用select的方式来实现超时控制的(需要ullib 3.1.22以后版本)
另外就是对于内核中还有一个宏NR_OPEN会限制fd的做大个数,目前这个值是1024*1024
小提示: linux系统中socket句柄和文件句柄是不区分的,如果文件句柄+socket句柄的个数超过1024同样也会出问题,这个时候也需要limit提高句柄数.
ulimit对于非root权限的帐户而言只能往小的值去设置, 在终端上的设置的结果一般是针对本次shell的, 要还原退出终端重新进入就可以了.
理论上来说这个是可以非常多的,取决于可以使用多少的内存.我们的系统一般采用一个四元组来表示一个唯一的连接{客户端ip, 客户端端口, 服务端ip, 服务端端口} (有些地方算上TCP, UDP表示成5元组), 在网络连接中对于服务端采用的一般是bind一个固定的端口, 然后监听这个端口,在有连接建立的时候进行accept操作,这个时候所有建立的连接都只用到服务端的一个端口.对于一个唯一的连接在服务端ip和 服务端端口都确定的情况下,同一个ip上的客户端如果要建立一个连接就需要分别采用不同的端,一台机器上的端口是有限,最多65535(一个unsigned char)个,在系统文件/proc/sys/net/ipv4/ip_local_port_range 中我们一般可以看到32768 61000 的结果,这里表示这台机器可以使用的端口范围是32768到61000, 也就是说事实上对于客户端机器而言可以使用的连接数还不足3W个,当然我们可以调整这个数值把可用端口数增加到6W. 但是这个时候对于服务端的程序完全不受这个限制因为它都是用一个端口,这个时候服务端受到是连接句柄数的限制,在上面对于句柄数的说明已经介绍过了,一个进程可以建立的句柄数是由/proc/sys/fs/file-max决定上限和ulimit来控制的.所以这个时候服务端完全可以建立更多的连接,这个时候的主要问题在于如何维护和管理这么多的连接,经典的一个连接对应一个线程的处理方式这个时候已经不适用了,需要考虑采用一些异步处理的方式来解决, 毕竟线程数的影响放在那边
小提示: 一般的服务模式都是服务端一个端口,客户端使用不同的端口进行连接,但是其实我们也是可以把这个过程倒过来,我们客户端只用一个端但是服务端确是不同的端口,客户端做下面的修改
原有的方式 socket分配句柄-> connect 分配的句柄 改为 socket分配句柄 ->对socket设置SO_REUSEADDR选项->像服务端一样bind某个端口->connect 就可以实现
不过这种应用相对比较少,对于像网络爬虫这种情况可能相对会比较适用,只不过6w连接已经够多了,继续增加的意义不一定那么大就是了.
这个要根据情况来看, 一般情况connect一个不存在的ip地址,发起连接的服务需要等待ack的返回,由于ip地址不存在,不会有返回,这个时候会一直等到超时才返回。如果连接的是一个存在的ip,但是相应的端口没有服务,这个时候会马上得到返回,收到一个ECONNREFUSED(Connection refused)的结果。
但是在我们的网络会存在一些有限制的路由器,比如我们一些机器不允许访问外网,这个时候如果访问的ip是一个外网ip(无论是否存在),这个时候也会马上返回得到一个Network is unreachable的错误,不需要等待。
对于超时时间的设置相对比较复杂,取决于各种业务不同的逻辑
外网情况比较复杂, 这里主要针对内网小数据包的情况进行分析
对于大数据量,由于本身数据传输就存在大量时间消耗,可以适当的增加
这里有几种可能:
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。