计算机网络之GBN协议

客户端程序:

//#include "stdafx.h" //创建 VS 项目包含的预编译头文件
#include <stdlib.h>
#include <time.h>
#include <WinSock2.h>
#include <fstream>
#include<iostream>
#include<stdio.h>
#include <io.h> 
#pragma comment(lib,"ws2_32.lib")
#define SERVER_PORT  12340  //端口号
#define SERVER_IP  "0.0.0.0" //IP 地址
const int BUFFER_LENGTH = 1026; //缓冲区大小, (以太网中 UDP 的数据帧中包长度应小于 1480 字节)
const int SEND_WIND_SIZE = 10;//发送窗口大小为 10,GBN 中应满足 W + 1 <=N(W 为发送窗口大小,N 为序列号个数)
//本例取序列号 0...19 共 20 个
//如果将窗口大小设为 1,则为停-等协议
const int SEQ_SIZE = 20; //序列号的个数,从 0~19 共计 20 个
//由于发送数据第一个字节如果值为 0, 则数据会发送失败
//因此接收端序列号为 1~20,与发送端一一对应
BOOL ack[SEQ_SIZE];//收到 ack 情况,对应 0~19 的 ack
int curSeq;//当前数据包的 seq
int curAck;//当前等待确认的 ack
int totalSeq;//收到的包的总数
int totalPacket;//需要发送的包总数
int size;
int flag = 0; //标识是否收到最后一个ack
//************************************
// Method: getCurTime
// FullName: getCurTime
// Access: public
// Returns: void
// Qualifier: 获取当前系统时间,结果存入 ptime 中
// Parameter: char * ptime
//************************************
void getCurTime(char *ptime) {
	char buffer[128];
	memset(buffer, 0, sizeof(buffer));
	time_t c_time;
	struct tm *p;
	p = new tm;
	time(&c_time);
	localtime_s(p, &c_time);
	sprintf_s(buffer, "%d/%d/%d %d:%d:%d",
		p->tm_year + 1900,
		p->tm_mon,
		p->tm_mday,
		p->tm_hour,
		p->tm_min,
		p->tm_sec);
	strcpy_s(ptime, sizeof(buffer), buffer);
}
//************************************
// Method: seqIsAvailable
// FullName: seqIsAvailable
// Access: public
// Returns: bool
// Qualifier: 当前序列号 curSeq 是否可用
//************************************
bool seqIsAvailable() {
	int step;
	step = curSeq - curAck;
	step = step >= 0 ? step : step + SEQ_SIZE;
	//序列号是否在当前发送窗口之内
	if (step >= SEND_WIND_SIZE) {
		return false;
	}
	if (ack[curSeq]) {
		return true;
	}
	return false;
}
//************************************
// Method: timeoutHandler
// FullName: timeoutHandler
// Access: public
// Returns: void
// Qualifier: 超时重传处理函数,滑动窗口内的数据帧都要重传
//************************************
void timeoutHandler() {
	printf("Timer out error.\n");
	int index;
	for (int i = 0; i< SEND_WIND_SIZE; ++i) {
		index = (i + curAck) % SEQ_SIZE;
		ack[index] = TRUE;
	}
	totalSeq -= SEND_WIND_SIZE;
	curSeq = curAck;
}
//************************************
// Method: ackHandler
// FullName: ackHandler
// Access: public
// Returns: void
// Qualifier: 收到 ack,累积确认,取数据帧的第一个字节
//由于发送数据时,第一个字节(序列号)为 0(ASCII)时发送失败,因此加一了,此处需要减一还原
// Parameter: char c
//************************************
void ackHandler(char c) {
	unsigned char index = (unsigned char)c - 1; //序列号减一
	printf("Recv a ack of %d\n", index);
	if (index >= (size / 1024))
		flag = 1;
	if (curAck <= index) {
		for (int i = curAck; i <= index; ++i) {
			ack[i] = TRUE;
		}
		curAck = (index + 1) % SEQ_SIZE;
	}
	else {
		//ack 超过了最大值,回到了 curAck 的左边
		for (int i = curAck; i< SEQ_SIZE; 
  • 10
    点赞
  • 40
    收藏
    觉得还不错? 一键收藏
  • 3
    评论
以下是GBN协议的Python代码实现: 发送端代码: ```python from socket import * import time serverName = 'localhost' serverPort = 12000 # 创建客户端套接字 clientSocket = socket(AF_INET, SOCK_DGRAM) # 设置超时时间 clientSocket.settimeout(1) # 发送窗口大小 N = 4 # 初始化变量 base = 0 nextseqnum = 0 pkt = [] # 读取文件 f = open('file.txt', 'r') data = f.read() # 分割数据 for i in range(0, len(data), 500): pkt.append(data[i:i+500]) while True: # 发送窗口未满 if nextseqnum < base + N: # 发送数据包 message = pkt[nextseqnum] clientSocket.sendto(message.encode(), (serverName, serverPort)) # 输出发送信息 print('Sent packet', nextseqnum) # 第一个数据包发送时开始计时 if nextseqnum == base: start_time = time.time() # 增加序列号 nextseqnum += 1 # 发送完最后一个数据包,退出循环 if nextseqnum == len(pkt): break try: # 接收ACK ack, serverAddress = clientSocket.recvfrom(2048) # 解析ACK序号 acknum = int(ack.decode()) # 输出接收信息 print('Received ACK', acknum) # 确认ACK是否在窗口内 if acknum >= base and acknum < nextseqnum: # 滑动窗口 base = acknum + 1 # 判断是否收到最后一个ACK if base == len(pkt): break except timeout: # 超时重传 print('Timeout, sequence number =', base) nextseqnum = base # 重新发送窗口内的数据包 for i in range(base, base+N): message = pkt[i] clientSocket.sendto(message.encode(), (serverName, serverPort)) print('Resent packet', i) # 重置计时器 start_time = time.time() # 输出传输时间 end_time = time.time() print('Transmission time:', end_time - start_time, 'seconds') # 关闭套接字 clientSocket.close() ``` 接收端代码: ```python from socket import * serverPort = 12000 # 创建服务器套接字 serverSocket = socket(AF_INET, SOCK_DGRAM) # 绑定服务器端口 serverSocket.bind(('', serverPort)) # 接收窗口大小 N = 4 # 初始化变量 expectedseqnum = 0 while True: # 接收数据包 message, clientAddress = serverSocket.recvfrom(2048) # 解析数据包序号 seqnum = int(message.decode()) # 输出接收信息 print('Received packet', seqnum) # 判断是否是期望的数据包 if seqnum == expectedseqnum: # 发送ACK ack = str(seqnum) serverSocket.sendto(ack.encode(), clientAddress) # 输出发送信息 print('Sent ACK', seqnum) # 增加期望序号 expectedseqnum += 1 # 判断是否是重复的数据包 elif seqnum < expectedseqnum: # 发送ACK ack = str(seqnum) serverSocket.sendto(ack.encode(), clientAddress) # 输出发送信息 print('Sent ACK', seqnum) # 判断是否是超出窗口范围的数据包 else: # 不发送ACK,等待发送端重传 print('Discarded packet', seqnum) ```

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值