您的位置:首页 > 编程语言 > C语言/C++

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

2011-11-15 21:07 417 查看
转至:
http://write.blog.csdn.net/postedit/6974585

主要讨论一下数据的接受:

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);

}

}

第三种方法在效率上可以满足要求了,但是需要管理线程,实现起来也明显要麻烦许多。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: