客户端程序:
//#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;