谁有UDP可靠传输文件的代码?

HYWServer 2005-09-12 03:13:57
200酬谢!
...全文
538 20 打赏 收藏 转发到动态 举报
AI 作业
写回复
用AI写文章
20 条回复
切换为时间正序
请发表友善的回复…
发表回复
sct 2005-11-01
  • 打赏
  • 举报
回复
mark
HYWServer 2005-09-21
  • 打赏
  • 举报
回复
你说的程序就简单的发送和接收,根本没做任何丢包处理。
balloy 2005-09-21
  • 打赏
  • 举报
回复
由于牵扯到的文件实在太多,不可能全给你,你就先看看把,看看原理应该是没问题了。有什么不明白的,或者发现设计不合理的,欢迎讨论啊
HYWServer 2005-09-21
  • 打赏
  • 举报
回复
先谢
balloy 2005-09-21
  • 打赏
  • 举报
回复
RUDPSocket.cpp

#include "stdafx.h"
#include <Easiware/RUDP/MFC/RUDPSocket.h>
#include <Easiware/Win32/SystemEx.h>

#ifdef _DEBUG
#define new DEBUG_NEW
#undef THIS_FILE
static char THIS_FILE[] = __FILE__;
#endif

namespace Easiware{

namespace RUDP
{
namespace MFC
{
void CRUDPSocket::CreateSocket(unsigned short nPort)
{
if (!CSocket::Create(nPort, SOCK_DGRAM))
{
throw Net::CSocketException(_T("无法创建Socket!"));
}
}

CRUDPSocket::ReceivedData CRUDPSocket::SendPacket(const void* pBuf, int nBufLen, const sockaddr_in& saTarget, uint32 nAck, uint16 nFlags, bool bAutoACK, long nTimeout)
{
// 发包
uint32 nSeq = _GetAvailableSEQ();
RUDPHeader header(nFlags, nBufLen, nSeq, nAck, 0);
Memory::CDataChunk packet(&header, sizeof(RUDPHeader));
packet.AppendWithoutLen(pBuf, nBufLen);
_SendDataOnly(packet.Data(), packet.Size(), saTarget);

// 等待回复
_WaitForReply(SendingData(header, packet, saTarget), nTimeout);

// 如果需要,发送自动回复包
ReceivedData& ack = _PeekReceivedData(nSeq);
if ( bAutoACK && (ack.Header.SEQNumber > 0) )
SendACKPacket(ack.Sender, ack.Header.SEQNumber);

// 返回接收到的数据
return _PopReceivedData(nSeq);
}

void CRUDPSocket::SendUniPacket(const void* pBuf, int nBufLen, const sockaddr_in& saTarget, uint32 nAck, uint16 nFlags)
{
// 发包
RUDPHeader header(nFlags, nBufLen, 0, nAck, 0);
Memory::CDataChunk packet(&header, sizeof(RUDPHeader));
packet.AppendWithoutLen(pBuf, nBufLen);
_SendDataOnly(packet.Data(), packet.Size(), saTarget);
}

void CRUDPSocket::SendACKPacket(const sockaddr_in& saTarget, uint32 nAck, uint16 nFlags)
{
SendUniPacket(NULL, 0, saTarget, nAck, 0);
}


void CRUDPSocket::OnReceive(int nErrorCode)
{
if (nErrorCode == WSAENETDOWN)
{
_OnReceiveError(_T("接受数据时发生错误!"));
return;
}

// 收包
char szBuffer[MAX_PACKET_SIZE] = "";
sockaddr_in saSender;
int nTmp = sizeof(sockaddr_in);
int nReceived = CSocket::ReceiveFrom(szBuffer, MAX_PACKET_SIZE, (sockaddr*)&saSender, &nTmp);
if (nReceived == SOCKET_ERROR)
{
_OnReceiveError(_T("接受数据时发生错误!"));
return;
}

// 分析得到报头、数据
RUDPHeader header(szBuffer, sizeof(RUDPHeader));
ReceivedData data(header,
Memory::CDataChunk(szBuffer + sizeof(RUDPHeader), nReceived - sizeof(RUDPHeader)),
saSender);

// 如果需要,写入接收队列
uint32 nAck = header.ACKNumber;
if ((nAck > 0) && _IsSEQExists(nAck))
{
m_mapReceived.insert( ReceivedDataMap::value_type(nAck, data) );
}
else
_OnReceivePacket(data);
}

void CRUDPSocket::_OnReceiveError(const TCHAR* szErr)
{
// do nothing
}

void CRUDPSocket::_WaitForReply(const SendingData& info, long nTimeout)
{
const int CHECK_INTERVAL = 200;
if (nTimeout == 0) nTimeout = m_nTimeOut;

// 取得 Sequence Number
uint32 nSeq = info.Header.SEQNumber;

// 写入发送队列
m_mapSending.insert( SendingDataMap::value_type(nSeq, info) );

// 这里实现了重试机制
int nMax = ( nTimeout / CHECK_INTERVAL );
for (int i=0; i < m_nRetryCount; ++i)
{
// 轮询等待接受到ACK包
for (int j=0; j < nMax; ++j)
{
if (_IsACKReceived(nSeq)) return;

::Sleep(CHECK_INTERVAL);
Easiware::Win32::CSystemEx::DoEvents();
}

// 如果没有成功,重新发包,再次尝试
SendingData& data = _GetSendingData(nSeq);
_SendDataOnly(data.Packet.Data(), data.Packet.Size(), data.Target);
}

// 没有收到ACK包,只好将该包从发送队列中删除,并报告错误
m_mapSending.erase(nSeq);
throw Net::CSocketTimeoutException();
}

// 确保得到不重复的Sequence Number
uint32 CRUDPSocket::_GetAvailableSEQ() const
{
for (uint32 nSeq=1; _IsSEQExists(nSeq) || _IsACKReceived(nSeq); ++nSeq);
return nSeq;
}

// 判断当前是否已经存在指定的Sequence Number
bool CRUDPSocket::_IsSEQExists(uint32 nSeq) const
{
return ( m_mapSending.find(nSeq) != m_mapSending.end() );
}

// 判断是否已经收到ACK包
bool CRUDPSocket::_IsACKReceived(uint32 nSeq) const
{
return ( m_mapReceived.find(nSeq) != m_mapReceived.end() );
}

CRUDPSocket::SendingData& CRUDPSocket::_GetSendingData(uint32 nSeq)
{
SendingDataMap::iterator it = m_mapSending.find(nSeq);
return it->second;
}

CRUDPSocket::ReceivedData& CRUDPSocket::_PeekReceivedData(uint32 nSeq)
{
ReceivedDataMap::iterator it = m_mapReceived.find(nSeq);
return it->second;
}

CRUDPSocket::ReceivedData CRUDPSocket::_PopReceivedData(uint32 nSeq)
{
CRUDPSocket::ReceivedData data(_PeekReceivedData(nSeq));

m_mapSending.erase(nSeq);
m_mapReceived.erase(nSeq);

return data;
}

void CRUDPSocket::_SendDataOnly(const void* pBuf, int nBufLen, const sockaddr_in& saTarget)
{
int nSended = CSocket::SendTo(pBuf, nBufLen, (const sockaddr*)&saTarget, sizeof(sockaddr));
if (nSended == SOCKET_ERROR)
throw Net::CSocketException(_T("发送数据时发生错误!"));
}

}
}

} // namespace Easiware
balloy 2005-09-21
  • 打赏
  • 举报
