C++ Builder下三种UDP通信实现方法的比较


 
 主要讨论一下数据的接受:
1.NMUDP控件
   这个控件使用起来比较简单,设定监听端口,然后响应DataReceived事件就可以了,例如:
void __fastcall TMoniter::NMUDPDataReceived(TComponent *Sender,
      int NumberBytes, AnsiString FromIP, int Port)
{
    /* 用一个标志变量控制控件受信后是否执行需要的操作 */
    if (recvFlag)
    {
        int rl;
        /* 用于接受数据的内存 */
        unsigned char rbuf[1024 * 9]; 

        /* 控件的ReadBuffer方法,把接受到的数据存储到rbuf */
        NMUDP -> ReadBuffer(rbuf , sizeof(rbuf) , rl);
        
        /* 字符串结束 */
        rbuf[rl]=0;

        /* stream是事先定义的文件指针 */
        if (stream != NULL)
        {
            /* 自编doLog函数,把接收数据写入日志文件 */
            doLog( false , rbuf ,rl ;
        }
    }
}
这个控件的优点是使用简单、效率比较高,但是只支持2K的缓冲,所以上面开辟的9K内存是多余的。2K的限制使我在项目中不得不放弃了这个控件。

2.IdUDPServer控件
  使用方法跟NMUDP差不多,响应UDPRead事件就可以了,例如:(注释参考1)
void __fastcall TMoniter::IdUDPServer1UDPRead(TObject *Sender,
      TStream *AData, TIdSocketHandle *ABinding)
{
    if (recvFlag)
    {
        int r1;
        unsigned char rbuf[1024 * 9];

        r1 =  AData->Size;
        /* 接受到的数据是存放在数据流AData中的,把它们读到rbuf里去 */
        AData->Read(rbuf , r1);
        rbuf[r1] = 0;

        if (stream != NULL)
        {
            doLog( false , rbuf ,r1);
        }
    }
}
这个控件支持了9K的缓冲,但是效率……我需要1秒钟接收150个1K多的数据包并解码后逐行显示在StringGrid中,虽然主要是对StringGrid的描绘浪费时间,但IdUDPServer还是不能令人满意。

3.回归自然吧——Socket
两个控件都不能满足我的需要,那么只能回头考虑底层的socket(我的C不好,对这个方法现在还不是很明白,所以注释很少,不过通过代码能大约猜出其功能)。
先定义这三个东东:
SOCKET sock
WSADATA wsaData
SOCKADDR_IN sockaddr
然后在需要开始受信的地方进行socket初始化,这里我用了一个按钮:
   int  result;
   WORD wVersionRequested;

    wVersionRequested = MAKEWORD(1,1);
    if((result = WSAStartup(wVersionRequested,&wsaData))!=0)
    {
         Application->MessageBoxA("Socket Initial Error","Error",MB_OK);
         WSACleanup();
         return;
    }

    memset(&sockaddr,0,sizeof(sockaddr));
    /* 设置端口号     */
    sockaddr.sin_port=htons(3000);
    sockaddr.sin_family=AF_INET;
    sockaddr.sin_addr.S_un.S_addr=htonl(INADDR_ANY);

    sock = socket(AF_INET,SOCK_DGRAM,0);
    if(sock == INVALID_SOCKET)
    {
        Application->MessageBoxA("Socket Open failed","Error",MB_OK);
        WSACleanup();
        return;
    }

    result = bind(sock,(LPSOCKADDR)&sockaddr,sizeof sockaddr);
    if(result == SOCKET_ERROR)
    {
        Application->MessageBoxA("Bind Error","Error",MB_OK);
        WSACleanup();
        return;
    }

    /* 自写函数getFileReady打开一个日志文件等待记录数据 */
    if( !getFileReady() 
    {
        WSACleanup();
        return;
    }

    /* 把StringGrid编辑区域清理一下 */
    sgLog -> RowCount    = 2;
    sgLog -> Rows[1] -> Clear();
    sgLog -> Cells[0][1] = "1";
    lineCount            =  1;

    /* 启动线程,接受数据 */
    recvFlag = true;
    tudpr    = new TUDPR(true);
    tudpr->Resume();
}
TUDPR是负责受信的线程,其类定义如下:
class TUDPR : public TThread
{            
private:
protected:
    void __fastcall Execute();
public:
    __fastcall TUDPR(bool CreateSuspended);
};

线程内的完整处理如下:
#include <vcl.h>
#pragma hdrstop
#include <winsock.h>

#include "TUDPR.h"
#include "Monitor.h"

extern int         m_sendRcvFlag;
extern SOCKET      sock;
extern WSADATA     wsaData;
extern SOCKADDR_IN sockaddr;

#pragma package(smart_init)

__fastcall TUDPR::TUDPR(bool CreateSuspended)
    : TThread(CreateSuspended)
{
}

void __fastcall TUDPR::Execute()
{
    int           result;
    unsigned char rbuf[SNDRCVDATALEN];

    /* 受信标志变量为真时接收数据 */
    while(recvFlag)
    {
        result = recvfrom(sock,
                          rbuf,
                          SNDRCVDATALEN,
                          0,
                          NULL,
                          NULL
                          ;
        if( !recvFlag 
        {
            break;
        }
        if(result == SOCKET_ERROR)
        {
            Application->MessageBoxA("Receive Error","Error",MB_OK);
            WSACleanup();
            return;
        }

        rbuf[result]  = 0;
       /* 参考1中的doLog注释 */ 
       Moniter -> doLog(false , rbuf , result);
    }
}


第三种方法在效率上可以满足要求了,但是需要管理线程,实现起来也明显要麻烦许多。

你可能感兴趣的:(C++,c,Stream,socket,null,存储)