TCP协议编程流程

一、TCP协议编程流程

这是TCP 编程的 "服务端 + 客户端" 流程,可以记成 "服务端搭台子,客户端来串门" 的故事,好背又好懂:

先明确角色:

  • ser = 服务端(比如奶茶店,负责 "等客人来")
  • cli = 客户端(比如你,负责 "去店里买奶茶")

流程拆解(像 "开奶茶店→买奶茶" 的过程):

服务端(奶茶店)的操作:
  1. socket → 租个店面(创建套接字,相当于 "开店的基础场地")
  2. bind() → 挂上门牌号(把 "店面" 和 "地址 + 端口" 绑定,比如 "XX 街 100 号奶茶店")
  3. listen() → 开门迎客(进入 "监听状态",等客人来)
  4. accept → 迎接客人(收到客人(客户端)的请求,和客人建立 "一对一连接")
  5. recv/send → 做奶茶 + 递奶茶(接收客人的点单(recv),做好后递给客人(send))
  6. close → 打烊关门(结束服务,关闭连接)
客户端(你)的操作:
  1. socket → 出门拿手机(创建自己的套接字,相当于 "出门的工具")
  2. connect() → 导航到奶茶店(根据 "门牌号"(服务端 IP + 端口),发起连接请求)
  3. send/recv → 点单 + 拿奶茶(把点单信息发给店里(send),拿到做好的奶茶(recv))
  4. close → 喝完回家(结束交流,关闭连接)

简化背诵版(一句话串起来):

服务端:建店(socket)→挂牌(bind)→开门(listen)→接客(accept)→买卖(recv/send)→关门(close) 客户端:拿工具(socket)→找店(connect)→买卖(send/recv)→回家(close)

二、三次握手面试极简背诵版

在面试中,我们可以这样来描述三次握手的过程:

"TCP 三次握手是客户端调 connect () 发起的:

  1. 客户端发 SYN 报文,带自己的序号 seq=n;
  2. 服务器回 SYN+ACK:带自己的 seq=m,同时 ack=n+1 确认客户端的 n;
  3. 客户端再回 ACK,ack=m+1 确认服务器的 m,连接建立。"

三、四次挥手面试极简背诵版

TCP 四次挥手是执行close()触发的连接断开流程(以服务端先关为例):

  1. 服务端执行close(),发FIN seq=i报文 ------ 告诉对方 "我没数据要发了,请求关连接";
  2. 客户端收到后,回ACK i+1报文 ------ 确认 "收到你的关闭请求";
  3. 客户端执行close()时,发FIN seq=j报文 ------ 告诉服务端 "我也没数据了,请求关连接";
  4. 服务端收到后,回ACK j+1报文 ------ 确认 "收到你的关闭请求",至此连接断开。

整个过程只有 TCP 控制报文(无数据,报文具体如下图所示),核心是双方都要确认 "无数据传输" 后,才彻底关闭连接

四、tcp 服务器和客户端链接通讯

服务器端代码:

cpp 复制代码
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>

int main()
{
    // 1. 创建监听套接字(相当于"门迎":负责站在门口接客)
    // AF_INET=IPv4,SOCK_STREAM=TCP,0=默认协议
    int sockfd = socket(AF_INET, SOCK_STREAM, 0); 
    if (sockfd == -1)  // 创建失败就退出
    {
        exit(1);
    }

    // 2. 配置服务器地址信息
    struct sockaddr_in saddr, caddr;  // saddr=服务器地址,caddr=客户端地址
    memset(&saddr, 0, sizeof(saddr)); // 初始化地址结构体
    saddr.sin_family = AF_INET;       // 地址族:IPv4
    saddr.sin_port = htons(6000);     // 端口号:6000(htons转网络字节序)
    saddr.sin_addr.s_addr = inet_addr("127.0.0.1"); // 绑定本地回环地址

    // 3. 绑定:把"门迎(sockfd)"和"店铺地址(saddr)"绑定
    int res = bind(sockfd, (struct sockaddr*)&saddr, sizeof(saddr));
    if (res == -1)  // 绑定失败提示并退出
    {
        printf("bind err\n");
        exit(1);
    }

    // 4. 监听:"门迎"开始在门口等客人,最多同时接待5个排队的
    listen(sockfd, 5);

    // 5. 循环接客(一直营业)
    while (1)
    {
        int len = sizeof(caddr);
        // accept:"门迎"接到客人,分配一个"服务员(c,连接套接字)"专门服务这个客人
        // 注意:accept会阻塞,直到有客人来
        int c = accept(sockfd, (struct sockaddr*)&caddr, &len);
        if (c < 0)  // 接客失败就继续等下一个
        {
            continue;
        }
        printf("accept c=%d\n", c);  // 打印这个"服务员"的编号

        // 6. 和当前客人的交互("服务员"服务客人)
        while (1)
        {
            char buff[128] = {0};
            // recv:"服务员"接收客人说的话(数据),recv=0表示客人走了(关闭连接)
            // 注意:recv会阻塞,直到收到客人消息
            int n = recv(c, buff, 127, 0);
            if (n <= 0)  // 没收到消息/客人走了,就结束服务
            {
                break;
            }

            printf("recv:%s\n", buff);  // 打印客人说的话
            send(c, "ok", 2, 0);        // "服务员"回复客人"ok"
        }

        // 7. 客人走了,"服务员"下班(关闭这个连接)
        close(c);
        printf("client close\n");
    }
}

通俗易懂讲解(结合你的 "门迎 / 服务员" 类比)

