CNetworkCommunication - UDP v MFC
Petr Wiedemann, 14. březen 2009
CNetworkCommunication je třída odvozená z CAsyncSocket, napsaná pro příjem a vysílání paketů protokolem UDP. Je závislá na funkcích MFC. Před jejím použitím je třeba zavolat funkci AfxSocketInit(), například při spuštění aplikace ve funkci InitInstance().
K použití jsou funkce:
bool InitializeInterface(UINT port);
int ReceiveData(BYTE *p_buffer, DWORD bufferSize, DWORD *p_readed);
int TransmitData(BYTE *p_buffer, DWORD n_count, CString address, UINT port, DWORD *p_wrote);
V souboru NetworkCommunication.h jsou definovány hodnoty pro maximální čas (ms), který čeká funkce ReceiveData na příchozí data a jejich návratové hodnoty.
#define NW_MAX_RESPONSE_TIME 500
#define NW_ALL_OK 0x00
#define NW_NO_DATA_IN_QUEUE 0x01
#define NW_IO_ERROR 0x03
Pro příjem a posílání dat je nejprve nutné zavolat funkci InitializeInterface, která inicializuje sokety pro příjem a vysílání. Parametr port určuje, ze kterého portu budou čtena data.
bool CNetworkCommunication::InitializeInterface(UINT port)
{
// vytvoreni soketu this (prijem)
if(!Create(port, SOCK_DGRAM, FD_READ))
return false;
BOOL bMultipleApps = TRUE;
SetSockOpt(SO_REUSEADDR, (void*)&bMultipleApps, sizeof(BOOL), SOL_SOCKET);
// nastaveni velikosti prichoziho bufferu
int iBufferSize = 131072;
BOOL bRet = SetSockOpt(SO_RCVBUF, (void*)&iBufferSize, sizeof(int), SOL_SOCKET);
ASSERT(bRet);
// vytvoreni soketu pro vysilani
if(!send.Create(0, SOCK_DGRAM, 0))
{
return false;
}
return true;
}
Příjem nových dat
Pro získání nových dat slouží funkce ReceiveData se 3 parametry: BYTE *p_buffer, DWORD bufferSize, DWORD *p_readed.
- BYTE *p_buffer - buffer, kam se uloží nová data
- DWORD bufferSize - velikost p_buffer
- DWORD *p_readed - počet dat, uložených v p_buffer
Výpis funkce:
int CNetworkCommunication::ReceiveData(BYTE *p_buffer, DWORD bufferSize, DWORD *p_readed)
{
DWORD dwLen;
DWORD dwTick1 = GetTickCount();
l_read_again:
#ifdef LOGGING
FormatLog(fLogStatus, _T("CNetworkCommunication::ReceiveData - l_read_again label"),
LOG_NEWLINE | LOG_LOGTIME);
#endif
if (!IOCtl(FIONREAD, &dwLen))
return NW_IO_ERROR;
// test, jestli buffer obsahuje data
// pokud nejsou data k dispozici, zkousi nacitat max. cas NW_MAX_RESPONSE_TIME
if (dwLen < 1)
{
if ((GetTickCount() - dwTick1) > NW_MAX_RESPONSE_TIME)
return NW_NO_DATA_IN_QUEUE;
else
{
Sleep(1);
goto l_read_again;
}
}
// nacteni dat
int error = ReceiveFrom (p_buffer, bufferSize, senderip, senderport);
#ifdef LOGGING
// logovani nactenych dat
DWORD log_counter;
CString str, str_temp;
if (error > 0)
{
str = _T("CNetworkCommunication::ReceiveData data in ->");
for (log_counter = 0; log_counter < (DWORD)error; log_counter++)
{
str_temp.Format(_T(" 0x%02X"), p_buffer[log_counter]);
str += str_temp;
if (iswprint((wint_t)p_buffer[log_counter]))
{
str_temp.Format(_T(" (%c)"), p_buffer[log_counter]);
str += str_temp;
}
}
FormatLog(fLogStatus, str, LOG_NEWLINE | LOG_LOGTIME);
}
else
{
FormatLog(fLogStatus, _T("CNetworkCommunication::ReceiveData data in -> no data"),
LOG_NEWLINE | LOG_LOGTIME);
}
#endif
if(error == SOCKET_ERROR)
{
return NW_IO_ERROR;
}
else
{
// pocet nactenych dat
*p_readed = error;
return NW_ALL_OK;
}
}
Odesílání dat
Pro odeslání dat je k dispozici funkce TransmitData s 5 parametry: BYTE *p_buffer, DWORD n_count, CString address, UINT port, DWORD *p_wrote.
- BYTE *p_buffer - buffer, kde jsou data k vyslání
- DWORD n_count - počet dat v p_buffer
- CString address - adresa počítače, na který mají být data vyslána
- UINT port - port, na kterém cílový počítač naslouchá
- DWORD *p_wrote - počet skutečně vyslaných dat
Výpis funkce:
int CNetworkCommunication::TransmitData(BYTE *p_buffer, DWORD n_count,
CString address, UINT port, DWORD *p_wrote)
{
// vyslani dat
int error = send.SendTo((BYTE*)p_buffer, n_count, port, address);
if (error == SOCKET_ERROR)
return NW_IO_ERROR;
#ifdef LOGGING
// logovani vyslanych dat
DWORD log_counter;
CString str, str_temp;
str = _T("CNetworkCommunication::TransmitData data out ->");
for (log_counter = 0; log_counter < n_count; log_counter++)
{
str_temp.Format(_T(" 0x%02X"), p_buffer[log_counter]);
str += str_temp;
}
FormatLog(fLogStatus, str.GetBuffer(1024), LOG_NEWLINE | LOG_LOGTIME);
str.ReleaseBuffer();
#endif
// pocet odeslanych dat
*p_wrote = error;
return NW_ALL_OK;
}
Třídu jsem použil v aplikaci, kde se o síťovou komunikaci staralo samostatné vlákno, takže případné zpoždění ve funkci ReceiveData tolik nevadilo. Hlavní vlákno aplikace potom bylo informováno o dostupnosti nových dat pomocí zpráv (SendMessage). Třída samotná asi moc využití nenajde, snad ale poslouží jako příklad pro posílání a příjem dat přes UDP.