TCP服务建立的全流程详解

发布于:2025-08-14 ⋅ 阅读:(14) ⋅ 点赞:(0)

TCP的服务监听步骤(等待客户端连接前)

TCP 服务器通过以下步骤完成从初始化到等待客户端连接,为后续的数据传输(send()/recv())奠定了基础

一、创建套接字(Socket)

  • 作用:套接字是网络通信的端点,用于标识通信中的双方(IP 地址和端口号)。服务器首先需要创建一个套接字,作为后续监听和通信的基础。
  • 实现:应用程序通过系统调用(如socket())创建套接字,指定地址族(如 IPv4 用AF_INET)、传输层协议(TCP 用SOCK_STREAM)等参数。
  • 示例:在 Linux 中,int sockfd = socket(AF_INET, SOCK_STREAM, 0); 会创建一个 TCP 套接字,返回套接字描述符sockfd

二、绑定套接字到本地地址和端口(Bind)

  • 作用:将创建的套接字与服务器的本地 IP 地址和指定端口号绑定,确保客户端能通过该地址和端口找到服务器。
  • 实现
    1. 应用程序定义一个包含本地 IP 地址和端口号的结构体(如sockaddr_in)。
    2. 通过系统调用(如bind())将套接字与该结构体绑定。
  • 注意
    • 端口号通常选择 1024 以上的非特权端口(避免与系统服务冲突)。
    • 若 IP 地址设为INADDR_ANY(通配地址),表示服务器监听所有可用的本地网络接口。
  • 示例bind(sockfd, (struct sockaddr*)&addr, sizeof(addr));

三、设置监听状态(Listen)

  • 作用:将绑定后的套接字转换为监听套接字,使其能够接收客户端的连接请求。同时,操作系统会为该套接字维护一个连接请求队列(未完成三次握手的客户端请求)。
  • 实现:通过系统调用(如listen())设置监听,参数包括监听套接字和队列的最大长度(backlog,即最多能同时等待处理的连接请求数)。
  • 注意backlog的值需根据服务器性能和预期并发量设置,若队列满,新的连接请求会被拒绝(客户端可能收到 “连接超时” 错误)。
  • 示例listen(sockfd, 5);(表示最多允许 5 个连接请求在队列中等待)。

四、等待并接受客户端连接(Accept)

  • 作用:监听套接字进入阻塞状态(默认情况),等待客户端的连接请求。当有客户端发起连接时,服务器通过accept()系统调用接受连接,生成一个新的连接套接字用于与该客户端通信。
  • 过程
    1. 客户端通过connect()发起 TCP 连接请求,与服务器进行三次握手。
    2. 三次握手完成后,连接请求从 “未完成队列” 移至 “已完成队列”。
    3. 服务器调用accept()从 “已完成队列” 中取出一个连接请求,创建新的连接套接字(与监听套接字的 IP 和端口相同,但用于单独的客户端通信)。
  • 注意
    • 监听套接字始终保持监听状态,用于接收新的连接请求;连接套接字则用于与特定客户端的数据传输。
    • 若需处理并发连接,服务器通常会通过多线程、多进程或 I/O 复用(如selectepoll)来同时管理多个连接套接字。
  • 示例int new_fd = accept(sockfd, (struct sockaddr*)&client_addr, &addr_len);new_fd即为新的连接套接字)。

总结:TCP 服务监听的完整流程

  1. 创建套接字(socket())→ 2. 绑定地址和端口(bind())→ 3. 设置监听(listen())→ 4. 接受连接(accept())。

服务端与客户端通信(3握手4挥手)

TCP 通信遵循 "三次握手建立连接、数据传输、四次挥手断开连接" 的经典模式,这种模式确保了通信的可靠性和有序性。

1. 连接建立:三次握手

TCP 是面向连接的协议,在进行实际数据传输前,通信双方必须先建立连接,这个过程被形象地称为 "三次握手"(Three-way Handshake)。

  • 第一次握手:客户端发送 SYN(同步序列编号)报文段,告知服务器客户端的初始序列号(ISN),并请求建立连接。
  • 第二次次握手:服务器收到 SYN 后,返回一个 SYN+ACK(确认)报文段,包含服务器的初始序列号,并确认收到客户端的 SYN(ACK 值 = 客户端 ISN+1)。
  • 第三次握手:客户端收到服务器的 SYN+ACK 后,发送一个 ACK 报文段,确认收到服务器的 SYN(ACK 值 = 服务器 ISN+1)。

