传输层为其上面的应用层提供通讯服务,它属于面向通信的最高层,也属于面向用户应用的最底层,所以非常重要!
ip层作用于主机间(定位至主机),而传输层作用于应用进程间(定位至端口),端到端通信就是指两个应用进程间的通信。发送端中应用层传来的报文由传输层统一处理,接收端中传输层传来的报文会分发给不同的应用程序。
传输层使用16位来标识一个端口(共有65535个端口可用),这里的端口是一个逻辑结构,与路由器的物理端口不一样。该端口只在本计算机中有效,在互联网中是无意义的,它用于联系运输层和应用层(不使用进程号做联系是因为不同os的进程号规范可能不一样,而且一个进程可能开有多个端口)。
tcp是面向字节流的,应用传下来的的报文,tcp并不认识,tcp只将其看做是字节流,如果字节流大了就分段,如果小了可以先缓存等累计到一定大小后再发送。接收方tcp同样只认识字节流,将收到的字节流传至应用进程。(这需要在应用层区分字节流了,比如http通常会有length首部)
TCP报文段的首部格式
ACK=1表示该报文段包含确认序号(ack),只有在tcp三次握手的首次请求中ACK=0;
SYN=1表示该报文段用于同步序号,只有在tcp三次握手的前两次握手中SYN=1(两次握手之后,双方就会各持有一对序号,一个是自己的,另一个是对端的);
FIN=1表示该报文段为终止报文段,即向对端表示我不再发送数据给你了(但我还可以接收你发送的数据);
窗口字段表示当前可接收的最大字节数,是动态变化的;
确认号ack表示已收到对端到ack-1为止的所有数据;
客户端状态的变迁:关闭->同步已发送->连接已建立;
服务端状态的变迁:关闭->监听->同步已接受->连接已建立;
通常都是客户端主动请求
客户端状态的变迁:连接已建立->终止等待-1->终止等待-2->时间等待->关闭
服务端状态的变迁:连接已建立->关闭等待->最后确认->关闭
以java socket编程为例,serverSocket监听8181,clientSocket绑定8282,clientSocket.close后双方tcp的状态如下图(可通过netstat -ano查看),serverSocket处于CLOSE_WAIT,clientSocket处于FIN_WAIT_2。此时,对于java Socket来说,clientSocket已不能再发送数据(应用程序将clientSocket标记为closed,os Socket底层也关闭了发送通道),serverSocket读取数据只会读到-1,虽然可以发送数据,但clientSocket已经无法再接收了(因为被标记为closed,可能其他语言此时还可以接收数据)。也就是说,对于java而言,一端关闭了,另一端就会只有关闭的份了,不能再传输数据了。
当serverSocket.close后,serverSocket持有的临时端口被关闭,只剩下处于TIME_WAIT状态的clientSocket的8282端口,经测试TIME_WAIT状态会持续很长时间,所以通常建议client端主动进行关闭,如果server端主动关闭,会存在大量的端口处于TIME_WAIT状态,剩余可用端口就变少了。
(如果长时间维持clientSocket关闭、serverSocket开启的状态,即client为FIN_WAIT_2,server为CLOSE_WAIT,最后二者都会关闭,这是什么原因呢?)
CLOSE_WAIT、FIN_WAIT_2、TIME_WAIT这三个状态较容易捕捉,其他状态都是瞬间状态,不容易捕捉到。