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. 


 


Downloads 
Program Only  Winsock2.exe.  No source files. 
Program and Source Files  Winsock2 source files, project files and executable. 
Note:  You must unzip these files with subdirectories!  (example:  "pkunzip -d ws1_full.zip") 


Project Files 


res\Toolbar.bmp  
res\Winsock2.ico  
res\Winsock2.rc2  
res\Winsock2Doc.ico  
DGModeless.cpp  
DGModeless.h  
MainFrm.cpp  
MainFrm.h  
resource.h  
stdafx.cpp  
stdafx.h  
VWSocket.cpp  
VWSocket.h  
Winsock2.aps  
Winsock2.clw  
Winsock2.cpp  
Winsock2.h  
Winsock2.mak  
Winsock2.mdp  
Winsock2.ncb  
Winsock2.rc  
Winsock2Doc.cpp  
Winsock2Doc.h  

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() 
 : CFormView(CVWSocket::IDD) 
{ 
} 

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; 
    } 

//-- Create UDP socket 
    if ((sock_Send = socket(AF_INET, SOCK_DGRAM, 0)) < 0) 
        AfxMessageBox("Error:  socket() failed."); 

//-- 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; 
    } 

//-- Create UDP socket 
    if ((sock_Send = socket(AF_INET, SOCK_DGRAM, 0)) < 0) 
        AfxMessageBox("Error:  socket() failed."); 

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); 
    } 

    dg.Kill(); 
    closesocket(sock_Accept); 
    closesocket(sock_Listen); 
} 

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; 
    } 

//-- Create TCP socket 
    if ((sock_Send = socket(AF_INET, SOCK_STREAM, 0)) < 0) 
        AfxMessageBox("Error:  socket() failed."); 

//-- 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; 
    } 

//-- Create TCP socket 
    if ((sock_Send = socket(AF_INET, SOCK_STREAM, 0)) < 0) 
        AfxMessageBox("Error:  socket() failed."); 

//-- 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."); 

    closesocket(sock_Send); 
} 


Back to Winsock Tutorials Main Page