1.题目描述
滑动窗口协议以基于分组的数据传输协议为特征,该协议适用于在数据链路层以及传输层中对按 顺序传送分组的可靠性要求较高的环境。在长管道传输过程(特别是无线环境)中,相应的滑动窗口 协议可实现高效的重传恢复。附录 3 给出了一个选择性重传的滑动窗口协议的简单实现,以此为参考, 设计并实现一个滑动窗口协议的仿真,显示数据传送过程中的各项具体数据,双方帧的个数变化,帧 序号,发送和接受速度,重传提示等。
2.程序演示
这里我们使用Socket来模拟滑动窗口协议,实现了双方帧的个数变化,帧序号,发送和接受速度控制,重传显示。
控制帧发送速度
双方帧的个数变化
丢失包
3.参考代码
接收端代码
#include <winsock2.h>
#include <iostream>
#include <list>
#include <time.h>
#include <unistd.h>
#pragma comment(lib, "ws2_32.lib")
using namespace std;
//函数说明------------------------------
DWORD WINAPI ThreadFun(LPVOID lpThreadParameter);
void init_app();
//---------------------------------------
WSADATA wd;
SOCKET Socket;
sockaddr_in addrClient;
int len = sizeof(sockaddr_in);
//变量------------------------------
struct Data {
//消息类型定义
int Type_ACK = 0;
int Type_Msg = 1;
int Type_Requst = 2;
int Type_Retransmission=3;
//消息内容
int Msg_Code = 0;//消息序号
int Msg_Type = 1;//消息类型
int Msg_ACK=0;//是否已经ACK了
char *Msg_Content[128];//消息内容
int Send_WinSize = 4;//发送窗口大小
};
Data Send_Msg_Data;
Data *Get_Msg_Data;
char Get_buf[1024] = {0}, send_buf[1024] = {0};
int main() {
//提示=======================================================================
cout << "滑动窗口协议仿真(Socket模拟)_接收方" << endl;
//初始化=======================================================================
init_app();
//==========================================================================
}
DWORD WINAPI ThreadFun(LPVOID lpThreadParameter) {
// 接受数据
SOCKET This_Socket = (SOCKET) lpThreadParameter;
cout << "*连接成功" << endl;
// 循环接收客户端数据
int ret = 0;
do {
//接收
ret = recv(This_Socket, Get_buf, sizeof(Get_buf), 0);
Get_Msg_Data = (Data *) Get_buf;
cout << "\n收到消息:" << endl;
cout << "类型:" << Get_Msg_Data->Msg_Type << " 序号: " << Get_Msg_Data->Msg_Code << endl;
//发送
if (Get_Msg_Data->Msg_Type==3)
{
Send_Msg_Data.Msg_Type =3;
} else
{
Send_Msg_Data.Msg_Type = 0;
}
Send_Msg_Data.Msg_Code = Get_Msg_Data->Msg_Code;
memcpy(send_buf, &Send_Msg_Data, sizeof(Data));
sleep(1);
if (send(This_Socket, send_buf, sizeof(send_buf), 0) > 0) {
cout << "\n回复帧:" << Send_Msg_Data.Msg_Code << " ACK" << endl;
} else {
cout << "\n失败回复:" << Send_Msg_Data.Msg_Code << " ACK" << endl;
}
} while (ret != SOCKET_ERROR && ret != 0);
return 0;
}
void init_app() {
if (WSAStartup(MAKEWORD(2, 2), &wd) != 0) {
cout << "WSAStartup Error:" << WSAGetLastError() << endl;
return;
}
// 创建流式套接字
Socket = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
if (Socket == INVALID_SOCKET) {
cout << "socket error:" << WSAGetLastError() << endl;
return;
}
//绑定端口和ip
sockaddr_in addr;
memset(&addr, 0, sizeof(sockaddr_in));
addr.sin_family = AF_INET;
addr.sin_port = htons(8000);
addr.sin_addr.s_addr = inet_addr("127.0.0.1");
//服务端bind绑定
if (bind(Socket, (SOCKADDR *) &addr, len) == SOCKET_ERROR) {
cout << "bind Error:" << WSAGetLastError() << endl;
return;
}
// 监听
listen(Socket, 5);
//主线程循环接收客户端的连接
while (true) {
cout << "*等待连接..." << endl;
// 接受成功返回与client通讯的Socket
SOCKET Client = accept(Socket, (SOCKADDR *) &addrClient, &len);
if (Client != INVALID_SOCKET) {
// 创建线程,并且传入与client通讯的套接字
HANDLE hThread = CreateThread(NULL, 0, ThreadFun, (LPVOID) Client, 0, NULL);
CloseHandle(hThread); // 关闭对线程的引用
}
}
}
发送端代码
#include<winsock2.h>//winsock2的头文件
#include<iostream>
#include <list>
#include <ctime>
#pragma comment(lib, "ws2_32.lib")
using namespace std;
//定义========================
void Init_Socket();
void *SendMsg(void *pVoid);
int getRand(int min, int max);
void Sycn();
void Send_Win_Move();
void Retransmission(int Num);
//发送的数据=======================
struct Data {
//消息类型定义
int Type_ACK = 0;
int Type_Msg = 1;
int Type_Requst = 2;
int Type_Retransmission = 3;
//消息内容
int Msg_Code = 0;//消息序号
int Msg_Type = 1;//消息类型
int Msg_ACK = 0;//是否已经ACK了
char *Msg_Content[128];//消息内容
int Send_WinSize = 4;//发送窗口大小
};
//默认参数=======================
int Rand_Num = 5;//随机概率1/5
int Receiver_WinSize = 10;
int Send_WinSize = 5;
int Send_Size = 10;
int ACK_OutTime = 10;
int Send_Num = 100;
char *Send_Msg = "0123456789";
SOCKET Socket;
int Win_Now_Size = 0;
//消息列表=======================
list<Data> MSG_Win_List;
Data Send_Msg_Data;
Data *Get_Msg_Data;
Data *Temp_Msg_Data;
int Num = 0;
char send_buf[1024] = {0}, Get_buf[1024] = {0};
void init_data() {
char auto_data;
//提示=======================================================================
cout << "*====滑动窗口协议仿真(Socket模拟)_发送方====*\n";
cout << "\n-------------------------------\n";
cout << " 请输入必要参数(y/n):";
cin >> auto_data;
if (auto_data == 'n') {
cout << " 发送窗口: ", cout << Send_WinSize << endl;
cout << " 消息帧数: ", cout << Send_Num << endl;
// cout << " 发送内容: ", cout << Send_Msg << endl;
} else {
cout << " 发送窗口: ", cin >> Send_WinSize;
cout << " 消息帧数: ", cin >> Send_Num;
}
cout << "-------------------------------" << endl;
system("pause");
}
int main() {
init_data();
Init_Socket();
//接收服务端的消息
pthread_t tids;
pthread_create(&tids, NULL, SendMsg, &Socket);
//随时给服务端发消息
do {
int ret = 0;
do {
ret = recv(Socket, Get_buf, sizeof(Get_buf), 0);
Get_Msg_Data = (Data *) Get_buf;
if (ret != SOCKET_ERROR && ret != 0) {
cout << "\n\n\t==>收到帧:" << " ACK: " << Get_Msg_Data->Msg_Code << endl;
list<Data>::iterator iter;
for (iter = MSG_Win_List.begin(); iter != MSG_Win_List.end(); iter++) {
if (Get_Msg_Data->Msg_Code == iter->Msg_Code) {
(*iter).Msg_ACK = 1;
if (Get_Msg_Data->Msg_Type == 3) {
cout << "\n\t重传删除了一个" << endl;
Win_Now_Size--;
MSG_Win_List.erase(iter);
}
break;
}
}
Sycn();
}
} while (ret != SOCKET_ERROR && ret != 0);
} while (true);
//关闭监听套接字
closesocket(Socket);
WSACleanup();
}
void Init_Socket() {
WSADATA wd;
//加载winsock2的环境
if (WSAStartup(MAKEWORD(2, 2), &wd) != 0) {
cout << "WSAStartup error:" << GetLastError() << endl;
return;
}
//创建流式套接字
Socket = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
if (Socket == INVALID_SOCKET) {
cout << "socket error:" << GetLastError() << endl;
return;
}
//连接服务器
sockaddr_in addr;
addr.sin_family = AF_INET;
addr.sin_port = htons(8000);
addr.sin_addr.s_addr = inet_addr("127.0.0.1");
int len = sizeof(sockaddr_in);
if (connect(Socket, (SOCKADDR *) &addr, len) == SOCKET_ERROR) {
cout << "connect error:" << GetLastError() << endl;
return;
}
}
void *SendMsg(void *pVoid) {
do {
Send_Win_Move();
Sycn();
system("pause");
Send_Msg_Data.Msg_Code = Num;
Send_Msg_Data.Msg_Type = 1;
memcpy(send_buf, &Send_Msg_Data, sizeof(Data));
if (Win_Now_Size == Send_WinSize) {
list<Data>::iterator iter;
for (iter = MSG_Win_List.begin(); iter != MSG_Win_List.end(); iter++) {
if (iter->Msg_ACK == 0) {
Send_Msg_Data.Msg_Code = iter->Msg_Code;
Send_Msg_Data.Msg_Type = 3;
memcpy(send_buf, &Send_Msg_Data, sizeof(Data));
if (send(Socket, send_buf, sizeof(send_buf), 0) > 0) {
cout << "\n\t<==发送重传帧:" << iter->Msg_Code << endl;
} else {
cout << "\n\t<==失败发送重传帧:" << iter->Msg_Code << endl;
}
break;
}
}
continue;
}
if (getRand(1, Rand_Num) == Rand_Num)//随机丢失
{
cout << "\n\t<=xxxx=随机丢失帧:" << Num++ << endl;
} else {
if (send(Socket, send_buf, sizeof(send_buf), 0) > 0) {
cout << "\n\t<==发送帧:" << Num++ << endl;
} else {
cout << "\n\t<<==失败发送帧:" << Num++ << endl;
}
}
Win_Now_Size++;
Send_Num--;
MSG_Win_List.push_back(Send_Msg_Data);
} while (Send_Num > 0);
}
void Send_Win_Move() {
if (MSG_Win_List.begin()->Msg_ACK == 0) return;
list<Data>::iterator iter=MSG_Win_List.begin();
while (iter->Msg_ACK==1)
{
MSG_Win_List.erase(iter);
Win_Now_Size--;
iter=MSG_Win_List.begin();
}
cout << "\n\t窗口移动了" << endl;
}
int getRand(int min, int max) {
return (rand() % (max - min + 1)) + min;
}
void Sycn() {
cout << "\n\t------------------------------" << endl;
cout << "\t当前发送窗口:" << Send_WinSize << "\t可用窗口大小:" << Send_WinSize - Win_Now_Size << endl;
list<Data>::iterator iter;
for (iter = MSG_Win_List.begin(); iter != MSG_Win_List.end(); iter++) {
cout << "\t#帧序号:" << iter->Msg_Code << "\t#是否ACK:" << iter->Msg_ACK << endl;
}
cout << "\t------------------------------" << endl;
}
4.导入ws2_32库到Clion :
导入ws2_32库到Clion项目-CSDN博客
2024 HNUST计算机网络课程设计-(ᕑᗢᓫ∗)˒芒果酱-参考文章
(代码可以参考,૮₍ ˃ ⤙ ˂ ₎ა 但同学们要认真编写哦)
-------------------------------------------------------------------------
1、网络聊天程序的设计与实现
C++ Socket 多线程 网络聊天室 支持用户端双向交流(2023)-CSDN博客
2、Tracert 与 Ping 程序设计与实现
Tracert 与 Ping 程序设计与实现(2024)-CSDN博客
3、滑动窗口协议仿真
滑动窗口协议仿真(2024)-CSDN博客
4、OSPF 路由协议原型系统设计与实现
OSPF 路由协议原型系统设计与实现-CSDN博客
5、基于 IP 多播的网络会议程序
基于 IP 多播的网络会议程序(2024)-CSDN博客
6、编程模拟 NAT 网络地址转换
编程模拟 NAT 网络地址转换(2024)-CSDN博客
7、网络嗅探器的设计与实现
网络嗅探器的设计与实现(2024)-转载-CSDN博客
8、网络报文分析程序的设计与实现
网络报文分析程序的设计与实现(2024)-CSDN博客
9、简单 Web Server 程序的设计与实现
简单 Web Server 程序的设计与实现 (2024)-CSDN博客
10、路由器查表过程模拟计算机网络 - 路由器查表过程模拟 C++(2024)-CSDN博客