1、问题
- 对方连接断掉了我知道吗?
- 连接超时设置多少合适?依据是什么?可以有一个统一值吗?
- 如何建立一个连接?
- 什么是长连接什么是短连接?
- 如何使用一个连接?
2、TCP连接
什么是一个TCP连接?机器A通过一个IP和端口与机器B的一个IP和端口通过TCP的三次握手建立的逻辑联系,用于可靠数据传输。
特点
- 占用一个端口;
- 可以用netstat -ntlp 进行查看(windows下 “netstat -qn -p TCP”);
- 有心跳,linux下默认的心跳间隔时间为2小时。
3、如何建立一个连接
Java代码:
Socket socket = new Socket();
SocketAddress remoteAddr = new InetSocketAddress("61.144.222.17" ,22);
socket.connect(remoteAddr);
4、如何使用一个连接
建立连接之后有什么用?一般情况都是获取数据。一般情况获取数据都是发送一个请求,对方(一般是提供服务者,即服务器)回一个响应,响应里包含了要获取的数据。
4.1 写
即发送请求。Java代码:
OutputStream os = socket.getOutputStream();
byte b[] = {'a' , 'b' , 'c'};
os.write(b);
4.2 读
即获取服务器的响应。注意:读的前提是服务器要把数据写入了连接,客户端才有数据可读,这时读才会返回,否则读会一直等待。
Java代码:
InputStream is = socket.getInputStream();
is.read(b);
有没有不是一个请求一个响应的连接?心跳、tcp连接的直播、老式论坛等。
4.3 高级篇(常见的url请求、执行sql语句、获取redis数据是如何使用链接的)
上图是Http客户端的一种设计,目的是想说明我们日常用得最多的http协议类,与连接的关系。
说明:
- Java已经有socket类了,PHP和c需要自己用内置socket函数实现Socket类。
- 连接类就是包含了socket实例,拥有连接、断连、读写功能的类。符合上面对连接作用的描述。
- 而HttpClient就是一个连接,只不过实现了Http协议。其实外部使用者可以直接传入一个HttpRequest对象给execute方法,execute方法会调用HttpRequest的serialize方法把http协议的header、body序列化成字符串,通过Connection的write发送给服务器。然后调用Connection的read方法,读取到服务器的响应字符串,再调用HttpResponse的Unserialize静态方法把字符串反序列化为HttpResponse对象。
- 而我们常见的CURL,又是对HttpClient的封装,简化了我们调用的代码。
5、超时时间
5.1 连接超时时间
为什么会有连接超时?因为TCP协议三次握手的第一次由客户端发送syn数据包,就开始等待服务器的响应,这时如果服务器主机不存在,就没有人回复ack,这是tcp协议定义的通信机制。这时,只有客户端自己根据时间判断是否这个syn包真的丢失了,或者说你(业务)能容忍的超时时间。
超时时间的设置,一般为客户端与服务器之间的网络延迟时间(一个来回),再适当放大几毫秒。网络延迟可以通过ping获得,因为ping是一个来回,TCP协议连接时的三次握手也是一个来回的时间(客户端回完ack包之后,就标志连接已经建立,所以其实只有一个来回的数据传输)。
比如在windows上北京ping深圳的主机,获得“往返行程的估计时间(以毫秒为单位):最短 = 37ms,最长 = 37ms,平均 = 37ms”,比较稳定,那连接超时可以设置为42ms;
5.2 写超时时间
Java里没有写超时时间。
5.4 读超时时间
5.4.1 为什么会有读超时时间?
- 网络传输有延时;
- 上面说的读的前提是服务器把数据写入连接,如果服务器写数据花了很长时间,或者很长时间之后再写,甚至服务器程序出错,都可能导致连接无数据可读,最终导致读超时;
- 网络抖动导致连接丢失。这是最常见也是最严重的情况,如果这种情况的连接丢失,客户端是感知不到的(直接拔网线,客户端也无法感知),所以客户端会一直在读等待,直到超时。如果没有设置读超时,也没有更改默认的tcp心跳时间,那这次读操作将在2小时(linux默认的tcp心跳是2小时)后才会返回错误(类似SocketErrorException)。
- 读超时时间的上层表现可能是请求超时时间(上例设计中的HttpClient),数据库的SQL语句执行时间等;
5.4.2 读超时设置多少合适?
根据业务制定。比如app 5s就弹出超时提示,不再等待服务器响应了,那么nginx的requestTimeout就设置为5s;如果是对redis的读,因为redis本身很快,就可以设置100ms(不能存储大对象);
5.4.3 为什么要设置超时时间?
如果不设置连接超时时间,当主机ip不存在(未开机,ping不通)或网络包丢失,就会导致花在连接上的时间会很长,无法快速进行重试(切换到下一个可用机器上),也无法快速失败。这样会导致占用大量连接或线程资源。
如果不设置读超时时间,会导致占用连接资源,占用线程资源。最常见的危害是占用线程资源,比如处理任务的最大线程数是20,如果每个任务都有访问其他网络资源,当网络发生抖动导致连接丢失,如果这时20个线程刚发生读请求,就会一直处于等待状态,线程无法结束,就无法接收新任务,导致服务夯死,最中还导致Listen Drop。
5.4.4 系统默认超时时间是多少?
- 连接超时时间linux系统为60s;
- 系统没有读超时时间默认值。但在网络断链无感知的情况下,取决于tcp心跳,默认是2小时。
作者:
轻易科技研发中心-金融研发中心-游克江