The following is sample code showing use of CAsyncSocket to send and receive
UDP packets. I used the ClassWizard to create a class (CUDPSocket) derived from
CAsyncSocket. The derived class does little more than override OnReceive. The
derived class is used in the application's document.
Sample Code
File stdafx.h
#pragma once
#define VC_EXTRALEAN
#include <afxwin.h>
#include <afxsock.h>
#include <afxcmn.h>
File UDPSocket.h
class CSendReceiveDocument;
class CUDPSocket : public CAsyncSocket {
public:
CSendReceiveDocument *m_pDocument; // public for debugging
CUDPSocket() : m_pDocument(NULL) {};
virtual ~CUDPSocket() {};
void SetDocument(CSendReceiveDocument *pDocument) {m_pDocument=pDocument;};
void Close() {CAsyncSocket::Close();};
// ClassWizard generated virtual function overrides
//{{AFX_VIRTUAL(CUDPSocket)
public:
virtual void OnReceive(int nErrorCode);
//}}AFX_VIRTUAL
// Generated message map functions
//{{AFX_MSG(CUDPSocket)
// NOTE - the ClassWizard will add and remove member functions here.
//}}AFX_MSG
};
File UDPSocket.cpp
void CUDPSocket::OnReceive(int nErrorCode) {
CString IPAddress;
char Buffer[512]; // Increase size as needed
CString Message;
DWORD Error;
UINT Port;
int Size;
CAsyncSocket::OnReceive(nErrorCode);
if (nErrorCode) {
Message.Format("OnReceive nErrorCode: %i", nErrorCode);
AfxMessageBox(Message);
return;
}
Size = ReceiveFrom(Buffer, sizeof Buffer, IPAddress, Port);
if (!Size || Size == SOCKET_ERROR) {
Error = GetLastError();
Message.Format("ReceiveFrom error code: %u", Error);
AfxMessageBox(Message);
return;
}
if (m_pDocument)
m_pDocument->OnReceive(Buffer, Size, IPAddress, Port);
else
AfxMessageBox("No document pointer");
}
I am using class CSocketAddressIn to simplify use of addresses. The following
is the definition.
class CSocketAddressIn : sockaddr_in {
public:
CSocketAddressIn()
{ZeroMemory(this, sizeof sockaddr_in);sin_family = AF_INET;};
CSocketAddressIn(u_long Address, u_short Port = 0);
void SetAddress(u_long Address) {sin_addr.s_addr = htonl(Address);};
void GetAddress(CString &s) {s=inet_ntoa(sin_addr);};
void SetPort(u_short Port) {sin_port = htons(Port);};
operator sockaddr *() const {return (struct sockaddr *)this;}
};
The following is the implementation of the extra constructor:
CSocketAddressIn::CSocketAddressIn(u_long Address, u_short Port) {
sin_family = AF_INET;
sin_addr.s_addr = htonl(Address);
sin_port = htons(Port);
ZeroMemory(sin_zero, sizeof sin_zero);
}
The CSendReceiveDocument definition is:
class CSendReceiveDocument : public CDocument {
protected:
DECLARE_DYNCREATE(CSendReceiveDocument)
CUDPSocket m_UDPSocket;
int m_DataSize;
UINT m_rSocketPort;
CString m_rSocketAddress;
static const CString m_NoPath;
CString m_Message, m_ReceivedData;
CTime m_TimeReceived;
// ClassWizard generated virtual function overrides
//{{AFX_VIRTUAL(CSendReceiveDocument)
public:
virtual BOOL OnOpenDocument(LPCTSTR lpszPathName);
virtual void OnCloseDocument();
virtual BOOL OnNewDocument();
virtual void DeleteContents();
virtual void SetPathName(LPCTSTR lpszPathName, BOOL bAddToMRU = TRUE);
protected:
virtual BOOL SaveModified();
//}}AFX_VIRTUAL
public:
CSendReceiveDocument();
virtual ~CSendReceiveDocument() {};
void SendTo(LPCTSTR, DWORD, LPCTSTR);
void OnReceive(void * const, int, const CString, UINT);
#ifdef _DEBUG
virtual void AssertValid() const;
virtual void Dump(CDumpContext& dc) const;
#endif
// Generated message map functions
protected:
//{{AFX_MSG(CSendReceiveDocument)
//}}AFX_MSG
DECLARE_MESSAGE_MAP()
};
The CSendReceiveDocument implementation is:
const CString CSendReceiveDocument::m_NoPath = "None";
CSendReceiveDocument::CSendReceiveDocument() : m_DataSize(0), m_rSocketPort(0) {
CString Path;
m_UDPSocket.SetDocument(this);
CSocketAddressIn SocketAddressIn(INADDR_ANY);
SocketAddressIn.GetAddress(Path);
Path += " : 0";
SetPathName(Path);
}
BOOL CSendReceiveDocument::OnOpenDocument(LPCTSTR lpszPathName) {
CString ErrorMessage, Message, PathName(lpszPathName), SocketAddress, s;
int ColonIndex;
UINT Port(0);
long Events;
OnCloseDocument();
if (PathName == m_NoPath)
return TRUE;
ColonIndex = PathName.Find(':');
if (ColonIndex == -1)
SocketAddress = PathName;
else {
SocketAddress = PathName.Left(ColonIndex);
s = PathName.Mid(ColonIndex+1);
s.TrimLeft();
Port = strtoul(s, NULL, 10);
}
Events = FD_READ | FD_WRITE | FD_CONNECT | FD_CLOSE;
if (!m_UDPSocket.Create(Port, SOCK_DGRAM, Events, SocketAddress)) {
GetErrorMessage(GetLastError(), ErrorMessage);
Message.Format("The socket for %s was not created:\r\n%s",
PathName, ErrorMessage);
AfxMessageBox(Message);
SetPathName(m_NoPath);
m_Message = "Open failed";
UpdateAllViews(NULL, 8, NULL);
return FALSE;
}
SetPathName(PathName);
m_Message = "Opened: ";
m_Message += PathName;
UpdateAllViews(NULL, 1, NULL);
SetModifiedFlag(TRUE);
return TRUE;
}
void CSendReceiveDocument::OnCloseDocument() {
m_UDPSocket.Close();
// CDocument::OnCloseDocument(); // Don't call; will close view and frame
DeleteContents();
SetModifiedFlag(FALSE);
}
BOOL CSendReceiveDocument::OnNewDocument() {
AfxMessageBox("Sorry, \"New Document\" is not available");
return CDocument::OnNewDocument();
}
void CSendReceiveDocument::DeleteContents() {
m_DataSize = 0;
m_Message.Empty();
m_ReceivedData.Empty();
UpdateAllViews(NULL, 9, NULL);
SetTitle(m_NoPath);
CDocument::DeleteContents();
}
void CSendReceiveDocument::SetPathName(LPCTSTR lpszPathName, BOOL bAddToMRU) {
m_strPathName = lpszPathName;
ASSERT(!m_strPathName.IsEmpty()); // must be set to something
m_bEmbedded = FALSE;
SetTitle(lpszPathName);
}
void CSendReceiveDocument::OnReceive(void *const Buffer, int Size,
const CString rSocketAddress, UINT rSocketPort) {
m_rSocketPort = rSocketPort;
m_rSocketAddress = rSocketAddress;
m_DataSize = Size;
m_TimeReceived = CTime::GetCurrentTime();
memcpy(m_ReceivedData.GetBuffer(Size), Buffer, Size);
m_ReceivedData.ReleaseBuffer(Size);
UpdateAllViews(NULL, 2, NULL);
}
void CSendReceiveDocument::SendTo(LPCTSTR Data, DWORD dwAddress, LPCTSTR Port) {
CSocketAddressIn SocketAddress;
CString ErrorMessage, Message;
int rv;
SocketAddress.SetAddress(dwAddress);
SocketAddress.SetPort((u_short)strtoul(Port, NULL, 10));
rv = m_UDPSocket.SendTo(Data, strlen(Data), SocketAddress, sizeof SocketAddress, 0);
if (rv == SOCKET_ERROR) {
GetErrorMessage(GetLastError(), ErrorMessage);
Message.Format("The data was not sent:\r\n%s", ErrorMessage);
AfxMessageBox(Message);
m_Message = "Send failed";
}
else
m_Message = "Data sent";
UpdateAllViews(NULL, 8, NULL);
}
BOOL CSendReceiveDocument::SaveModified() {return TRUE;}
Description
I have not had time to write a description of any of that, but I hope the
sample code helps.
Sample Project
SendReceive.zip is a complete sample. I wrote
it using my Creating an MFC Application
Without the AppWizard, so it does not use document templates, but the sample
code above was copied from the project.
This sample does not do anything useful except be a sample. What it does do is:
-
Create a socket using an address and port we
specify
-
Receive packets from that address (and port)
-
Sends a packet when we provide an address, port
and data
To use the sample, first use the "Open" menu item.
The program will listen to the address and port specified for the socket and
receive packets sent to it. Then to send a packet, specify an address and
port to send it to and type in the data to send. Then send the data using
the "Send" menu item. If the data is sent to the address and port that was
specified in the open dialog, then the data sent should appear in the second
large text box (the one below where it says "No Data Received" as shown to
the right). |
 |