Introduction
The following code is intended to provide a very simple example of how to use Microsoft MFC’s CAsyncSocket
class to send and receive UDP datagrams.
I was unable to find a simple example of how to use CAsynSocket
to send & receive UDP datagrams, so I created my own. I thought others might find it useful. The example shown creates two independent send sockets & a single receiver socket, that will receive data from both senders.
Using the Code
To reduce clutter, I have:
- included only code relevant to the UDP communications (all socket class code is included, much application code is not).
- created separate classes to deal with sending UDP & receiving UDP
The code snippets of my test applications main dialog, UdpTestAppDlg
(.cpp & .h), show how to use the simple UdpReceiveSocket
& UdpSendSocket
classes that I have provided here. The example code shown creates two independent send sockets & a single receiver socket. The receive socket will receive datagrams from both senders.
The rudimentary error handling that is included is sufficient to highlight the commonest socket problems.
#pragma once
#include <afxtempl.h>
class UdpReceiveSocket : public CAsyncSocket
{
void OnReceive(int nErrorCode);
public:
UdpReceiveSocket();
virtual ~UdpReceiveSocket();
};
#include "stdafx.h"
#include "UdpTestApp.h"
#include "UdpReceiveSocket.h"
UdpReceiveSocket::UdpReceiveSocket()
{
BOOL bRet = Create(9122,SOCK_DGRAM,FD_READ);
if (bRet != TRUE)
{
UINT uErr = GetLastError();
TCHAR szError[256];
wsprintf(szError, "Server Receive Socket Create() failed: %d", uErr);
AfxMessageBox(szError);
}
}
UdpReceiveSocket::~UdpReceiveSocket()
{
}
void UdpReceiveSocket::OnReceive(int nErrorCode)
{
static int i=0;
i++;
TCHAR buff[4096];
int nRead;
CString strSendersIp;
UINT uSendersPort;
nRead = ReceiveFromEx(buff, 4096, strSendersIp, uSendersPort);
switch (nRead)
{
case 0: Close();
break;
case SOCKET_ERROR:
if (GetLastError() != WSAEWOULDBLOCK)
{
AfxMessageBox ("Error occurred");
Close();
}
break;
default: buff[nRead] = 0; CString strReceivedData(buff); }
CAsyncSocket::OnReceive(nErrorCode);
}
#pragma once
class UdpSendSocket : public CAsyncSocket
{
bool m_bReadyToSend;
public:
UdpSendSocket();
virtual ~UdpSendSocket();
virtual bool Send(const void* lpBuf, int nBufLen );
virtual void OnSend(int nErrorCode);
};
#include "stdafx.h"
#include "UdpTestApp.h"
#include "UdpSocket.h"
UdpSendSocket::UdpSendSocket():m_bReadyToSend(false)
{
BOOL bRet = Create(0,SOCK_DGRAM,FD_WRITE);
if (bRet != TRUE)
{
UINT uErr = GetLastError();
TCHAR szError[256];
wsprintf(szError, "Send Socket Create() failed: %d", uErr);
AfxMessageBox(szError);
}
}
UdpSendSocket::~UdpSendSocket()
{
}
void UdpSendSocket::OnSend(int nErrorCode)
{
m_bReadyToSend = true; CAsyncSocket::OnSend(nErrorCode);
}
bool UdpSendSocket::Send(const void* lpBuf, int nBufLen)
{
if ( ! m_bReadyToSend )
return(false);
m_bReadyToSend = false;
int dwBytes;
CAsyncSocket *paSocket = this;
if ((dwBytes = CAsyncSocket::SendToEx
((LPCTSTR)lpBuf,nBufLen,9122,"172.XX.XX.XXX")) == SOCKET_ERROR)
{
UINT uErr = GetLastError();
if (uErr != WSAEWOULDBLOCK)
{
TCHAR szError[256];
wsprintf(szError, "Server Socket failed to send: %d", uErr);
AfxMessageBox(szError);
}
return(false);
}
return(true);
…
void CUdpTestAppDlg::SendUdpDatagram()
{
CString strTestMessage = "Hello World";
m_ussSendSocket1.Send(strTestMessage.GetBuffer(),strTestMessage.GetLength()+1);
m_ussSendSocket2.Send(strTestMessage.GetBuffer(),strTestMessage.GetLength()+1);
}
#pragma once
#include "UdpReceiveSocket.h"
#include "UdpSendSocket.h"
class CUdpTestAppDlg : public CDialog
{
…
protected:
HICON m_hIcon;
UdpReceiveSocket m_ursReceiveSocket; UdpSendSocket m_ussSendSocket1; UdpSendSocket m_ussSendSocket2; void SendUdpDatagram();
…
};
Points of Interest
It turns out that using CAsyncSocket
to send & receive UDP messages is quite straightforward, once you know how. The Microsoft documentation covers both TCP & UDP, which tends to obscure how simply this can be achieved. The other problem, lack of a simple example, has hopefully been addressed here.
The example above sends & receives strings. It is a good idea to keep UDP messages small for a number of reason, however it is a simple matter to send a buffers of bytes (e.g. based on a byte-aligned structure). ALthough non-essential in this example, it is also good practice to convert Microsoft Windows Intel alignment to network byte order before sending & to convert back when processing received data, using functions like ntohl()
.