Android开发应该知道的网络知识之Tcp
Tcp属于操作系统的协议,他位于应用层(http,ftp等)的下面一层。我们常说tcp是面向连接的,因为他能控制数据发送的节奏,包括发送速度、发送顺序、重新发送等。网络编程的抽象是socket(套接字),他的实体是通信操作的控制信息,如ip地址、端口号、进行状态等。http的请求信息就是通过socket带给了tcp。那么接下来我们先来讲讲两者的关系。
创建socket
首先创建socket,与此同时会分配一块内存空间,写入初始状态(描述符)。
连接socket
接着调用connect双方的socket开始连接。socket创建了以后并不知道通信对象是谁,所以需要把目标ip、端口号等信息告诉tcp。但是光这样还不够,我们还得让对方知道自己的存在,这就是大名鼎鼎的三次握手。那么三次握手的意义是什么呢?第一次握手,客户端向服务端发送同步控制位syn标记为1,表示发起握手,同时带上自己的控制信息;第二次握手,服务端向客户端发送同步控制位syn标记为1,表示服务端也发起握手,还有确认控制位ack标记为1,表示确认收到了客户端发起的握手,同时带上自己的控制信息;第三次握手,客户端发送确认控制位ack标记为1,表示确认收到了服务端发起的握手。由于互联网比较复杂,丢包是很正常的事,所以三次握手相当于正式通信前做个确认,确认双方都有通信的意愿并且能正常通信。那么控制信息又是什么呢?控制信息位于tcp头,包括发送方端口号、接收方端口号、发送序号、ack号(注意这个和上面i你说的ack控制位不一样)、数据偏移量、窗口等。注意,发送方和接收方的ip地址并不位于tcp头,而是在ip头,封包的时候是一层层往上添加头部的,在tcp层加了tcp头后面到了ip层再加ip头,再到mac层加mac头。那么这一大堆控制信息是干嘛用的呢?其实不光是三次握手的时候需要带控制信息,正式接收数据的时候也要带,所以下面说数据接收的时候一起分析。
发送数据
当调用socket的write写入二进制流时,我们的数据发送就开始了。然后开始委托tcp,而tcp并不会马上开始发送,而是会将数据放在缓存中。为什么?因为他要控制数据包的发送速度。那又如何控制呢?tcp有一套自己的算法,首先需要考虑包能容纳的长度,称为MTU,减去头部就是能容纳的实际数据的长度称为MSS。tcp会尽量凑满最大容纳长度,以避免频繁发送大量小包。打个比方,坐过黑车的都知道,司机会骗你上车说马上走,其实为了降低成本会一直等到车满才开。其次还要考虑时间问题,如果总是等到装满了再发,那么网络通信的延迟将奇高无比,所以时间上到了一个阈值就会发送。
到目前为止,有些问题并没有解决。我们知道tcp有重发功能,那么他怎么知道什么时候需要重发呢?又是哪些数据需要重发呢?这就需要给我们的包编号,每个包都需要在头部定义发送序号。然后接收方根据包的长度计算出结束值。比如当前包的序号是1,长度是1000字节,那么第二个包的序号就是1001,如果接收方收到第二个包的序号不是1001,说明丢包了。那么接收方如何通知发送方需要重发呢?又是需要重发哪个包呢?
这就需要接收方发送tcp头的ack号,比如上面的例子,第一个包的ack号应该是1001。当发送方收到1001的ack号,就知道第一个包对方已经接收成功了。那么序号的初始值是多少呢?是根据当前时间随机计算的,他被放在了三次握手的tcp头部进行双方交换。
tcp每次发送过的包都会放在缓冲区中,如果确认对方收到了才会从缓冲区中删除。而接收方的tcp也会将收到的包放在缓冲区中,拿出一部分来处理,处理完了再继续从缓冲区中取。这就会产生一种情况,发送的速度比接收方处理的速度快,接收方的缓冲区满出来了。为了避免这种情况,接收方必须告诉发送方,当前缓冲区还剩多少容量。这被称为窗口。那么什么时候更新窗口大小呢?我们来举个例子。假设在连接的时候,接收方告诉发送方窗口大小为1000,那么发送方发送了1个200的包和1个300的包以后,窗口大小为1000-200-300=500,发送方可以自己计算,所以这两次发送包都无需接收方告知。然后接收方从缓冲区取了200交给应用程序去处理,那么这时候窗口大小变为了700,这个过程发送方是无法获知的,所以这时候就需要告知发送方窗口大小。
接收数据
接收方调用socket的read方法就能读取从tcp过来的二进制流。首先tcp从缓冲区中取出数据,并将数据包按顺序链接起来还原出原始的数据,最后数据就交到我们应用程序了。
删除socket
这个过程其实就是四次挥手。客户端先发包,tcp头中带结束标志位fin为1,表示客户端要断开连接;服务端回发给客户端,tcp头中带标志位ack,表示服务端已经收到,而客户端收到ack后处于等待状态;服务端发给客户端,tcp头中带标志位fin为1,表示服务端要断开连接;客户端收到最后发包回应服务端,已收到断开连接的请求。
由于tcp安全可靠的特性,所以http用的就是tcp协议,另外比如流媒体协议rtsp也是。但是有些场景并不需要这么可靠,比如直播,因为实时性比较强,所以如果数据丢了就丢了,直接跳过就行,这时候可以用UDP协议。