回复
2、核心类

RUDPSocket.h

#include <map>
#include <afxsock.h>
#include <Easiware/RUDP/RUDPHeader.h>
#include <Easiware/Memory/DataChunk.h>
#include <Easiware/Net/SocketException.h>


namespace Easiware{

namespace RUDP
{
namespace MFC
{
/// RUDP:Reliable UDP,基于UDP实现了可靠的传输
class CRUDPSocket : public CSocket
{
private:
using CSocket::Accept;
using CSocket::Bind;
using CSocket::Connect;
using CSocket::Listen;
using CSocket::Receive;
using CSocket::ReceiveFrom;
using CSocket::Send;
using CSocket::SendTo;
using CSocket::Create;

protected: // SendingData
struct SendingData
{
public:
RUDPHeader Header;
Memory::CDataChunk Packet;
sockaddr_in Target;
public:
SendingData(const RUDPHeader& header, const Memory::CDataChunk& packet, const sockaddr_in& saTarget)
: Header(header), Packet(packet), Target(saTarget)
{}
};
typedef std::map<uint32, SendingData> SendingDataMap;

protected: // ReceivedData
struct ReceivedData
{
public:
RUDPHeader Header;
Memory::CDataChunk Payload;
sockaddr_in Sender;
public:
ReceivedData(const RUDPHeader& header, const Memory::CDataChunk& data, const sockaddr_in& saSender)
: Header(header), Payload(data), Sender(saSender)
{}
};
typedef std::map<uint32, ReceivedData> ReceivedDataMap;

public:
CRUDPSocket()
: m_nRetryCount(1)
, m_nTimeOut(6000)
{}
virtual ~CRUDPSocket() {}

public:
/// 创建Socket并绑定端口,开始侦听
void CreateSocket(unsigned short nPort = 0);

/// 发送数据(要求接收方发送响应包), 返回接受到的回应包
ReceivedData SendPacket(const void* pBuf, int nBufLen, const sockaddr_in& saTarget, uint32 nAck = 0, uint16 nFlags = 0, bool bAutoACK = true, long nTimeout = 0);

/// 发送单向数据(不要求接收方发送响应包)
void SendUniPacket(const void* pBuf, int nBufLen, const sockaddr_in& saTarget, uint32 nAck = 0, uint16 nFlags = 0);

void SendACKPacket(const sockaddr_in& saTarget, uint32 nAck = 0, uint16 nFlags = 0);

public:
long GetRetryCount() const { return m_nRetryCount; }
void SetRetryCount(long v) { m_nRetryCount = v; }

/// in milliseconds
long GetTimeout() const { return m_nTimeOut; }
void SetTimeout(long v) { m_nTimeOut = v; }

private: // implements of CSocket
virtual void OnReceive(int nErrorCode);

protected: // virtual functions
virtual void _OnReceivePacket(ReceivedData& data) = 0;
virtual void _OnReceiveError(const TCHAR* szErr);

protected:
SendingData& _GetSendingData(uint32 nSeq);

ReceivedData& _PeekReceivedData(uint32 nSeq);
ReceivedData _PopReceivedData(uint32 nSeq);

void _SendDataOnly(const void* pBuf, int nBufLen, const sockaddr_in& saTarget);

private:
uint32 _GetAvailableSEQ() const;
bool _IsSEQExists(uint32 nSeq) const;

bool _IsACKReceived(uint32 nSeq) const;

void _WaitForReply(const SendingData& info, long nTimeout);

protected:
SendingDataMap m_mapSending;
ReceivedDataMap m_mapReceived;

private:
long m_nTimeOut;
long m_nRetryCount;
};
}
}

} // namespace Easiware


