Winsock 1 - UDP Server
The UDP Server tutorial is the counterpart to the
UDP Client tutorial. These two programs demonstrate how to set up
a Client application and communicate with a Server via UDP. 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 Server. Subsequent tutorials will assume an understanding
of these concepts.
All of these files, with the exception of Modeless Dialog files, were generated
by App Wizard, using a the dialog-based application template. Only
the files UDPServerDlg.cpp
and UDPServerDlg.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.
For details on the Modeless Dialog files, see the discussion on the Modeless
Message Box.
Discussion
To understand how this application works, all we need to do is take a detailed
look at the file UDPServerDlg.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 UDPServerDlg.cpp.
BEGIN_MESSAGE_MAP(CUDPServerDlg, CDialog) //{{AFX_MSG_MAP(CUDPServerDlg) ON_WM_SYSCOMMAND() ON_WM_PAINT() ON_WM_QUERYDRAGICON() ON_BN_CLICKED(IDC_BN_QUIT, OnBnQuit) ON_BN_CLICKED(IDC_BN_LISTEN, OnBnListen) //}}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_LISTEN, OnBnListen) This line tells us that the function OnBnListen()
will be called when the user hits the "Listen" button.
As it turns out, all the sockets processing happens in this function.
CUDPServerDlg::CUDPServerDlg(CWnd* pParent /*=NULL*/) : CDialog(CUDPServerDlg::IDD, pParent) { ... } This is the a standard C++ constructer, no changes are needed.
CUDPServerDlg::~CUDPServerDlg() { 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 CUDPServerDlg::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 CUDPServerDlg::OnInitDialog() { char buf[255]; ... //-- Initialization InitSockets_1_1(); m_edLocalName.SetWindowText(GetHostName(buf,
255)); m_edLocalIP.SetWindowText(IPString(uGetLocalIP(),
buf, 255)); m_edPort.SetWindowText("4000"); return TRUE; } 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 CUDPServerDlg::OnQueryDragIcon() { ... } These function are more standard MFC fare. They were created by App
Wizard.
void CUDPServerDlg::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 CTCPServerDlg::OnBnListen() { We need to declare some variables. We need a client address to fill
in, addr_Cli. Also, because this is a server application,
we need to bind the socket to the local host. For this we need an
additional address structure addr_Srv. We need a
socket, sock_Listen, which is used as the socket that will
be listening for the client's message. We also need two string buffers
for the message we are receiving and a worksapce for formatting the message,
szMessage[256] and buf[255]. We need
a port number to listen on, listenport. We need a
temporary string to hold some values for us, csTemp.
And finally, we need an integer to hold the length of the addr_Cli
structure, clilen.
SOCKADDR_IN addr_Srv, addr_Cli; SOCKET sock_Listen; char
szMessage[256], buf[255]; int
clilen; short
listenport; CString csTemp;
Get the port number to listen on and put it in listenport.
//-- Get the port to listen on m_edPort.GetWindowText(csTemp); listenport = atoi(csTemp.GetBuffer(0)); csTemp.ReleaseBuffer(-1); if (listenport < 1) { AfxMessageBox("Please
enter a valid listen port"); return; }
Now, create a UDP 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.
//-- Open a UDP socket to listen on if ((sock_Listen = socket(AF_INET, SOCK_DGRAM,
0)) < 0) AfxMessageBox("Unable
to create socket");
Now fill in the local host address structure. For this purpose, we
don't need to know the local host's actual IP address. We can simply
pass the parameter INADDR_ANY and the system will automatically
assign an IP address. Of course, we first need to convert INADDR_ANY
to network-byte order with htonl().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 structure fields for binding to local host memset((char *) &addr_Srv, 0, sizeof(addr_Srv)); addr_Srv.sin_family
= AF_INET; addr_Srv.sin_addr.s_addr = htonl(INADDR_ANY); addr_Srv.sin_port
= htons(listenport);
Now bind the listening socket to the local host. Refer to Quinn &
Shute pp. 57 - 58.
//-- Bind it if (bind(sock_Listen, (sockaddr*)&addr_Srv,
sizeof(addr_Srv)) < 0) AfxMessageBox("Error:
bind() failed.");
Standard maintenance to prepare the client address structure to receive
new data.
//-- Prepare Client address to receive new data memset(&addr_Cli, 0, sizeof(addr_Cli)); clilen = sizeof(addr_Cli);
Let the user know that this thread is about to begin a blocking operation.
//-- Put up a message to let them know this thread will be blocking CDGModelessMessage dg("Waiting for UDP message
from client...", "Winsock1 - UDP Server"); dg.Show();
Now receive the message from the client. Refer to Quinn & Shute
pp. 68 - 69.
//-- Wait for an incoming message (Note: This blocks!) if (recvfrom(sock_Listen, szMessage, 256, 0,
(sockaddr*)&addr_Cli, &clilen) == SOCKET_ERROR) AfxMessageBox("Error:
accept() failed."); else { If the program got to this point we have received a valid message from
the client. It is convenient to pull out the IP address of the client
and show it to the user along with the message.
//-- We recieved a message. Show it, along
with the source IP address csTemp = "From:
"; csTemp += IPString(uGetIPFromSockAddr_In(&addr_Cli),
buf, 255); csTemp += "\n"; csTemp += szMessage; AfxMessageBox(csTemp); }
Now kill the modeless message dialog because our blocking operation is
complete.
dg.Kill(); Clean up the socket that we have created before we exit.
closesocket(sock_Listen); }