这个代码是TCP 服务端程序,就像开了一家店:

  1. socket()创建监听套接字(sockfd) → 雇个 "门迎",负责站在门口接客。
  2. bind()绑定地址端口 → 给店铺挂上门牌号(127.0.0.1:6000),让客人能找到。
  3. listen()监听 → "门迎" 开始在门口等客人,最多允许 5 个客人排队。
  4. accept()接收连接(得到 c) → 有客人来,"门迎" 分配一个 "专属服务员(c)" 负责接待这个客人(c就是连接套接字)。
  5. recv()/send()交互 → "服务员" 和客人聊天:收客人的消息(recv)、回客人 "ok"(send)。
  6. close(c)关闭连接 → 客人走了,这个 "服务员" 下班(释放连接)。

整个流程就是:门迎(sockfd)接客→分配服务员(c)→服务员陪聊→客人走了服务员下班,循环往复一直营业

客户端代码:

cpp 复制代码
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>

int main()
{
    // 1. 创建客户端套接字(相当于"客人出门带的手机",用来联系店铺)
    // AF_INET=IPv4,SOCK_STREAM=TCP,0=默认协议
    int sockfd = socket(AF_INET, SOCK_STREAM, 0); 
    if (sockfd == -1)  // 创建失败就退出
    {
        exit(1);
    }

    // 2. 配置要连接的服务器地址(相当于"店铺的门牌号")
    struct sockaddr_in saddr;  // saddr=要去的服务器地址(ip+port)
    memset(&saddr, 0, sizeof(saddr)); // 初始化地址结构体
    saddr.sin_family = AF_INET;       // 地址族:IPv4
    saddr.sin_port = htons(6000);     // 服务器端口号:6000(转网络字节序)
    saddr.sin_addr.s_addr = inet_addr("127.0.0.1"); // 服务器IP:本地回环地址

    // 3. 发起连接(相当于"客人导航到店铺门口,敲门进店")
    int res = connect(sockfd, (struct sockaddr*)&saddr, sizeof(saddr));
    if (res == -1)  // 连接失败提示并退出
    {
        printf("connect err\n");
        exit(1);
    }

    // 4. 和服务器聊天(相当于"客人和服务员交流")
    while (1)
    {
        printf("input\n");  // 提示客人输入内容
        char buff[128] = {0};
        fgets(buff, 128, stdin);  // 读取客人输入的内容

        // 如果输入"end",就结束聊天
        if (strncmp(buff, "end", 3) == 0)
        {
            break;
        }

        // 把输入的内容发给服务器(相当于"客人和服务员说话")
        // strlen(buff)-1:去掉fgets读入的换行符
        send(sockfd, buff, strlen(buff)-1, 0);
        
        memset(buff, 0, 128); // 清空缓冲区,准备接收服务器回复
        recv(sockfd, buff, 127, 0); // 接收服务器的回复(比如"ok")
        printf("buff=%s\n", buff);  // 打印服务器的回复
    }

    // 5. 结束聊天,关闭连接(相当于"客人离开店铺")
    close(sockfd);
    exit(0);
}

这个代码是TCP 客户端程序 ,相当于去店铺消费的客人

  1. socket()创建套接字(sockfd) → 客人出门带个 "手机",用来联系店铺。
  2. 配置saddr → 查好店铺的 "门牌号"(127.0.0.1:6000)。
  3. connect()发起连接 → 客人导航到店铺门口,敲门进店(和服务器建立连接)。
  4. fgets()输入内容 → 客人准备要和服务员说的话。
  5. send()发消息 → 客人把话传给服务员。
  6. recv()收回复 → 客人收到服务员的回应(比如 "ok")。
  7. 输入 "end" 则break → 客人说 "不聊了",结束交流。
  8. close(sockfd) → 客人离开店铺,关掉 "手机"(关闭连接)。

整个流程就是:客人带手机→查店铺地址→进店→和服务员聊天→聊完离开,对应客户端和服务器的交互逻辑

运行服务端代码./ser后,执行netstat -natp看到的127.0.0.1:6000 LISTEN ./ser,就是我的监听套接字 sockfd(门迎)在 6000 端口等待连接;运行客户端后出现的ESTABLISHED状态,就是连接套接字 c(服务员)和客户端建立通信;整个命令查的就是我写的 TCP 服务端的真实运行状态,和代码、三次握手 / 四次挥手、套接字分工完全对应。

相关推荐
Insist7532 小时前
KingbaseES 集群运维案例之 --- 集群架构拆分为单实例操作
网络·数据库·oracle
阿里超级工程师2 小时前
yunedit-ssh相比jenkins和winscp的特点和优势分析
服务器·ssh·jenkins
Tab6092 小时前
接入谷歌home/assistant/智能音箱
服务器·前端·智能音箱
aliyunaliyun3 小时前
2026年京东云企业专享优惠:云服务器租用费用明细及最新报价
服务器·网络·京东云
ホロHoro3 小时前
数据结构非线性部分(二)review
linux·服务器·数据结构
hqwest3 小时前
码上通QT实战21--监控页面13-控制灯珠状态
网络·modbus·线圈·modbus功能码05·modbus功能码0f·开关量
克里斯蒂亚诺更新3 小时前
宝塔 服务器一个端口页面访问另外一个服务器的端口页面
运维·服务器
傣味洋芋3 小时前
WebSocket
网络·vue.js·websocket·网络协议
翼龙云_cloud3 小时前
阿里云渠道商:如何使用弹性伸缩同时管理实例和托管实例?
服务器·阿里云·云计算