网络编程套接字( TCP协议通讯流程)

目录

1、绑定失败问题

2、TCP协议通讯流程

        三次握手的过程

        数据传输的过程

        四次挥手的过程

        TCP和UDP对比


1、绑定失败问题

当我们测试网络代码时,先将服务端绑定8080端口运行,然后运行客户端,并让客户端连接当前服务器:

当有客户端连接的情况下,我直接将服务端关闭,此时服务端想要再次绑定8080端口时,就会绑定失败:

综上:如果server在保有连接的时候,server自己先因为一些原因而导致退出,服务器无法立即重启。具体原因等后续博主讲解TCP协议时再详谈。


2、TCP协议通讯流程

下图是基于TCP协议的客户端 / 服务器程序的一般流程:

  • TCP是面向连接的,client 通信前先 connect;server 通信前先 accept
  • TCP在建立连接的时候,采用的是三次握手,在断开连接的时候,采用的是四次挥手 
  • connect发起三次挥手(client),connect只有客户端做。close客户端和服务端都要做。close() client && close() server -> close() 执行4次挥手中的2次

下面我们结合TCP协议的通信流程,来初步认识一下三次握手和四次挥手,以及建立连接和断开连接与各个网络接口之间的对应关系。


三次握手的过程

采用三次握手来建立连接。三次握手的过程就是两台主机建立通信连接的过程。软件服务器架构:客户端(左),服务端(右)

  • 第一次,客户端给服务端发送SYN码,请求连接。
  • 第二次,服务端收到客户端发来的请求,然后表示可以连接,发送SYN+ACK。
  • 第三次,客户端收到服务器的确认,并再次向服务器发送 ACK码表示确认,服务器收到,两台主机建立连接完成。

示例:

  • 偶然的一次机遇,你(客户端)遇到了多年未见的老同学(服务端)。因为变化太大,你只是觉着像,但还是像她微笑点了点头(第一次)。老同学看到了你,一眼就认出了你,也对你微笑点了点头并挥了挥手(第二次)。你看到老同学的反应,更加坚定你们认识。随后,你也向老同学挥了挥手(第三次)。在这个例子中微笑点头用SYN表示,挥挥手用ACK表示。

初始化服务器

  • 当服务器完成套接字创建、绑定以及监听的初始化动作之后,就可以调用accept函数阻塞等待客户端发起请求连接了。

服务器初始化:

  • 调用socket,创建文件描述符。
  • 调用bind,将当前的文件描述符和IP/PORT绑定在一起,如果这个端口已经被其他进程占用了,就会bind失败。
  • 调用listen,声明当前这个文件描述符作为一个服务器的文件描述符,为后面的accept做好准备。
  • 调用accept,并阻塞,等待客户端连接到来。

建立连接

  • 而客户端在完成套接字创建后,就会在合适的时候通过connect函数向服务器发起连接请求,而客户端在connect的时候本质是通过某种方式向服务器三次握手,因此connect的作用实际就是触发三次握手。

建立连接的过程:

  • 调用socket,创建文件描述符。
  • 调用connect,向服务器发起连接请求。
  • connect会发出SYN段并阻塞等待服务器应答(第一次)。
  • 服务器收到客户端的SYN,会应答一个SYN-ACK段表示“同意建立连接”(第二次)。
  • 客户端收到SYN-ACK后会从connect返回,同时应答一个ACK段(第三次)。

这个建立连接的过程,通常称为三次握手。

  • 需要注意的是,连接并不是立马建立成功的,由于TCP属于传输层协议,因此在建立连接时双方的操作系统会自主进行三次协商,最后连接才会建立成功。

数据传输的过程

  • 连接一旦建立成功并且被accept获取上来后,此时客户端和服务器就可以进行数据交互了。需要注意的是,连接建立和连接被拿到用户层是两码事,accept函数实际不参与三次握手这个过程,因为三次握手本身就是底层TCP所做的工作。accept要做的只是将底层已经建立好的连接拿到用户层,如果底层没有建立好的连接,那么accept函数就会阻塞住直到有建立好的连接。
  • 而双方在进行数据交互时使用的实际就是read和write,其中write就叫做写数据,read就叫做读数据。write的任务就是把用户数据拷贝到操作系统,而拷贝过去的数据何时发以及发多少,就是由TCP决定的。而read的任务就是把数据从内核读到用户。

