您的位置:首页 > 科技 > IT业 > 龙岩市新罗区疫情最新消息_中小企业网b2b_互联网营销外包推广_seo分析与优化实训心得

龙岩市新罗区疫情最新消息_中小企业网b2b_互联网营销外包推广_seo分析与优化实训心得

2024/12/23 7:59:26 来源:https://blog.csdn.net/weixin_74447766/article/details/144444591  浏览:    关键词:龙岩市新罗区疫情最新消息_中小企业网b2b_互联网营销外包推广_seo分析与优化实训心得
龙岩市新罗区疫情最新消息_中小企业网b2b_互联网营销外包推广_seo分析与优化实训心得

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

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

下面博客实现了停等机制。

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

本篇博客,我将在此基础上实现滑动窗口机制,完成客户端发送服务器接收的累积确认(GBN)

目录

一、协议设计

1.思路概览

2.完整实例 

二、核心代码

1.代码实现思路

 2. 核心源码

3.可运行完整源码

三、运行演示

1.正常传输(收到正确序列号)

2.乱序抵达(出现丢包情况,收到偏大/偏小数据包)


一、协议设计

1.思路概览

​ 1)对于发送端(客户端)发送窗口为N>1,每次发送的基准窗口设置为base(即每轮发送的最小序列值)。并且:

  • 允许发出N个未得到确认的分组,连续发送而不等待接收端的回复

  • 如果在定时范围内收到该轮最大的ack(expectedAck=base+N-1),立即更新基准窗口base=expectedAck+1,并且开始下一轮发送。(也就是说,即使定时器时间未到,也立刻开始下一轮发送)

  • 如果在定时范围内没有收到该轮最大的ack,将base更新为这段时间收到的最大ack值+1,即base=max(getAck)+1.并且从base开始进行下一轮发送

2)对于接收端(服务器端)接收窗口大小设置为1,因此每收到一个数据包,都会发送一次ack。为了处理丢包,设置buffer缓冲区,用于存储乱序抵达的包

  •  如果收到期待的数据包(seq==expectedseq,其中expectedseq=last\_ack+1),将数据写入文件,并检查缓冲区中是否存储有后续数据包的内容;如果有,将缓冲区的内容写入文件。依据已经写入文件的数据包序列号来更新expectedseq,并发送ack = expectedSeq - 1。

  •  如果收到的数据包序列号大于期待值(seq>expectedseq),将数据暂存进缓冲区buffer,发送最后一次按序抵达的数据包对应的ack.

  • 如果收到的数据包序列号小于期待值(seq<expectedseq),说明收到了重复的数据包,,只发送最后一次按序抵达的数据包对应的ack

2.完整实例 

 

  • 发送窗口大小设置为4,初始时客户端连续发送序列号为0,1,2,3的四个数据包。

  • 图中的seq=0,seq=1的数据包按序抵达(seq==expectedseq,其中expectedseq=last\_ack+1),服务器端会发送对应的ack=0,ack=1.

  •  图中seq=2的数据包丢失,于是当seq=3的数据包抵达时,服务器端收到了偏大的序号(seq=3>expectedseq=2)。服务器端会暂存seq=3的数据包到缓冲区中,并发送已经收到的最大正确序号(ack=1).

  • 客户端一直期待收获到“ack=3”,等待一段时间没有收到,说明超时。于是基准窗口移动为收到的最大ack+1,即1+1=2,发送序列号为2,3,4,5的四个数据包。

  •  服务器端收到seq=2的数据包后,由于之前暂存了seq=3的数据包,因此ack更新为3,将两个数据包一起写入文件,并发送ack=3.

  • 服务器端再次收到seq=3的数据包,不会重复写入,依旧发送ack=3.

  •  服务器端成功接收四个数据包,客户端收到期待的ack(ack=5),不再等待定时器到时,立刻更新基准窗口为6,开始下一轮发送。

二、核心代码

1.代码实现思路

服务器端为接收方,只需要简单地接收数据包并发送对应ack.

而客户端的实现较为复杂,主要逻辑如下图:

客户端每收到一个ack,就会比较ack与getACK的值,把最大ack赋给getACK.

如果客户端收到了期待的最大ack(即传输过程中没有出现丢包),会立即更新base并开始下一轮发送;

