Winsock 2
The Winsock2 Tutorial brings together the four small applications
that make up the Winsock1 Tutorials. Therefore, each instance of
Winsock2 is both the client and the server. This is a more realistic
way for a WinSock program to be implemented. In addition to the TCP
and UDP send and receive functionality, Winsock2 introduces broadcasting
and out-of-band data. Broadcasting is only allowed on UDP sockets.
Likewise, out-of-band data is only applicable to TCP sockets.
Unlike the Winsock1 Tutorials, the Winsock2 Tutorial uses a Single Document
Interface (SDI) as opposed to a dialog based interface. All of these
files, with the exception of VWSocket.cpp,
VWSocket.h and the Modeless Dialog
files, were generated by App Wizard, using the SDI application template.
VWSocket.cpp and VWSocket.h
were added to implement the form view class that we use for this application.
One line was added to stdafx.h to
enable socket support. See the section below for discussions on the
socket code. A line was also changed in Winsock2.cpp to
make our form view load instead of the default view.
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 VWSocket.cpp.
As always, we need to include the Winsock header files into the project.
This is done in stdafx.h.
#include <afxsock.h> // MFC socket extensions
We also needed to change the document template in Winsock2.cpp
to load our form view instead of the default view.
CSingleDocTemplate* pDocTemplate; pDocTemplate = new CSingleDocTemplate( IDR_MAINFRAME, RUNTIME_CLASS(CWinsock2Doc), RUNTIME_CLASS(CMainFrame),
// main SDI frame window RUNTIME_CLASS(CVWSocket)); AddDocTemplate(pDocTemplate);
CVWSocket is our form view that replaces the default view.
Simply replace it as the last parameter to the CSingleDocTemplate
constructor. Now, let's take a look at VWSocket.cpp.
BEGIN_MESSAGE_MAP(CVWSocket, CFormView) //{{AFX_MSG_MAP(CVWSocket) ON_BN_CLICKED(IDOK, OnQuit) ON_BN_CLICKED(IDC_BN_TCPRECV, OnTCPReceive) ON_BN_CLICKED(IDC_BN_TCPSEND, OnTCPSend) ON_BN_CLICKED(IDC_BN_TCPSENDOOB, OnTCPSendOOB) ON_BN_CLICKED(IDC_BN_UDPBROADCAST, OnUDPBroadcast) ON_BN_CLICKED(IDC_BN_UDPRECV, OnUDPReceive) ON_BN_CLICKED(IDC_BN_UDPSEND, OnUDPSend) //}}AFX_MSG_MAP END_MESSAGE_MAP() This is a standard MFC message map. Note the 6 functions corresponding
to the six buttons on the screen. By now you should be familiar with
the workings of a message map. If you are not, refer the Winsock1
Tutorials.
IMPLEMENT_DYNCREATE(CVWSocket, CFormView) This is an MFC macro used to allow dynamic creation of this class.
It is way outside the scope of this tutorial. This is generated by
Class Wizard and should be ignored.
CVWSocket::~CVWSocket() { RemoveSockets(); } Standard C++ constructor and destructor.
void CVWSocket::DoDataExchange(CDataExchange* pDX) { CFormView::DoDataExchange(pDX); //{{AFX_DATA_MAP(CVWSocket) DDX_Control(pDX, IDC_UDPPORT, m_edUDPListenPort); DDX_Control(pDX, IDC_UDPDESTPORT, m_edUDPDestPort); DDX_Control(pDX, IDC_UDPDESTIP, m_edUDPDestIP); DDX_Control(pDX, IDC_TCPPORT, m_edTCPListenPort); DDX_Control(pDX, IDC_TCPDESTPORT, m_edTCPDestPort); DDX_Control(pDX, IDC_TCPDESTIP, m_edTCPDestIP); DDX_Control(pDX, IDC_LOCALNAME, m_edLocalName); DDX_Control(pDX, IDC_LOCALIP, m_edLocalIP); //}}AFX_DATA_MAP } Standard DoDataExchange(). All this function does
is associate the controls placed on the dialog with member variables in
this class.
OnInitialUpdate() is called exactly once just before the
window is shown to the user. It is called after the constructer and
DoDataExchange(). This is where we do our initialization.
void CVWSocket::OnInitialUpdate() { char buf[255];
Call the base class OnInitialUpdate().
CFormView::OnInitialUpdate();
Initialize the WinSock APIs
if (!InitSockets_1_1()) AfxMessageBox("Winsock
error. Could not initialize.");
Display the local host information.
//-- The the local host info m_edLocalName.SetWindowText(GetHostName(buf,
255)); m_edLocalIP.SetWindowText(IPString(uGetLocalIP(),
buf, 25));
Fill the controls on the screen with their default values.
//-- Set default data m_edUDPListenPort.SetWindowText("3000"); m_edUDPDestPort.SetWindowText("3000"); m_edTCPListenPort.SetWindowText("4000"); m_edTCPDestPort.SetWindowText("4000"); }
void CVWSocket::OnQuit() { GetParentFrame()->SendMessage(WM_CLOSE); } The sole purpose of this function is to close the application when the
user presses the "Quit" button. Note that closing an SDI application
is done differently than closing a dialog based application.
Now all that's left is to process the six functions that the user can launch
by pressing the buttons on the view. This function, OnUDPReceive(),
is identical to the one found in Winsock1
- UDP Server. Refer to this tutorial for details.
void CVWSocket::OnUDPReceive() { SOCKADDR_IN addr_Srv, addr_Cli; SOCKET sock_Receive; char
szMessage[256], buf[255]; int
listenport; int
clilen; int
val = 1; CString csTemp;
//-- Get the port to listen on m_edUDPListenPort.GetWindowText(csTemp); listenport = atoi(csTemp.GetBuffer(0)); csTemp.ReleaseBuffer(-1); if (listenport < 1) { AfxMessageBox("Please
enter a valid UDP listen port"); return; }
//-- Open a UDP socket if ((sock_Receive = socket(AF_INET, SOCK_DGRAM,
0)) < 0) AfxMessageBox("Unable
to create socket");
//-- 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);
//-- Bind it if (bind(sock_Receive, (sockaddr*)&addr_Srv,
sizeof(addr_Srv)) < 0) AfxMessageBox("Error:
bind() failed.");
//-- Prepare Client address to receive new data memset(&addr_Cli, 0, sizeof(addr_Cli)); clilen = sizeof(addr_Cli);
//-- Put up a message to let them know this thread will be blocking CDGModelessMessage dg("Waiting for UDP message...",
"Winsock2"); dg.Show();
//-- Receive a message if (recvfrom(sock_Receive, szMessage, 256, 0,
(sockaddr*)&addr_Cli, &clilen) == SOCKET_ERROR) { AfxMessageBox("Error:
recvfrom() failed."); } else { //-- 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); }
dg.Kill(); closesocket(sock_Receive); }
This function, OnUDPSend(), is identical to the one found
in Winsock1 - UDP Client.
Refer to this tutorial for details.
void CVWSocket::OnUDPSend() { SOCKADDR_IN addr_Dest; SOCKET sock_Send; char
szMessage[256], szDestIP[20];; int
destport; CString csTemp;
//-- Prepare the message sprintf(szMessage, "This is a UDP message."); //-- Get the IP addres to send to m_edUDPDestIP.GetWindowText(csTemp); if (csTemp.IsEmpty()) { AfxMessageBox("Please
enter a UDP destination IP address"); return; } strcpy(szDestIP, csTemp.GetBuffer(0)); csTemp.ReleaseBuffer(-1); //-- Get the port to send to m_edUDPDestPort.GetWindowText(csTemp); destport = atoi(csTemp.GetBuffer(0)); csTemp.ReleaseBuffer(-1); if (destport < 1) { AfxMessageBox("Please
enter a valid UDP destination port"); return; }
//-- Fill in target addr (for a broadcast message) 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);
//-- Send it if (sendto(sock_Send, szMessage, 256, 0, (sockaddr*)&addr_Dest,
sizeof(addr_Dest)) == SOCKET_ERROR) AfxMessageBox("Error:
sendto() failed.");
closesocket(sock_Send); }
This function, OnUDPBroadcast(), is very similar to the
one found in Winsock1 -
UDP Client. The only difference is that we specify a broadcast
address instead of a specific IP address. We also need to enable
a broadcast flag for the socket.
void CVWSocket::OnUDPBroadcast() { SOCKADDR_IN addr_Broadcast; SOCKET sock_Send; char
szMessage[256]; int
val = 1; short
destport; CString csTemp;
//-- Prepare the message sprintf(szMessage, "This is a UDP broadcast
message."); //-- Get the port to broadcast to m_edUDPDestPort.GetWindowText(csTemp); destport = atoi(csTemp.GetBuffer(0)); csTemp.ReleaseBuffer(-1); if (destport < 1) { AfxMessageBox("Please
enter a valid UDP destination port"); return; }
Notice that we use INADDR_BROADCAST instead of a normal
IP address. This requires that we set the SO_BROADCAST
socket option to allow the socket to send a broadcast message.
//-- Fill in target addr (for a broadcast message) memset((char *) &addr_Broadcast, 0, sizeof(addr_Broadcast)); addr_Broadcast.sin_family
= AF_INET; addr_Broadcast.sin_addr.s_addr = htonl(INADDR_BROADCAST); addr_Broadcast.sin_port
= htons(destport);
We need to use the setsockopt() function to allow this
socket to send broadcast packets. Refer to Quinn & Shute pp.
550-551.
//-- Allow socket to broadcast if (setsockopt(sock_Send, SOL_SOCKET, SO_BROADCAST,
(char*)&val, sizeof(int)) == SOCKET_ERROR) AfxMessageBox("Error:
setsockopt() failed.");
The just send it as we normally would.
//-- Send it if (sendto(sock_Send, szMessage, 256, 0, (sockaddr*)&addr_Broadcast,
sizeof(addr_Broadcast)) == SOCKET_ERROR) AfxMessageBox("Error:
sendto() failed."); closesocket(sock_Send); }
This function, OnTCPReceive(), is identical to the one
found in Winsock1 - TCP
Server. Refer to this tutorial for details.
void CVWSocket::OnTCPReceive() { SOCKADDR_IN addr_Srv, addr_Cli; SOCKET sock_Listen,
sock_Accept; char
szMessage[256], buf[255]; int
clilen; short
listenport; CString csTemp;
//-- Get the port to listen on m_edTCPListenPort.GetWindowText(csTemp); listenport = atoi(csTemp.GetBuffer(0)); csTemp.ReleaseBuffer(-1); if (listenport < 1) { AfxMessageBox("Please
enter a valid TCP listen port"); return; }
//-- Open a TCP socket to listen on if ((sock_Listen = socket(AF_INET, SOCK_STREAM,
0)) < 0) AfxMessageBox("Unable
to create socket");
//-- 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);
//-- Bind it if (bind(sock_Listen, (sockaddr*)&addr_Srv,
sizeof(addr_Srv)) < 0) AfxMessageBox("Error:
bind() failed.");
//-- Put the socket in listen mode if (listen(sock_Listen, SOMAXCONN) < 0) AfxMessageBox("Error:
listen() failed.");
//-- Prepare Client address to receive new data memset(&addr_Cli, 0, sizeof(addr_Cli)); clilen = sizeof(addr_Cli);
//-- Put up a message to let them know this thread will be blocking CDGModelessMessage dg("Waiting for TCP message...",
"Winsock2"); dg.Show();
//-- Wait for an incoming message (Note: This blocks!) if ((sock_Accept = accept(sock_Listen, (sockaddr*)&addr_Cli,
&clilen)) < 0) AfxMessageBox("Error:
accept() failed.");
if (recv(sock_Accept, szMessage, 256, 0) == SOCKET_ERROR) { int res = WSAGetLastError(); AfxMessageBox("Error:
recv() failed."); } else { //-- 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); }
This function, OnTCPSend(), is identical to the one found
in Winsock1 - TCP Client.
Refer to this tutorial for details.
void CVWSocket::OnTCPSend() { SOCKADDR_IN addr_Dest; SOCKET sock_Send; char
szMessage[256], szDestIP[20];; short
destport; CString csTemp;
//-- Prepare the message sprintf(szMessage, "This is a TCP message."); //-- Get the IP addres to send to m_edTCPDestIP.GetWindowText(csTemp); if (csTemp.IsEmpty()) { AfxMessageBox("Please
enter a TCP destination IP address"); return; } strcpy(szDestIP, csTemp.GetBuffer(0)); csTemp.ReleaseBuffer(-1); //-- Get the port to send to m_edTCPDestPort.GetWindowText(csTemp); destport = atoi(csTemp.GetBuffer(0)); csTemp.ReleaseBuffer(-1); if (destport < 1) { AfxMessageBox("Please
enter a valid TCP destination port"); return; }
//-- 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);
//-- Connect to destination if (connect(sock_Send, (sockaddr*) &addr_Dest,
sizeof(addr_Dest)) < 0) AfxMessageBox("Error:
connect() failed.");
//-- Send it if (send(sock_Send, szMessage, 256, 0) == SOCKET_ERROR) AfxMessageBox("Error:
send() failed.");
closesocket(sock_Send); }
This function, OnUDPSendOOB(), is very similar to the one
found in Winsock1 - TCP
Client. The only difference is that we specify the MSG_OOB
flag in the send() function.
void CVWSocket::OnTCPSendOOB() { SOCKADDR_IN addr_Dest; SOCKET sock_Send; char
szMessage[256], szDestIP[20];; short
destport; CString csTemp;
//-- Prepare the message sprintf(szMessage, "This is a TCP OOB message."); //-- Get the IP addres to send to m_edTCPDestIP.GetWindowText(csTemp); if (csTemp.IsEmpty()) { AfxMessageBox("Please
enter a TCP destination IP address"); return; } strcpy(szDestIP, csTemp.GetBuffer(0)); csTemp.ReleaseBuffer(-1); //-- Get the port to send to m_edTCPDestPort.GetWindowText(csTemp); destport = atoi(csTemp.GetBuffer(0)); csTemp.ReleaseBuffer(-1); if (destport < 1) { AfxMessageBox("Please
enter a valid TCP destination port"); return; }
//-- 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);
//-- Connect to destination if (connect(sock_Send, (sockaddr*) &addr_Dest,
sizeof(addr_Dest)) < 0) AfxMessageBox("Error:
connect() failed.");
All we have to do to send this as Out Of Band (OOB) data is specify the
MSG_OOB flag in the send() function.
For a discussion on OOB relevance to the WinSock APIs, refer to Quinn & Shute
p. 393.
//-- Send it if (send(sock_Send, szMessage, 256, MSG_OOB)
== SOCKET_ERROR) AfxMessageBox("Error:
send() failed.");