socket编程UDP-实现停等机制(接收确认、超时重传)

在下面博客中,我介绍了利用UDP模拟TCP连接、按数据包发送文件 的过程,并附上完整源码

socket编程UDP-文件传输&模拟TCP建立连接脱离连接(进阶篇)_udp socket发送-CSDN博客

下面博客实现的是滑动窗口机制

socket编程UDP-实现滑动窗口机制与累积确认GBN-CSDN博客

本篇博客,我将在此基础上实现停等机制 ,完成客户端发送的接收确认、超时重传

目录

一、停等机制的协议设计

二、停等机制的代码实现

1.实现思路

2.核心源码

3.可运行完整源码

三、运行演示

1.建立与断开连接

2.接收确认(无丢包)

3.丢包处理&超时重传


一、停等机制的协议设计

在设计中,客户端为文件发送方服务器端为文件接收方

每次客户端发送的数据包有唯一的序列号seq (随着数据包的发送不断递增), 如果服务器端收到新的数据包会发送对应的ack.(比如收到seq1就会发送ack1,收到seq2就会发送ack2).

所谓停等机制,就是发送方每轮只发送一个数据包,直到收到期待的ack (即与序列号对应的ack),才会发送下一个数据包。

如果发送方在定时器时间内没有收到期待的ack,将会重传这一数据包。(正如图中发送端重传seq2)

二、停等机制的代码实现

1.实现思路

接收确认和超时重传机制主要通过 'waitForAck'、'receiveAck'和 'sendFile'函数来完成。以下是实现过程的描述:

  • 在 'receiveAck'方法中,服务器会不断监听****ACK 消息。收到任何数据包后,首先验证其校

    验和和 ACK 序列号是否匹配。如果验证成功,会将 'ackReceived'**设置为 'true',并通过条件变量通知 'waitForAck'**,使其能够退出等待状态。

  • 'sendFile '方法负责逐个发送数据包 ,并在**每次发送后调用'waitForAck',**等待接收 ACK 确认。每个数据包都包含一个序列号('seqNum'),用于标识数据的顺序和确认接收的正确性。发送数据包后, 'ackReceived'标志被设置为 'false',并记录期望的 ACK 序列号。

  • 'waitForAck '方法使用条件变量和超时机制,如果在设定的超时时间内未收到正确的 ACK 确认,便会返回****'false' ,触发重传逻辑;如果收到了正确ack,则会返回true.

2.核心源码

cpp 复制代码
bool Sender::waitForAck(int seqNum) {
    std::unique_lock<std::mutex> lock(mtx);
    return cv.wait_for(lock, std::chrono::milliseconds(TIMEOUT), [this, seqNum]() { return ackReceived && expectedAck == seqNum; });
}
cpp 复制代码
void Sender::receiveAck() {
    Datagram ackPacket(SERVER_PORT,ROUTER_PORT);
    socklen_t len = sizeof(routerAddr);
    while (true) {
        if (recvfrom(sock, reinterpret_cast<char*>(&ackPacket), sizeof(ackPacket), 0, (struct sockaddr*)&routerAddr, &len) > 0) {
            if (ackPacket.validateChecksum(clientAddr.sin_addr.S_un.S_addr, routerAddr.sin_addr.S_un.S_addr) && ackPacket.ack == expectedAck) {
                std::lock_guard<std::mutex> lock(mtx);
                std::cout<<"收到ACK,ack="<<ackPacket.ack<<std::endl;
                ackReceived = true;
                cv.notify_one();
            }
        }
    }
}
cpp 复制代码
void Sender::sendFile(const std::string& filename) {
    //......
    int seqNum = 0;
    while (!file.eof()) {
        Datagram packet(CLIENT_PORT,ROUTER_PORT);
        packet.seq = seqNum;
        file.read(packet.data, BUFFER_SIZE);
        packet.dataSize = static_cast<int>(file.gcount());
        packet.flag = 0; // 数据包

        ackReceived = false;
        expectedAck = seqNum;

        //1.创建接收线程,避免第三次握手时ACK的丢包
        Datagram AckPacket(SERVER_PORT,ROUTER_PORT);
        if(seqNum<3)
        {
            std::thread ackThread1(&Sender::receivePacket,this, std::ref(AckPacket));
            std::this_thread::sleep_for(std::chrono::milliseconds(TIMEOUT)); //休眠等一会儿
            ackThread1.detach();//修改
        }
        
        while (true) {
            if(AckPacket.flag == 2&&seqNum<3&&AckPacket.validateChecksum(clientAddr.sin_addr.S_un.S_addr, routerAddr.sin_addr.S_un.S_addr))//2.如果此时又收到了SYN-ACK
            {
                std::cout << "重新收到SYN-ACK包\n";
                Datagram ackPacket(CLIENT_PORT,ROUTER_PORT);
                ackPacket.flag = 3; // ACK
                sendPacket(ackPacket);
                std::cout << "重新发送ACK包,连接建立成功\n";
                std::this_thread::sleep_for(std::chrono::milliseconds(TIMEOUT)); //休眠等一会儿
                AckPacket.flag=1;
            }
            sendPacket(packet);
            std::cout << "发送数据包.SEQ=" << packet.seq <<",校验码="<< packet.checksum<<std::endl;
            std::this_thread::sleep_for(std::chrono::milliseconds(5*TIMEOUT)); //休眠等一会儿
            if (waitForAck(seqNum)) {
                break; // 收到ACK,跳出重传循环
            }
            
            std::cout << "ACK超时,重传数据包,SEQ=" << packet.seq << std::endl;
        }
        seqNum++;
    }
    //......
}

3.可运行完整源码

已上传github:

https://github.com/yeyeyeyeye-zhang/Computer-Network/tree/main/lab3-1/codes

三、运行演示

在src目录下输入:

cpp 复制代码
 g++ -o cs main.cpp Datagram.cpp Sender.cpp Receiver.cpp -lws2_32

1.建立与断开连接

客户端建立连接

服务器端建立连接

客户端断开连接

服务器端断开连接

2.接收确认(无丢包)

客户端正常发送与接收

服务器端正常接收与发送

3.丢包处理&超时重传

出现丢包后,超时,客户端重传数据包

相关推荐
jenchoi41319 小时前
【2025-11-23】软件供应链安全日报:最新漏洞预警与投毒预警情报汇总
网络·数据库·安全·web安全·网络安全
独行soc21 小时前
2025年渗透测试面试题总结-258(题目+回答)
网络·python·安全·web安全·渗透测试·安全狮
AI绘画小3321 小时前
网络安全(黑客技术)—2025自学手册
网络·安全·web安全·网络安全·渗透测试
s09071361 天前
ZYNQ DMA to UDP 数据传输系统设计文档
网络协议·fpga开发·udp
恒创科技HK1 天前
香港服务器流量有限制和带宽有限制区别在哪?
运维·服务器·网络
hazy1k1 天前
ESP32基础-Socket通信 (TCP/UDP)
c语言·单片机·嵌入式硬件·网络协议·tcp/ip·udp·esp32
赖small强1 天前
【Linux 网络基础】WebSockets 技术指南
linux·网络·https·websockets·ping/pong
专家大圣1 天前
告别局域网束缚!飞牛云 NAS+cpolar 让远程管理更简单
开发语言·网络·内网穿透·cpolar
xinxinhenmeihao1 天前
爬虫为什么要用动态ip?动态IP在爬虫中起到哪些作用?
爬虫·网络协议·tcp/ip
swanwei1 天前
2025年11月22-23日互联网技术热点TOP3及影响分析(AI增量训练框架开源)
网络·人工智能·程序人生·安全·百度