主要讨论一下数据的接受: 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); } }
第三种方法在效率上可以满足要求了,但是需要管理线程,实现起来也明显要麻烦许多。

|