200酬谢!

解决方案 »

  1.   

    是我自己原来写的一个DEMO。 自己曾经测试传5000M的文件,全部正常。 不过由于是DEMO,重在实现原理,对效率照顾不多, 每次发文件数据都只发256个字节,这个太少了。 如果更高一些可能会快很多。
      

  2.   

    参照TCP,双方保存缓冲队列,序列号,确认,超时重传等。
      

  3.   

    自己实现把,又不难。
    其实使用udp进行文件传输最好得用途是在一对多才有效率,否则还是用tcp
      

  4.   

    http://www.vckbase.net/document/viewdoc/?id=440
      

  5.   

    我有一份,是继承CSocket实现的,但是不能把代码给你,只能给你片段,还需要吗?
      

  6.   

    还有一点,我发现我的代码在传输文件时,效率实在不高,没有什么好的解决办法。不过,这样就可以理解qq传输文件为什么要用tcp了,:)
      

  7.   

    QQ的也是用的UDP吧?
    [email protected]
    谢谢!
      

  8.   

    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.hnamespace Easiware{namespace RUDP
    {
    typedef unsigned short uint16;
    typedef unsigned long uint32;
    }} // namespace Easiware
    Flag.h
    //暂无定义
      

  9.   

    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
      

  10.   

    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__;
    #endifnamespace 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