三次握手完成后,TCP 连接正式建立,双方可以开始数据传输。这种设计有效防止了因网络延迟导致的 "已失效的连接请求报文段" 被服务器接收,从而避免资源浪费。

2. 数据传输阶段

连接建立后,进入数据传输阶段。TCP 通过以下机制保证数据传输的可靠性:

  • 序列号与确认机制:每个数据字节都有一个序列号,接收方收到数据后会返回确认信息,告知发送方已成功接收的数据量。
  • 超时重传:发送方设置超时计时器,若在规定时间内未收到确认,则重传数据。
  • 流量控制:通过滑动窗口机制,控制发送方的发送速率,避免接收方缓冲区溢出。
  • 拥塞控制:检测网络拥塞状态并调整发送速率,避免网络过载。

3. 连接终止:四次挥手

当通信结束需要断开连接时,TCP 使用 "四次挥手"(Four-way Wavehand)过程:

  • 第一次挥手:主动关闭方发送 FIN(结束)报文段,告知对方要关闭连接。
  • 第二次挥手:被动关闭方收到 FIN 后,返回 ACK 确认,此时主动关闭方到被动关闭方的连接半关闭。
  • 第三次挥手:被动关闭方准备好关闭连接后,也发送一个 FIN 报文段。
  • 第四次挥手:主动关闭方收到 FIN 后,返回 ACK 确认,被动关闭方到主动关闭方的连接也半关闭。等待一段时间确保确认报文送达后,连接完全关闭。

四次挥手的设计是因为 TCP 连接是全双工的,允许双方独立关闭各自的发送通道。

服务端的全流程

1. 调用 socket 函数创建 socket(监听socket)
2. 调用 bind 函数 将 socket绑定到某个ip和端口的二元组上
3. 调用 listen 函数 开启侦听
4. 当有客户端请求连接上来后,调用 accept 函数接受连接,产生一个新的 socket(客户端 socket)
5. 基于新产生的 socket 调用 send 或 recv 函数开始与客户端进行数据交流
6. 通信结束后,调用 close 函数关闭监听 socket

1. 三次握手(建立连接)发生在第 4 步:accept() 函数执行期间

  • 当服务器调用 listen() 后,进入 “监听” 状态,内核会维护两个队列:
    • 未完成连接队列:客户端已发送 SYN 但三次握手未完成的请求。
    • 已完成连接队列:三次握手已完成、等待服务器处理的连接。
  • 当客户端调用 connect() 发起连接时,内核会自动完成三次握手:
    1. 客户端发送 SYN → 服务器内核接收并放入未完成队列,返回 SYN+ACK(第二次握手)。
    2. 客户端返回 ACK(第三次握手)→ 服务器内核将连接从 “未完成队列” 移至 “已完成队列”。
  • 此时服务器调用 accept() 函数,只是从 “已完成队列” 中取出一个已建立的连接,并创建新的客户端 socket。三次握手的整个过程由内核在 listen() 之后、accept() 返回之前自动完成,应用程序无需干预。

2. 四次挥手(断开连接)发生在数据传输结束后,由 close() 函数触发

  • 当通信双方(客户端或服务器)决定结束连接时,调用 close() 函数会触发内核执行四次挥手:
    1. 主动关闭方调用 close() → 内核发送 FIN 报文(第一次挥手)。
    2. 被动关闭方收到 FIN 后,内核自动返回 ACK(第二次挥手),此时主动关闭方向被动关闭方的连接 “半关闭”。
    3. 被动关闭方处理完剩余数据后,调用 close() → 内核发送 FIN 报文(第三次挥手)。
    4. 主动关闭方收到 FIN 后,内核自动返回 ACK(第四次挥手),等待超时后连接完全关闭。
  • 注意:通常服务器会先关闭 “客户端 socket”(与单个客户端的连接),最后再关闭 “监听 socket”(停止接受新连接)。四次挥手由内核在 close() 调用后自动完成,应用程序只需调用 close() 触发这个过程。

网站公告

今日签到

点亮在社区的每一天
去签到