老板逼着让写一个基于UDP可靠通信的demo以作为其网络课程的课件内容之一来给学生演示
dup是不可靠的,为了实现可靠文件传输,需要加入额外的机制来确保可靠,在此选用了滑动窗口机制。
滑动窗口介绍:
滑动窗口协议的基本原理就是在任意时刻,发送方都维持了一个连续的允许发送的帧的序号,称为发送窗口;同时,接收方也维持了一个连续的允许接收的帧的序号,称为接收窗口。发送窗口和接收窗口的序号的上下界不一定要一样,甚至大小也可以不同。不同的滑动窗口协议窗口大小一般不同。发送方窗口内的序列号代表了那些已经被发送,但是还没有被确认的帧,或者是那些可以被发送的帧。下面举一个例子(假设发送窗口尺寸为2,接收窗口尺寸为1):
分析:①初始态,发送方没有帧发出,发送窗口前后沿相重合。接收方0号窗口打开,等待接收0号帧;②发送方打开0号窗口,表示已发出0帧但尚确认返回信息。此时接收窗口状态不变;③发送方打开0、1号窗口,表示0、1号帧均在等待确认之列。至此,发送方打开的窗口数已达规定限度,在未收到新的确认返回帧之前,发送方将暂停发送新的数据帧。接收窗口此时状态仍未变;④接收方已收到0号帧,0号窗口关闭,1号窗口打开,表示准备接收1号帧。此时发送窗口状态不变;⑤发送方收到接收方发来的0号帧确认返回信息,关闭0号窗口,表示从重发表中删除0号帧。此时接收窗口状态仍不变;⑥发送方继续发送2号帧,2号窗口打开,表示2号帧也纳入待确认之列。至此,发送方打开的窗口又已达规定限度,在未收到新的确认返回帧之前,发送方将暂停发送新的数据帧,此时接收窗口状态仍不变;⑦接收方已收到1号帧,1号窗口关闭,2号窗口打开,表示准备接收2号帧。此时发送窗口状态不变;⑧发送方收到接收方发来的1号帧收毕的确认信息,关闭1号窗口,表示从重发表中删除1号帧。此时接收窗口状态仍不变。
若从滑动窗口的观点来统一看待1比特滑动窗口、后退n及选择重传三种协议,它们的差别仅在于各自窗口尺寸的大小不同而已。1比特滑动窗口协议:发送窗口=1,接收窗口=1;后退n协议:发窗口>1,接收窗口>1;选择重传协议:发送窗口>1,接收窗口>1。
demo演示截图:
核心代码:
发送方:(接收和发送线程函数)
bool CUDPTrsmtFile::ServerSend()
{
if(!s_SendHoleMsg())
return false;
// 设置文件指针位置,指向上次已发送的大小
SetFilePointer(m_hFile, m_dwSend, NULL, FILE_BEGIN);
int ret;
int nPacketCount = 0;
DWORD dwRet;
SendBuf sendbuf;
DWORD dwRead;
DWORD dwReadSize;
SendBuf* pushbuf;
SetEvent(m_hEvent);
//若已发送大小小于文件大小并且发送窗口前沿等于后沿,则继续发送
//否则退出循环
m_sumPacket = (m_dwFileSize - m_dwSend)/(MAXBUF_SIZE);
if(m_dwSend < m_dwFileSize) // 文件没有传输完时才继续传输
{
while(1)
{
dwRet = WaitForSingleObject(m_hEvent, 1000);
if(dwRet == WAIT_FAILED)
{
CString temp="等待发送信号灯出错!";
SendMessage(the_hWnd,ECHO_MSG,0,LPARAM(&temp));
SendMessage(the_hWnd,FINISHEDTRANS_MSG,0,0);
return false;
}
else if(dwRet == WAIT_TIMEOUT)
{
//重发
int ackindex=0;
::EnterCriticalSection(&m_csQueue); // 进入m_bufqueue的排斥区
ackindex=m_bufqueue.front()->index;
ret = m_hsocket.hsendto((char*)m_bufqueue.front(), sizeof(sendbuf));
::LeaveCriticalSection(&m_csQueue); // 退出m_bufqueue的排斥区
if(ret == SOCKET_ERROR)
{
CString temp="发送窗口中的数据包发送失败,重发数据包!";
SendMessage(the_hWnd,ECHO_MSG,0,LPARAM(&temp));
continue;
}
CString temp;
temp.Format("等待ACK确认消息超时,重发发送窗口的第 %d 个数据包",ackindex);
SendMessage(the_hWnd,ECHO_MSG,0,LPARAM(&temp)