20.TCP连接

发布时间 2023-11-07 20:56:37作者: CodeMagicianT

20.TCP连接

学习目标

▶掌握三次握手建立连接过程

▶掌握四次握手关闭连接的过程

▶掌握滑动窗口的概念

▶掌握错误处理函数封装

▶实现多进程并发服务器

▶实现多线程并发服务器

1.三次握手和四次挥手

  思考: 为什么TCP是面向连接的安全可靠的传输????

  TCP是面向连接的安全的数据传输, 在客户端与服务端建立建立的时候要经过三次握手的过程,在客户端与服务端断开连接的时候要经历四次挥手的过程,下图是客户端与服务端三次握手建立连接,数据传输和断开连接四次挥手的全过程。

  TCP时序:

说明讲义中图的含义。

  SYN:表示请求,ACK:表示确认

  服务端发送的SYN和客户端发送的SYN本身也会占1位。

单独讲解三次握手过程,以图解形式说明。

  上图中ACK表示确认序号,确认序号的值是对方发送的序号值+数据的长度,特别注意的是SYN和FIN本身也会占用一位。

  注: SYS----->synchronous

   ACK----->acknowledgement

   FIN------>finish

三次握手和四次挥手的过程都是在内核实现的。

下图是TCP数据报格式

窗口大小:指的是缓冲区大小

通信的时候不再需要SYN标识位了,只有在请求连接的时候需要SYN标识位。

传输数据的时候的随机序号seq就是最近一次对方发送给自己的ACK的随机序号值,而发给对方的ACK就是上次刚刚发给对方的ACK的值。

  图中发送的ACK确认包表示给对方发送数据的一个确认,表示你发送的数据我都收到了,同时告诉对方下次发送该序号开始的数据。

  由于每次发送数据都会收到对方发来的确认包,所以可以确认对方是否收到了,若没有收到对方发来的确认包,则会进行重发。

  mss: 最大报文长度,告诉对方我这边最多一次能收多少,你不能超过这个长度。

  win: 表示告诉对方我这边缓存大小最大是多少。

1.1三次握手



















第一种回答

刚开始客户端处于 Closed 的状态,服务端处于 Listen 状态,进行三次握手:

  • 第一次握手:客户端给服务端发一个 SYN 报文,并指明客户端的初始化序列号 ISN(c)。此时客户端处于 SYN_SEND 状态。

    首部的同步位SYN=1,初始序号seq=x,SYN=1的报文段不能携带数据,但要消耗掉一个序号。

  • 第二次握手:服务器收到客户端的 SYN 报文之后,会以自己的 SYN 报文作为应答,并且也是指定了自己的初始化序列号 ISN(s)。同时会把客户端的 ISN + 1 作为ACK 的值,表示自己已经收到了客户端的 SYN,此时服务器处于 SYN_RCVD 的状态。

    在确认报文段中SYN=1,ACK=1,确认号ack=x+1,初始序号seq=y。

  • 第三次握手:客户端收到 SYN 报文之后,会发送一个 ACK 报文,当然,也是一样把服务器的 ISN + 1 作为 ACK 的值,表示已经收到了服务端的 SYN 报文,此时客户端处于 ESTABLISHED 状态。服务器收到 ACK 报文之后,也处于 ESTABLISHED 状态,此时,双方已建立起了连接。

    确认报文段ACK=1,确认号ack=y+1,序号seq=x+1(初始为seq=x,第二个报文段所以要+1),ACK报文段可以携带数据,不携带数据则不消耗序号。

发送第一个SYN的一端将执行主动打开(active open),接收这个SYN并发回下一个SYN的另一端执行被动打开(passive open)。

在socket编程中,客户端执行connect()时,将触发三次握手。

第二种回答

  • 初始状态:客户端处于 closed(关闭)状态,服务器处于 listen(监听) 状态。
  • 第一次握手:客户端发送请求报文将 SYN = 1同步序列号和初始化序列号seq = x发送给服务端,发送完之后客户端处于SYN_Send状态。(验证了客户端的发送能力和服务端的接收能力)
  • 第二次握手:服务端受到 SYN 请求报文之后,如果同意连接,会以自己的同步序列号SYN(服务端) = 1、初始化序列号 seq = y和确认序列号(期望下次收到的数据包)ack = x+ 1 以及确认号ACK = 1报文作为应答,服务器为SYN_Receive状态。(问题来了,两次握手之后,站在客户端角度上思考:我发送和接收都ok,服务端的发送和接收也都ok。但是站在服务端的角度思考:哎呀,我服务端接收ok,但是我不清楚我的发送ok不ok呀,而且我还不知道你接受能力如何呢?所以老哥,你需要给我三次握手来传个话告诉我一声。你要是不告诉我,万一我认为你跑了,然后我可能出于安全性的考虑继续给你发一次,看看你回不回我。)
  • 第三次握手: 客户端接收到服务端的 SYN + ACK之后,知道可以下次可以发送了下一序列的数据包了,然后发送同步序列号 ack = y + 1和数据包的序列号 seq = x + 1以及确认号ACK = 1确认包作为应答,客户端转为established状态。(分别站在双方的角度上思考,各自ok)

