TCP报文格式
source
:发送TCP数据的源端口dest
:接受TCP数据的目的端口seq
:标识该TCP所包含的数据字节的开始序列号ack_seq
:确认序列号,表示接受方下一次接受的数据序列号。doff
:数据首部长度。和IP协议一样,以4字节为单位。一般的时候为5urg
:如果设置紧急数据指针,则该位为1ack
:如果确认号正确,那么为1psh
:如果设置为1,那么接收方收到数据后,立即交给上一层程序rst
:为1的时候,表示请求重新连接syn
:为1的时候,表示请求建立连接fin
:为1的时候,表示请求关闭连接window窗口,告诉接收者可以接收的大小check对TCP数据进行较核urg_ptr
:如果urg=1,那么指出紧急数据对于历史数据开始的序列号的偏移值
TCP连接建立三次握手
- 第一次握手:建立连接时,客户端发送syn包(seq=x)到服务器,并进入SYN_SEND状态,等待服务器确认;
- 第二次握手:服务器收到syn包,必须确认客户的SYN(ack=j+1),同时自己也发送一个SYN包(seq=y),即SYN+ACK包,此时服务器进入SYN_RECV状态;
- 第三次握手:客户端收到服务器的SYN+ACK包,向服务器发送确认包ACK(ack=y+1),此包发送完毕,客户端和服务器进入ESTABLISHED状态,完成三次握手。
- 完成三次握手,客户端与服务器开始传送数据,
TCP优缺点
优点
- TCP提供以认可的方式显式地创建和终止连接。
- TCP保证可靠的、顺序的(数据包以发送的顺序接收)以及不会重复的数据传输。
- TCP通过保持连续并将数据块分成更小的分片来处理大数据块。无需程序员知道
缺点
- TCP在传递数据时必须创建(并保持)一个连接。这个连接给通信进程增加了开销,让它比UDP速度要慢。TCP 连接的资源消耗,其中包括:数据包信息、条件状态、序列号等。
- TCP连接存在安全隐患:通过故意不完成建立连接所需要的三次握手过程,造成连接一方的资源耗尽
- 序列号的可预测性,目标主机应答连接请求时返回的SYN/ACK 的序列号是可预测的 TCP 会话劫持和SYN FLOOD(同步洪流)就是根据TCP的这个弱点出现的一种网络攻击方式
TCP 数据结构
在TCP连接建立的过程中,会用到一些数据结构
- 半连接队列:在三次握手协议中,服务器维护一个未连接队列,该队列为每个客户端的SYN包(syn=j)开设一个条目,该条目表明服务器已收到SYN 包,并向客户发出确认,正在等待客户的确认包。这些条目所标识的连接在服务器处于Syn_RECV状态,当服务器收到客户的确认包时,删除该条目,服务器进入ESTABLISHED状态。
- Backlog参数:表示未连接队列的最大容纳数目。
- SYN-ACK 重传次数:服务器发送完SYN-ACK包,如果未收到客户确认包,服务器进行首次重传,等待一段时间仍未收到客户确认包,进行第二次重传,如果重传次数超过系统规定的最大重传次数,系统将该连接信息从半连接队列中删除。注意,每次重传等待的时间不一定相同。
- 半连接存活时间:是指半连接队列的条目存活的最长时间,也即服务从收到SYN包到确认这个报文无效的最长时间,该时间值是所有重传请求包的最长等待时间总和。有时我们也称半连接存活时间为Timeout时间、SYN_RECV存活时间
TCP 标志位
应该就是code bit那个字段的位的意义
- SYN(同步标志):该标志置上的时候,同步序列编号(Synchronize Sequence Numbers)栏有效。该标志仅在三次握手建立TCP连接时有效。它提示TCP连接的服务端检查序列编号,该序列编号为TCP连接初始端(一般是客户端)的初始序列编号。在这里,可以把TCP序列编号看作是一个范围从0到4,294,967,295的32位计数器。通过TCP连接交换的数据中每一个字节都经过序列编号。在TCP报头中的序列编号栏包括了TCP分段中第一个字节的序列编号。
- ACK(确认标志):该标志置上的时候,确认编号(Acknowledgement Number)栏有效。大多数情况下该标志位是置位的。TCP报头内的确认编号栏内包含的确认编号(w+1,Figure-1)为下一个预期的序列编号,同时提示远端系统已经成功接收所有数据。
- RST(复位标志):该标志置上的时候,用于复位相应的TCP连接。
- URG(紧急标志):该标志置上的时候,表示紧急(The urgent pointer) 标志有效。
- PSH(推标志):该标志置位时,接收端不将该数据进行队列处理,而是尽可能快将数据转由应用处理。在处理 telnet 或 rlogin 等交互模式的连接时,该标志总是置位的。
- FIN(结束标志):带有该标志置位的数据包用来结束一个TCP回话,但对应端口仍处于开放状态,准备接收后续数据。
TCP连接过程的中间状态
- SYN_SENT:客户端发送syn数据包向服务器端申请建立TCP连接,此时客户端的状态为SYN_SENT
- SYN_RCVD:接收方收到请求,给发起方发送一个设置了SYN与ACK标志位的TCP数据包做为应答,另外设置一个比客户机发送来的ISN大1个单位的ISN值,这常被称为SYN_ACK数据包或SYN_ACK报文这时连接的状态称做SYN_RCVD
- ESTABLISHED:发起方然后发送一个带有ACK应答和增1后的ISN标志来确认SYN_ACK至此,完成了三次握手,此时的连接状态为连结成功: ESTABLISHED
TCP连接终止协议(四次挥手)
由于TCP连接是全双工的,因此每个方向都必须单独进行关闭。这原则是当一方完成它的数据发送任务后就能发送一个FIN来终止这个方向的连接。收到一个 FIN只意味着这一方向上没有数据流动,一个TCP连接在收到一个FIN后仍能发送数据。首先进行关闭的一方将执行主动关闭,而另一方执行被动关闭。
TCP客户端发送一个FIN,用来关闭客户到服务器的数据传送(报文段4)。
服务器收到这个FIN,它发回一个ACK,确认序号为收到的序号加1(报文段5)。和SYN一样,一个FIN将占用一个序号。
服务器关闭客户端的连接,发送一个FIN给客户端(报文段6)。
客户段发回ACK报文确认,并将确认序号设置为收到序号加1(报文段7)。
关闭连接等待两倍最大生存时间
主动关闭连接方发送ack响应后,为什么需要等待两倍最大生存时间后再关闭连接?
因为主动方发送的ACK消息可能丢失,并导致被动方再次发送FIN报文,TIME_WAIT为连接中”离群的段”提供从网络中消失的时间。考虑一下,如果延迟或者重传段在连接关闭后到达时会发生什么呢?通常情况下,因为TCP仅仅丢弃该数据并响应RST消息,所以这不会造成任何问题。当RST消息到达发出延时段的主机时,因为该主机也没有记录连接的任何信息,所以它也丢弃该段。然而,如果两个相同主机之间又建立了一个具有相同端口号的新连接,那么离群的段就可能被看成是新连接的,如果离群的段中数据的任何序列号恰恰在新连接的当前接收窗口中,数据就会被重新接收,其结果就是破坏新连接
- 简单点说:就是为了将之前的TCP连接的端口号给占住,不让新的TCP连接使用,避免之前TCP连接上发出的包(因为某些原因比较晚才到达目标主机)被新的TCP连接给收到和处理。
TCP关闭连接时状态
- CLOSED: 初始状态。
- LISTEN: 服务器端的某个SOCKET处于监听状态,可以接受连接了
- FIN_WAIT_1:FIN_WAIT_1和FIN_WAIT_2状态的真正含义都是表示等待对方的FIN报文。而这两种状态的区别是:FIN_WAIT_1状态实际上是当SOCKET在ESTABLISHED状态时,它想主动关闭连接,向对方发送了FIN报文,此时该SOCKET即进入到FIN_WAIT_1状态。而当对方回应ACK报文后,则进入到FIN_WAIT_2状态,当然在实际的正常情况下,无论对方何种情况下,都应该马上回应ACK报文,所以FIN_WAIT_1状态一般是比较难见到的,而FIN_WAIT_2状态还有时常常可以用netstat看到。
- FIN_WAIT_2:FIN_WAIT_2状态下的SOCKET,表示半连接,也即有一方要求close连接,但另外还告诉对方,我暂时还有点数据需要传送给你,稍后再关闭连接。
- TIME_WAIT: 表示收到了对方的FIN报文,并发送出了ACK报文,就等2MSL(2倍最大生存时间)后即可回到CLOSED可用状态了。如果FIN_WAIT_1状态下,收到了对方同时带FIN标志和ACK标志的报文时,可以直接进入到TIME_WAIT状态,而无须经过FIN_WAIT_2状态。
- CLOSING: 正常情况下,当你发送FIN报文后,按理来说是应该先收到(或同时收到)对方的ACK报文,再收到对方的FIN报文。但是CLOSING状态表示你发送FIN报文后,并没有收到对方的ACK报文,反而却也收到了对方的FIN报文。什么情况下会出现此种情况呢?其实细想一下,也不难得出结论:那就是如果双方几乎在同时close一个SOCKET的话,那么就出现了双方同时发送FIN报文的情况,也即会出现CLOSING状态,表示双方都正在关闭SOCKET连接。
- CLOSE_WAIT: 在等待关闭。怎么理解呢?当对方close一个SOCKET后发送FIN报文给自己,你系统毫无疑问地会回应一个ACK报文给对方,此时则进入到CLOSE_WAIT状态。接下来呢,实际上你真正需要考虑的事情是察看你是否还有数据发送给对方,如果没有的话,那么你也就可以close这个SOCKET,发送FIN报文给对方,也即关闭连接。所以你在CLOSE_WAIT状态下,需要完成的事情是等待你去关闭连接。
- LAST_ACK: 它是被动关闭一方在发送FIN报文后,最后等待对方的ACK报文。当收到ACK报文后,也即可以进入到CLOSED可用状态了。
三次和四次
为什么建立连接协议是三次握手,而关闭连接却是四次握手呢?
这是因为服务端的LISTEN状态下的SOCKET当收到SYN报文的建连请求后,它可以把ACK和SYN(ACK起应答作用,而SYN起同步作用)放在一个报文里来发送。但关闭连接时,当收到对方的FIN报文通知时,它仅仅表示对方没有数据发送给你了;但未必你所有的数据都全部发送给对方了,所以你可以未必会马上会关闭SOCKET,也即你可能还需要发送一些数据给对方之后,再发送FIN报文给对方来表示你同意现在可以关闭连接了,所以它这里的ACK报文和FIN报文多数情况下都是分开发送的。
TCP Socket 编程流程图
UDP socket 编程流程图
UDP和TCP区别
- UDP没有三次握手过程。
- UDP处理的细节比TCP少。UDP不能保证消息被传送到(它也报告消息没有传送到)目的地。
- UDP也不保证数据包的传送顺序。UDP把数据发出去后只能希望它能够抵达目的地。
UDP优缺点:
- UDP不要求保持一个连接
- UDP没有因接收方认可收到数据包(或者当数据包没有正确抵达而自动重传)而带来的开销。
- 设计UDP的目的是用于短应用和控制消息
- 在一个数据包连接一个数据包的基础上,UDP要求的网络带宽比TDP更小。
Socket概念
- 网络的Socket数据传输是一种特殊的I/O,Socket也是一种文件描述符
- Socket也具有一个类似于打开文件的函数调用Socket(),该函数返回一个整型的Socket描述符,随后的连接建立、数据传输等操作都是通过该Socket实现的。
Socket类型
- 流式Socket(SOCK_STREAM):流式是一种面向连接的Socket,针对于面向连接的TCP服务应用
- 数据报式Socket(SOCK_DGRAM):数据报式Socket是一种无连接的Socket,对应于无连接的UDP服务应用
socket调用库函数主要有:
下面这几个接口都会创建一个socket
Socket(af,type,protocol)
:创建套接字bind(sockid, local addr, addrlen)
:建立地址和套接字的联系listen( Sockid ,quenlen)
:服务器端侦听客户端的请求
建立服务器/客户端的连接 (面向连接TCP)
Connect(sockid, destaddr, addrlen)
:客户端请求连接newsockid=accept(Sockid,Clientaddr, paddrlen)
:服务器端等待从编号为Sockid的Socket上接收客户连接请求
发送/接收数据
send(sockid, buff, bufflen)
:面向连接的发送数据recv( )
:面向连接的接收数据sendto(sockid,buff,…,addrlen)
面向无连接发送数据recvfrom( )
面向无连接的接收数据
释放套接字
close(sockid)
【网络基础】 TCP & UDP 基础