balloy 2005-09-21
  • 打赏
  • 举报
回复
1、部分头文件定义


RUDPHeader.h

#include <assert.h>
#include <Easiware/RUDP/Type.h>
#include <Easiware/RUDP/Flag.h>

namespace Easiware{

namespace RUDP
{
const int MAX_PACKET_SIZE = 16384; //16K

#pragma pack(1)
struct RUDPHeader
{
public:
uint16 Flags;
uint16 DataLen;
uint32 SEQNumber;
uint32 ACKNumber;
uint32 CheckSum;
public:
RUDPHeader(uint16 nFlags = 0, uint16 nLen = 0, uint32 nSeq = 0, uint32 nAck = 0, uint32 nSum = 0)
: Flags(nFlags), DataLen(nLen), SEQNumber(nSeq), ACKNumber(nAck), CheckSum(nSum)
{}

RUDPHeader(const void* pBuffer, size_t nBufferSize)
{
if (nBufferSize > sizeof(RUDPHeader))
nBufferSize = sizeof(RUDPHeader);
memcpy(this, pBuffer, nBufferSize);
}
};
#pragma pack()
}

} // namespace Easiware


Type.h

namespace Easiware{

namespace RUDP
{
typedef unsigned short uint16;
typedef unsigned long uint32;
}

} // namespace Easiware


Flag.h
//暂无定义
HYWServer 2005-09-21
  • 打赏
  • 举报
回复
QQ的也是用的UDP吧?
zhchyg@163.com
谢谢!
balloy 2005-09-21
  • 打赏
  • 举报
回复
还有一点,我发现我的代码在传输文件时,效率实在不高,没有什么好的解决办法。不过,这样就可以理解qq传输文件为什么要用tcp了,:)
balloy 2005-09-21
  • 打赏
  • 举报
回复
我有一份,是继承CSocket实现的,但是不能把代码给你,只能给你片段,还需要吗?
54783szg 2005-09-20
  • 打赏
  • 举报
回复
http://www.vckbase.net/document/viewdoc/?id=440
HYWServer 2005-09-20
  • 打赏
  • 举报
回复
up
HYWServer 2005-09-13
  • 打赏
  • 举报
回复
upup
hailiang 2005-09-13
  • 打赏
  • 举报
回复
如果只是一对一:
发送方:
1。文件开始请求,包含文件共分的包数,等对方应答。
2。读文件 延迟 发送 直到文件结束。(至于包大小,延迟时间可以测试找到丢包相对少得合适数据)
3。是否可以结束请求,等待对方应答,缺哪些包传哪些包,直到结束。

接受方:
1。收到开始请求,准备接受数据
2。接受数据写入文件,并统计哪些包已经接受了
3。收到结束请求时,监察是否全部接受了,把没有接受到的包编号全部发出,直到全部接收到响应一个结束。
hailiang 2005-09-13
  • 打赏
  • 举报
回复
自己实现把,又不难。
其实使用udp进行文件传输最好得用途是在一对多才有效率,否则还是用tcp
softrain 2005-09-13
  • 打赏
  • 举报
回复
参照TCP,双方保存缓冲队列,序列号,确认,超时重传等。
HYWServer 2005-09-13
  • 打赏
  • 举报
回复
是不是发送方每发送一包数据就等待对方的应答?
而接收方只有受到数据才发送一份确认应答?
flashboy 2005-09-13
  • 打赏
  • 举报
回复
是我自己原来写的一个DEMO。 自己曾经测试传5000M的文件,全部正常。 不过由于是DEMO,重在实现原理,对效率照顾不多, 每次发文件数据都只发256个字节,这个太少了。 如果更高一些可能会快很多。
HYWServer 2005-09-12
  • 打赏
  • 举报
回复
zhchyg@163888.net
HYWServer 2005-09-12
  • 打赏
  • 举报
回复
谁有呀?

18,363

社区成员

发帖
与我相关
我的任务
社区描述
VC/MFC 网络编程
c++c语言开发语言 技术论坛(原bbs)
社区管理员
  • 网络编程
加入社区
  • 近7日
  • 近30日
  • 至今
社区公告
暂无公告

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