C++ Builder下三種UDP通訊實現方法的比較 選擇自 findheart 的 Blog
關鍵字 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);
}
}
第三種方法在效率上可以滿足要求了,但是需要管理線程,實現起來也明顯要麻煩許多。
作者Blog:http://blog.csdn.net/findheart/ ;