如果客户端未收到期待的最大ack,会陷入等待,定时器到时后会将base更新为getAck+1,已经读取过的数据包重新发送、未读取的数据包读取后再发送。 

 2. 核心源码

 bool Sender::waitForAck() {std::unique_lock<std::mutex> lock(mtx);//如果 `ackReceived` 在超时之前变为 `true`,则 `wait\_for` 返回并继续执行后续代码;//如果超时后 `ackReceived` 仍为 `false`,则 `wait\_for` 也会返回。return cv.wait_for(lock, std::chrono::milliseconds(5*TIMEOUT), [this]() { return ackReceived.load() ; });//超时或者收到所有ack返回
}
 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.flag == 0 && ackPacket.validateChecksum(clientAddr.sin_addr.S_un.S_addr, routerAddr.sin_addr.S_un.S_addr)) {if(ackPacket.ack==65535){continue;}std::lock_guard<std::mutex> lock(mtx);std::cout << "收到ack=" << ackPacket.ack << std::endl;if(ackPacket.ack>getAck){getAck=ackPacket.ack;}if(ackPacket.ack==expectedAck){ackReceived = true;cv.notify_one();}   }}}
}

void Sender::sendFile(const std::string& filename) {//.......base = 0;nextseq = 0;while (true) {//.......//1.基于窗口连续发送N条消息while (nextseq < base + WINDOW_SIZE && !file.eof()) {if(nextseq<3&&AckPacket.flag == 2&&AckPacket.validateChecksum(clientAddr.sin_addr.S_un.S_addr, routerAddr.sin_addr.S_un.S_addr))//2.如果此时又收到了SYN-ACK{//...重新发送}std::unique_lock<std::mutex> lock(mtx);ackReceived = false;expectedAck = base+WINDOW_SIZE-1;Datagram packet;//默认构造,从客户端发往路由器端packet.seq = nextseq;file.read(packet.data, BUFFER_SIZE);packet.dataSize = static_cast<int>(file.gcount());packet.flag = 0;window[nextseq] = packet;//window.emplace(nextseq, packet);sendPacket(packet);std::cout << "发送数据包.SEQ=" << packet.seq <<",校验码="<< packet.checksum << std::endl;nextseq++;lock.unlock();}std::this_thread::sleep_for(std::chrono::milliseconds(TIMEOUT));// 2.结束条件:如果所有包都被确认,则跳出if (base == nextseq && file.eof()) break;if (waitForAck()) {std::unique_lock<std::mutex> lock(mtx);base = base + WINDOW_SIZE;std::cout << "base窗口后移为:" << base << ",开始下一轮发送" << std::endl;} else {std::unique_lock<std::mutex> lock(mtx);if(getAck==-1){base=0;}else{base =getAck+1;}std::cout << "未收到所有ack,base窗口后移为:" << base << ",重新发送此部分" << std::endl;for (int i = base; i < nextseq; ++i) {sendPacket(window[i]);std::cout << "发送数据包.SEQ=" << window[i].seq << ",校验码=" << window[i].checksum << std::endl;}}}//.......}

3.可运行完整源码

已上传github:

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

三、运行演示

在src目录下输入:

 g++ -o cs main.cpp Datagram.cpp Sender.cpp Receiver.cpp -lws2_32

1.正常传输(收到正确序列号)

客户端发送数据包,收到期待ack值,窗口正常后移的情况

服务器端收到期待序列号的数据包,正常发送对应ack的情况

2.乱序抵达(出现丢包情况,收到偏大/偏小数据包)

客户端发送时出现丢包现象,丢失seq=493的数据包,收到的ack值为492.定时器超时后,base窗口后移为493,发送从493到496的数据包。

 服务器端未收到seq=493的数据包,之后再收到seq=494,495的数据包都仅发送ack=492.

版权声明:

本网仅为发布的内容提供存储空间,不对发表、转载的内容提供任何形式的保证。凡本网注明“来源:XXX网络”的作品,均转载自其它媒体,著作权归作者所有,商业转载请联系作者获得授权,非商业转载请注明出处。

我们尊重并感谢每一位作者,均已注明文章来源和作者。如因作品内容、版权或其它问题,请及时与我们联系,联系邮箱:809451989@qq.com,投稿邮箱:809451989@qq.com