1.2四次挥手相关内容

2.滑动窗口

主要作用: 滑动窗口主要是进行流量控制的。

见下图:如果发送端发送的速度较快,接收端接收到数据后处理的速度较慢,而接收缓冲区的大小是固定的,就会导致接收缓冲区满而丢失数据。TCP协议通过“滑动窗口(Sliding Window)”机制解决这一问题。

详细说明:

1.发送端发起连接,声明最大段尺寸是1460,初始序号是0,窗口大小是4K,表示“我的接收缓冲区还有4K字节空闲,你发的数据不要超过4K”。接收端应答连接请求,声明最大段尺寸是1024,初始序号是8000,窗口大小是6K。发送端应答,三方握手结束。

2.发送端发出段4-9,每个段带1K的数据,发送端根据窗口大小知道接收端的缓冲区满了,因此停止发送数据。

3.接收端的应用程序提走2K数据,接收缓冲区又有了2K空闲,接收端发出段10,在应答已收到6K数据的同时声明窗口大小为2K。

4.接收端的应用程序又提走2K数据,接收缓冲区有4K空闲,接收端发出段11,重新声明窗口大小为4K。

5.发送端发出段12-13,每个段带2K数据,段13同时还包含FIN位。

6.接收端应答接收到的2K数据(6145-8192),再加上FIN位占一个序号8193,因此应答序号是8194,连接处于半关闭状态,接收端同时声明窗口大小为2K。

7.接收端的应用程序提走2K数据,接收端重新声明窗口大小为4K。

8.接收端的应用程序提走剩下的2K数据,接收缓冲区全空,接收端重新声明窗口大小为6K。

9.接收端的应用程序在提走全部数据后,决定关闭连接,发出段17包含FIN位,发送端应答,连接完全关闭。

上图在接收端用小方块表示1K数据,实心的小方块表示已接收到的数据,虚线框表示接收缓冲区,因此套在虚线框中的空心小方块表示窗口大小,从图中可以看出,随着应用程序提走数据,虚线框是向右滑动的,因此称为滑动窗口。

从这个例子还可以看出,发送端是一K一K地发送数据,而接收端的应用程序可以两K两K地提走数据,当然也有可能一次提走3K或6K数据,或者一次只提走几个字节的数据。也就是说,应用程序所看到的数据是一个整体,或说是一个流(stream),在底层通讯中这些数据可能被拆成很多数据包来发送,但是一个数据包有多少字节对应用程序是不可见的,因此TCP协议是面向流的协议。而UDP是面向消息的协议,每个UDP段都是一条消息,应用程序必须以消息为单位提取数据,不能一次提取任意字节的数据,这一点和TCP是很不同的。

图中win表示告诉对方我这边缓冲区大小是多少,mss表示告诉对方我这边最多一次可以接收多少数据,你最好不要超过这个长度。

在客户端给服务端发包的时候,不一定是非要等到服务端返回响应包,由于客户端知道服务端的窗口大小,所以可以持续多次发送,当发送数据达到对方窗口大小了就不再发送,需要等到对方进行处理,对方处理之后可继续发送。

mss和MTU

MTU:最大传输单元

MTU:通信术语最大传输单元(Maximum Transmission Unit,MTU)

是指一种通信协议的某一层上面所能通过的最大数据包大小(以字节为 单位)。最大传输单元这个参数通常与通信接口有关(网络接口卡、串口等),这个值如果设置为太大会导致丢包重传的时候重传的数据量较大,图中的最大值是1500,其实是一个经验值。

mss: 最大报文长度,只是在建立连接的时候,告诉对方我最大能够接收多少数据,在数据通信的过程中就没有mss了。

3.函数封装思想

函数封装的思想-处理异常情况

 结合man-page和errno进行封装。

 在封装的时候起名可以把第一个函数名的字母大写,如socket可以封装成Socket, 这样可以按shift+k进行搜索,shift+k搜索函数说明的时候不区分大小写,使用man page也可以查看,man page对大小写不区分。

像accept,read这样的能够引起阻塞的函数,若被信号打断,由于信号的优先级较高, 会优先处理信号, 信号处理完成后,会使accept或者read解除阻塞,然后返回,此时返回值为 -1,设置errno=EINTR;

errno=ECONNABORTED表示连接被打断,异常。

errno宏:

/usr/include/asm-generic/errno.h文件中包含了errno所有的宏和对应的错误描述信息。

粘包的概念

 粘包:多次数据发送,收尾相连,接收端接收的时候不能正确区分第一次发送多少,第二次发送多少。

 粘包问题分析和解决??

 方案1: 包头+数据

   如4位的数据长度+数据 -----------> 00101234567890

   其中0010表示数据长度,1234567890表示10个字节长度的数据。

   另外,发送端和接收端可以协商更为复杂的报文结构,这个报文结构就相当于双方约定的一个协议。

 方案2: 添加结尾标记。

   如结尾最后一个字符为\n \$等。

 方案3: 数据包定长
   如发送方和接收方约定,每次只发送128个字节的内容,接收方接收定长128个字节就可以了。

wrap.c代码解读和分析。

 要求能看懂代码,会使用即可。