UDP文件传输
- 1、下载文档前请自行甄别文档内容的完整性,平台不提供额外的编辑、内容补充、找答案等附加服务。
- 2、"仅部分预览"的文档,不可在线预览部分如存在完整性等问题,可反馈申请退款(可完整预览的文档不适用该条件!)。
- 3、如文档侵犯您的权益,请联系客服反馈,我们会尽快为您处理(人工客服工作时间:9:00-18:30)。
1.实验目的
了解udp文件传输过程,掌握传输方法。
2.实验内容
要实现无差错的传输数据,我们可以采用重发请求(ARQ)协议,它又可分为连续ARQ 协议、
选择重发ARQ 协议、滑动窗口协议。本文重点介绍滑动窗口协议,其它的两种有兴趣的可参考相关
的网络通信之类的书。
采用滑动窗口协议,限制已发送出去但未被确认的数据帧的数目。循环重复使用已收到的那些数
据帧的序号。具体实现是在发送端和接收端分别设定发送窗口和接收窗口。
3.实验总结
学会了udp协议传输和代码设计,了解了udp的格式。
发送端的发送线程:
int ret;
int nPacketCount = 0;
DWORD dwRet;
SendBuf sendbuf;
DWORD dwRead;
DWORD dwReadSize;
SendBuf* pushbuf;
//计算一共要读的文件次数,若文件已读完,但客户端没有接收完,
//则要发送的内容不再从文件里读取,而从m_bufqueue 里提取
nPacketCount = m_dwFileSize / sizeof(sendbuf.buf);
//若不能整除,则应加1
if(m_dwFileSize % sizeof(sendbuf.buf) != 0)
++nPacketCount;
SetEvent(m_hEvent);
CHtime htime;
//若已发送大小小于文件大小并且发送窗口前沿等于后沿,则继续发送
//否则退出循环
if(m_dwSend < m_dwFileSize) // 文件没有传输完时才继续传输
{
while(1)
{
dwRet = WaitForSingleObject(m_hEvent, 1000);
if(dwRet == WAIT_FAILED)
{
return false;
}
else if(dwRet == WAIT_TIMEOUT)
{
//重发
::EnterCriticalSection(&m_csQueue); // 进入m_bufqueue 的排斥区
ret = m_hsocket.hsendto((char*)m_bufqueue.front(), sizeof(sendbuf));
::LeaveCriticalSection(&m_csQueue); // 退出m_bufqueue 的排斥区
if(ret == SOCKET_ERROR)
{
cout << "重发失败,继续重发" << endl;
continue;
}
ResetEvent(m_hEvent);
continue;
}
//若发送窗口大小< 预定大小&& 已读文件次数(nReadIndex) < 需要读文件的次数(nReadCoun t),则继续读取发送
//否则,要发送的内容从m_bufqueue 里提取
if(m_dwSend < m_dwFileSize)
{
dwReadSize = m_dwFileSize - m_dwSend;
dwReadSize = dwReadSize < MAXBUF_SIZE ? dwReadSize : MAXBUF_SIZE;
memset(sendbuf.buf, 0, sizeof(sendbuf.buf));
if(!ReadFile(m_hFile, sendbuf.buf, dwReadSize, &dwRead, NULL))
{
//AfxMessageBox("读取文件失败,请确认文件存在或有读取权限.");
cout << "读取文件失败,请确认文件存在或有读取权限." << endl;
return false;
}
m_dwSend += dwRead;
sendbuf.index = m_nSendIndexHead;
m_nSendIndexHead = (m_nSendIndexHead + 1) % Sliding_Window_Size; // 发送窗口前沿向前移一格
sendbuf.dwLen = dwRead;
//保存发送过的数据,以便重发
::EnterCriticalSection(&m_csQueue); // 进入m_bufqueue 的排斥区
pushbuf = GetBufFromLookaside();
memcpy(pushbuf, &sendbuf, sizeof(sendbuf));
m_bufqueue.push(pushbuf);
if(m_dwSend >= m_dwFileSize) // 文件已读完,在队列中加一File_End 标志,以便判断是否需要继续发送
{
pushbuf = GetBufFromLookaside();
pushbuf->index = File_End;
pushbuf->dwLen = File_End;
memset(pushbuf->buf, 0, sizeof(pushbuf->buf));
m_bufqueue.push(pushbuf);
}
::LeaveCriticalSection(&m_csQueue); // 退出m_bufqueue 的排斥区
}
::EnterCriticalSection(&m_csQueue); // 进入m_bufqueue 的排斥区
if(m_bufqueue.front()->index == File_End) // 所有数据包已发送完毕,退出循环
{
::LeaveCriticalSection(&m_csQueue); // 退出m_bufqueue 的排斥区
break;
}
else if(m_bufqueue.size() <= Send_Window_Size) // 发送窗口小于指定值,继续发送
{
ret = m_hsocket.hsendto((char*)m_bufqueue.front(), sizeof(sendbuf));
if(ret == SOCKET_ERROR)
{
::LeaveCriticalSection(&m_csQueue); // 退出m_bufqueue 的排斥区
cout << "发送失败,重发" << endl;
continue;
}
//延时,防止丢包