UDP实现可靠文件传输(转)

xueli124283814 2011-10-20 01:22:44
1 UDP实现可靠文件传输 2 大家都清楚,如果用TCP传输文件的话,是很简单的,根本都不用操心会丢包,除非是网络坏了,就得重来。用UDP的话,因为UDP是不可靠的,所以用它传输文件,要保证不丢包,就得我们自己写额外的代码来保障了。本文就说说如果保证可靠传输。 3 4 要实现无差错的传输数据,我们可以采用重发请求(ARQ)协议,它又可分为连续ARQ协议、选择重发ARQ协议、滑动窗口协议。本文重点介绍滑动窗口协议,其它的两种有兴趣的可参考相关的网络通信之类的书。 5 6 采用滑动窗口协议,限制已发送出去但未被确认的数据帧的数目。循环重复使用已收到的那些数据帧的序号。具体实现是在发送端和接收端分别设定发送窗口和接收窗口。 7 (1)发送窗口 8 发送窗口用来对发送端进行流量控制。发送窗口的大小Wt代表在还没有收到对方确认的条件下,发送端最多可以发送的数据帧的个数。具体意思请参考下图: 9 10 11 (2)接收窗口 12 接收窗口用来控制接收数据帧。只有当接收到的数据帧的发送序号落在接收窗口内,才允许将该数据帧收下,否则一律丢弃。接收窗口的大小用Wr来表示,在连续ARQ协议中,Wr = 1。接收窗口的意义可参考下图: 13 14 15 在接收窗口和发送窗口间存在着这样的关系:接收窗口发生旋转后,发送窗口才可能向前旋转,接收窗口保持不动时,发送窗口是不会旋转的。这种收发窗口按如此规律顺时钟方向不断旋转的协议就犯法为滑动窗口协议。 16 17 好了,在上面对滑动窗口协议有大致了解后,我们还是进入正题吧:) 18 19 发送端的发送线程: 20 int ret; 21 int nPacketCount = 0; 22 DWORD dwRet; 23 SendBuf sendbuf; 24 DWORD dwRead; 25 DWORD dwReadSize; 26 27 SendBuf* pushbuf; 28 29 //计算一共要读的文件次数,若文件已读完,但客户端没有接收完, 30 //则要发送的内容不再从文件里读取,而从m_bufqueue里提取 31 nPacketCount = m_dwFileSize / sizeof(sendbuf.buf); 32 33 //若不能整除,则应加1 34 if(m_dwFileSize % sizeof(sendbuf.buf) != 0) 35 ++nPacketCount; 36 37 SetEvent(m_hEvent); 38 39 CHtime htime; 40 41 //若已发送大小小于文件大小并且发送窗口前沿等于后沿,则继续发送 42 //否则退出循环 43 44 if(m_dwSend < m_dwFileSize) // 文件没有传输完时才继续传输 45 { 46 while(1) 47 { 48 dwRet = WaitForSingleObject(m_hEvent, 1000); 49 if(dwRet == WAIT_FAILED) 50 { 51 return false; 52 } 53 else if(dwRet == WAIT_TIMEOUT) 54 { 55 //重发 56 ::EnterCriticalSection(&m_csQueue); // 进入m_bufqueue的排斥区 57 ret = m_hsocket.hsendto((char*)m_bufqueue.front(), sizeof(sendbuf)); 58 ::LeaveCriticalSection(&m_csQueue); // 退出m_bufqueue的排斥区 59 if(ret == SOCKET_ERROR) 60 { 61 cout << "重发失败,继续重发" << endl; 62 continue; 63 } 64 65 ResetEvent(m_hEvent); 66 continue; 67 } 68 69 //若发送窗口大小 < 预定大小 && 已读文件次数(nReadIndex) < 需要读文件的次数(nReadCount),则继续读取发送 70 //否则,要发送的内容从m_bufqueue里提取 71 if(m_dwSend < m_dwFileSize) 72 { 73 dwReadSize = m_dwFileSize - m_dwSend; 74 dwReadSize = dwReadSize < MAXBUF_SIZE ? dwReadSize : MAXBUF_SIZE; 75 76 memset(sendbuf.buf, 0, sizeof(sendbuf.buf)); 77 if(!ReadFile(m_hFile, sendbuf.buf, dwReadSize, &dwRead, NULL)) 78 { 79 //AfxMessageBox("读取文件失败,请确认文件存在或有读取权限."); 80 cout << "读取文件失败,请确认文件存在或有读取权限." << endl; 81 return false; 82 } 83 84 m_dwSend += dwRead; 85 86 sendbuf.index = m_nSendIndexHead; 87 m_nSendIndexHead = (m_nSendIndexHead + 1) % Sliding_Window_Size; // 发送窗口前沿向前移一格 88 89 sendbuf.dwLen = dwRead; 90 91 //保存发送过的数据,以便重发 92 ::EnterCriticalSection(&m_csQueue); // 进入m_bufqueue的排斥区 93 pushbuf = GetBufFromLookaside(); 94 95 memcpy(pushbuf, &sendbuf, sizeof(sendbuf)); 96 97 m_bufqueue.push(pushbuf); 98 if(m_dwSend >= m_dwFileSize) // 文件已读完,在队列中加一File_End标志,以便判断是否需要继续发送 99 { 100 pushbuf = GetBufFromLookaside(); 101 102 pushbuf->index = File_End; 103 pushbuf->dwLen = File_End; 104 memset(pushbuf->buf, 0, sizeof(pushbuf->buf)); 105 106 m_bufqueue.push(pushbuf); 107 } 108 ::LeaveCriticalSection(&m_csQueue); // 退出m_bufqueue的排斥区 109 } 110 111 ::EnterCriticalSection(&m_csQueue); // 进入m_bufqueue的排斥区 112 if(m_bufqueue.front()->index == File_End) // 所有数据包已发送完毕,退出循环 113 { 114 ::LeaveCriticalSection(&m_csQueue); // 退出m_bufqueue的排斥区 115 break; 116 } 117 else if(m_bufqueue.size() <= Send_Window_Size) // 发送窗口小于指定值,继续发送 118 { 119 ret = m_hsocket.hsendto((char*)m_bufqueue.front(), sizeof(sendbuf)); 120 if(ret == SOCKET_ERROR) 121 { 122 ::LeaveCriticalSection(&m_csQueue); // 退出m_bufqueue的排斥区 123 cout << "发送失败,重发" << endl; 124 continue; 125 } 126 127 //延时,防止丢包 128 Sleep(50); 129 } 130 else // 发送窗口大于指定值,等持接收线程接收确认消息 131 { 132 ResetEvent(m_hEvent); 133 } 134 ::LeaveCriticalSection(&m_csQueue); // 退出m_bufqueue的排斥区 135 } 136 } 137 138 发送端的接收线程: 139 int ret; 140 RecvBuf recvbuf; 141 142 while(m_hFile != NULL) 143 { 144 ret = m_hsocket.hrecvfrom((char*)&recvbuf, sizeof(recvbuf)); 145 if(ret == SOCKET_ERROR) 146 { 147 //AfxMessageBox("接收确认消息出错"); 148 ::EnterCriticalSection(&m_csQueue); 149 if(m_bufqueue.front()->index == File_End) // 文件传输完毕 150 { 151 ::LeaveCriticalSection(&m_csQueue); 152 break; 153 } 154 ::LeaveCriticalSection(&m_csQueue); 155 156 cout << "接收确认消息出错: " << GetLastError() << endl; 157 return false; 158 } 159 160 if(recvbuf.flag == Flag_Ack && recvbuf.index == m_nSendIndexTail) 161 { 162 m_nSendIndexTail = (m_nSendIndexTail + 1) % Sliding_Window_Size; 163 164 //该结点已得到确认,将其加入旁视列表,以备再用 165 ::EnterCriticalSection(&m_csQueue); 166 m_bufLookaside.push(m_bufqueue.front()); 167 m_bufqueue.pop(); 168 ::LeaveCriticalSection(&m_csQueue); 169 170 SetEvent(m_hEvent); 171 } 172 } 173 174 接收端的接收线程: 175 int ret; 176 DWORD dwWritten; 177 SendBuf recvbuf; 178 RecvBuf sendbuf; 179 int nerror = 0; 180 181 // 设置文件指针位置,指向上次已发送的大小 182 SetFilePointer(m_hFile, 0, NULL, FILE_END); 183 184 //若已接收文件大小小于需要接收的大小,则继续 185 while(m_dwSend < m_dwFileSize) 186 { 187 //接收 188 memset(&recvbuf, 0, sizeof(recvbuf)); 189 ret = m_hsocket.hrecvfrom((char*)&recvbuf, sizeof(recvbuf)); 190 if(ret == SOCKET_ERROR) 191 { 192 return false; 193 } 194 195 //不是希望接收的,丢弃,继续接收 196 if(recvbuf.index != (m_nRecvIndex) % Sliding_Window_Size) 197 { 198 nerror++; 199 cout << recvbuf.index << "error?" << m_nRecvIndex << endl; 200 continue; 201 } 202 203 if(!WriteFile(m_hFile, recvbuf.buf, recvbuf.dwLen, &dwWritten, NULL)) 204 { 205 //AfxMessageBox("写入文件失败"); 206 cout << "写入文件失败" << endl; 207 return false; 208 } 209 210 //已接收文件大小 211 m_dwSend += dwWritten; 212 213 //发送确认消息 214 sendbuf.flag = Flag_Ack; 215 sendbuf.index = m_nRecvIndex; 216 217 ret = m_hsocket.hsendto((char*)&sendbuf, sizeof(sendbuf)); 218 if(ret == SOCKET_ERROR) 219 { 220 return false; 221 } 222 223 //接收窗口前移一格 224 m_nRecvIndex = (m_nRecvIndex + 1) % Sliding_Window_Size; 225 } 226
--reply by CSDN Study Trial V1.0.1 (starts_2000)
...全文
390 8 打赏 收藏 转发到动态 举报
写回复
用AI写文章
8 条回复
切换为时间正序
请发表友善的回复…
发表回复
孤胆龙少 2011-10-20
  • 打赏
  • 举报
回复
同意五楼大哥的意见
一叶之舟 2011-10-20
  • 打赏
  • 举报
回复
用UDP实现可靠传输,就得象TCP一样来确认数据包已经收到,最好是接收端一包一包的请求,处理完回应。
thinkboy234 2011-10-20
  • 打赏
  • 举报
回复
模拟TCP?
libai 2011-10-20
  • 打赏
  • 举报
回复
本人自己的相法,要保证UDP不丢包,要自设定协议+校验+多次发包.同一个包一次发3个或以上.
龙龙胖 2011-10-20
  • 打赏
  • 举报
回复
顶··

64,637

社区成员

发帖
与我相关
我的任务
社区描述
C++ 语言相关问题讨论,技术干货分享,前沿动态等
c++ 技术论坛(原bbs)
社区管理员
  • C++ 语言社区
  • encoderlee
  • paschen
加入社区
  • 近7日
  • 近30日
  • 至今
社区公告
  1. 请不要发布与C++技术无关的贴子
  2. 请不要发布与技术无关的招聘、广告的帖子
  3. 请尽可能的描述清楚你的问题,如果涉及到代码请尽可能的格式化一下

试试用AI创作助手写篇文章吧