数据传输的过程

  • 建立连接后,TCP协议提供全双工的通信服务,所谓全双工的意思是,在同一条连接中,同一时刻,通信双方可以同时写数据,相对的概念叫做半双工,同一条连接在同一时刻,只能由一方来写数据。
  • 服务器从accept返回后立刻调用read,读socket就像读管道一样,如果没有数据到达就阻塞等待。
  • 这时客户端调用write发送请求给服务器,服务器收到后从read返回,对客户端的请求进行处理,在此期间客户端调用read阻塞等待服务器端应答。
  • 服务器调用write将处理的结果发回给客户端,再次调用read阻塞等待下一条请求。
  • 客户端收到后从read返回,发送下一条请求,如此循环下去。

四次挥手的过程

四次挥手的过程就是两台主机断开通信连接的过程。软件服务器架构:客户端(左),服务端(右)

  • 第一次,客户端发送FIN给服务端,表达要断开连接,现在是请求断开的状态
  • 第二次,服务端收到FIN请求,回复ACK表达可以,等待结束的状态
  • 第三次,服务端发送FIN给客户端,并且客户端收到ACK请求,进入半连接半断开状态
  • 第四次,客户端收到FIN,等待时间,回复ACK确认,双方进入close状态

示例:

  • 当你成功追到了女孩并走上婚姻的殿堂。结婚后你整体游手好闲,女生受不了你不思进取。便要提出和你离婚(第一次),你同意了她的离婚请求(第二次)。但同意归同意,离婚协议书上你死皮赖脸就是不签字,一直耗着女生。终于有一天,你也无法忍受这样耗下去了,便向她也提出了离婚申请(第三次)。女方果断答应(第四次)。此时双方进入CLOSE状态。

端口连接

  • 当双方通信结束之后,需要通过四次挥手的方案使双方断开连接,当客户端调用close关闭连接后,服务器最终也会关闭对应的连接。而其中一次close就对应两次挥手,因此一对close最终对应的就是四次挥手。

断开连接的过程

  • 如果客户端没有更多的请求了,就调用close关闭连接,客户端会向服务器发送FIN段(第一次)。
  • 此时服务器收到FIN后,会回应一个ACK,同时read会返回0(第二次)。
  • read返回之后,服务器就知道客户端关闭了连接,也调用close关闭连接,这个时候服务器会向客户端发送一个FIN(第三次)。
  • 客户端收到FIN,再返回一个ACK给服务器(第四次)。

这个断开连接的过程,通常称为四次挥手。

注意通讯流程与socket API之间的对应关系

在学习socket API时要注意应用程序和TCP协议是如何交互的:

  • 应用程序调用某个socket函数时TCP协议层完成什么动作,比如调用connect会发出SYN段。
  • 应用程序如何知道TCP协议层的状态变化,比如从某个阻塞的socket函数返回就表明TCP协议收到了某些段,再比如read返回0就表明收到了FIN段。

为什么要断开连接?

  • 建立连接本质上是为了保证通信双方都有专属的连接,这样我们就可以加入很多的传输策略,从而保证数据传输的可靠性。但如果双方通信结束后不断开对应的连接,那么系统的资源就会越来越少。
  • 因为服务器是会收到大量连接的,操作系统必须要对这些连接进行管理,在管理连接时我们需要“先描述再组织”。因此当一个连接建立后,在服务端就会为该连接维护对应的数据结构,并且会将这些连接的数据结构组织起来,此时操作系统对连接的管理就变成了对链表的增删查改。
  • 如果一个连接建立后不断开,那么操作系统就需要一直为其维护对应的数据结构,而维护这个数据结构是需要花费时间和空间的,因此当双方通信结束后就应该将这个连接断开,避免系统资源的浪费,这其实就是TCP比UDP更复杂的原因之一,因为TCP需要对连接进行管理。

TCP和UDP对比

  • 可靠传输 vs 不可靠传输
  • 有连接 vs 无连接
  • 字节流 vs 数据报
  • 1
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

三分苦

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值