Winsock 1 - UDP Client
The UDP Client tutorial demonstrates how to set
up a Client application and communicate with a Server via UDP. This
application needs to be run in conjunction with UDP Server. Together,
these two apps create a simple connection and can facilitate one-way communication
between two hosts. This tutorial explains the fundamental mechanics
involved with a UDP Client. Subsequent tutorials will assume an understanding
of these concepts.
All of these files were generated by App Wizard, using a the dialog-based
application template. Only the files UDPClientDlg.cpp
and UDPClientDlg.h were added
to for the tutorial code. One line was added to stdafx.h
to enable socket support. With the exception of the resource files,
no other files were changed. See the section below for discussions
on the socket code.
Discussion
To understand how this application works, all we need to do is take a detailed
look at the file UDPClientDlg.cpp.
This section will also briefly introduce some MFC programming concepts,
in case you are unfamiliar with them. But before we can do anything
with sockets, we need to include the Winsock header files into the project.
This is done in stdafx.h.
#include <afxsock.h> // MFC socket extensions
This is the last line stdafx.h.
It was added manually to allow us to use the Winsock data structures and
APIs in our code. Including this line in stdafx.h
lets us use Winsock API's anywhere in our project. Now, let's take
a look at UDPClientDlg.cpp.
BEGIN_MESSAGE_MAP(CUDPClientDlg, CDialog) //{{AFX_MSG_MAP(CUDPClientDlg) ON_WM_SYSCOMMAND() ON_WM_PAINT() ON_WM_QUERYDRAGICON() ON_BN_CLICKED(IDC_BN_QUIT, OnBnQuit) ON_BN_CLICKED(IDC_BN_SEND, OnBnSend) //}}AFX_MSG_MAP END_MESSAGE_MAP() This is the Message Map. This is simply used to associated functions
with user-interface events, or "messages." The line that
we care about in the Message Map is:
ON_BN_CLICKED(IDC_BN_SEND, OnBnSend) This line tells us that the function OnBnSend() will
be called when the user hits the "Send" button. As it turns
out, all the sockets processing happens in this function.
CUDPClientDlg::CUDPClientDlg(CWnd* pParent /*=NULL*/) : CDialog(CUDPClientDlg::IDD, pParent) { ... } This is the a standard C++ constructer, no changes are needed.
CUDPClientDlg::~CUDPClientDlg() { RemoveSockets(); } This is a standard C++ destructer. I felt that it was a good place
to call our sockets cleanup routine RemoveSockets().
This will be explained more below when socket initialization is discussed.
void CUDPClientDlg::DoDataExchange(CDataExchange* pDX) { ... } This function, DoDataExchange(), is where MFC associates
C++ class members with their corresponding user interface control.
All of this generated by Class Wizard, so no changes were necessary.
BOOL CUDPClientDlg::OnInitDialog() { char buf[255]; ... //-- Initialization InitSockets_1_1(); m_edLocalName.SetWindowText(GetHostName(buf,
255)); m_edLocalIP.SetWindowText(IPString(uGetLocalIP(),
buf, 255)); m_edMessage.SetWindowText("This is a UDP
Message."); m_edServerPort.SetWindowText("4000"); return TRUE; // return TRUE unless
you set the focus to a control } This function, OnInitDialog(), is called exactly one time,
precisely when the dialog starts up. It is a handy place to do all
of our initialization. We can not do this initialization in the constructer
because the constructor is called before some of our members are created
and attached to their corresponding user-interface controls. Most
of the code in this function is standard MFC fare. The lines that we are
interested in for this tutorial are at the end.
The first call we make is InitSockets_1_1(). This
function determines if the host is capable of supporting Winsock 1.1 functionality.
If it is, this function enables the host to use the Winsock APIs.
Otherwise, the function fails. If this function succeeds, you must
call RemoveSockets() when you are done with the Winsock
APIs. This function is one of our general
winsock utility functions. Refer to Quinn & Shute pp. 320
- 322.
After InitSockets_1_1(), we simply set up a few of the
user-interface controls with some desired values. The functions GetHostName(),
IPString() and uGetLocalIP() are all general
winsock utility functions.
HCURSOR CUDPClientDlg::OnQueryDragIcon() { ... } These function are more standard MFC fare. They were created by App
Wizard.
void CUDPClientDlg::OnBnQuit() { EndDialog(0); } This function was added manually. It's sole purpose is to end the
application when the user hits the "Quit" button.
Finally, this function is where the core functionality lies.
void CUDPClientDlg::OnBnSend() { We need to declare some variables. We need a destination address
to fill in, addr_Dest. We need a socket, sock_Send.
We need two string buffers for the message we are sending and the destination
IP address, szMessage[256] and szDestIP[20].
We need a port number to send on, destport. And finally
we need a temporary string to hold some values for us, csTemp.
SOCKADDR_IN addr_Dest; SOCKET
sock_Send; char
szMessage[256], szDestIP[20]; short
destport; CString
csTemp;
First, get the message to send from the edit box. Then copy it into
szMessage[256].
//-- Prepare the message m_edMessage.GetWindowText(csTemp); if (csTemp.IsEmpty()) { AfxMessageBox("Please
enter a message."); return; } sprintf(szMessage, csTemp.GetBuffer(0)); csTemp.ReleaseBuffer(-1);
Now get the destination IP address and copy it into szDestIP[20].
//-- Get the IP addres to send to m_edServerName.GetWindowText(csTemp); if (csTemp.IsEmpty()) { AfxMessageBox("Please
enter a valid host name or IP address"); return; } strcpy(szDestIP, csTemp.GetBuffer(0)); csTemp.ReleaseBuffer(-1);
Likewise, get the port number to send on and put it in destport.
//-- Get the port to send to m_edServerPort.GetWindowText(csTemp); destport = atoi(csTemp.GetBuffer(0)); csTemp.ReleaseBuffer(-1); if (destport < 1) { AfxMessageBox("Please
enter a valid UDP destination port"); return; }
Now, create a TCP socket with the socket() function.
In winsock using TCP/IP, the first and last parameters will always be AF_INET
and 0. The middle parameter determines whether a
TCP or UDP socket gets created. Using SOCK_DGRAM
will create a UDP socket. Refer to Quinn & Shute pp. 50 - 53.
//-- Create TCP socket if ((sock_Send = socket(AF_INET, SOCK_DGRAM,
0)) < 0) AfxMessageBox("Error:
socket() failed.");
Now fill in the message destination address structure. The function
inet_addr() converts a text string with a dotted decimal
IP address into the 4 byte form that the network layer understands.
The function htons() makes sure that the port number is
stored in the proper network byte order. Refer to Quinn & Shute
pp. 54 - 55.
//-- Fill in target addr memset((char *) &addr_Dest, 0, sizeof(addr_Dest)); addr_Dest.sin_family
= AF_INET; addr_Dest.sin_addr.s_addr = inet_addr(szDestIP); addr_Dest.sin_port
= htons(destport);
We don't need to connect to the Server for a UDP message. Just go
ahead and try to send it to the Server's address. Refer to Quinn
& Shute pp. 64 - 66.
//-- Send it if (sendto(sock_Send, szMessage, 256, 0, (sockaddr*)&addr_Dest,
sizeof(addr_Dest)) == SOCKET_ERROR) AfxMessageBox("Error:
sendto() failed.");
Now we are done with this socket, so we need to close it. Refer to
Quinn & Shute pp. 70 - 71.
closesocket(sock_Send); }