Winsock 1 - TCP Client
The TCP Client tutorial demonstrates how to set
up a Client application and communicate with a Server via TCP. This
application needs to be run in conjunction with TCP 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 TCP 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 TCPClientDlg.cpp
and TCPClientDlg.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 TCPClientDlg.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 TCPClientDlg.cpp.
BEGIN_MESSAGE_MAP(CTCPClientDlg, CDialog) //{{AFX_MSG_MAP(CTCPClientDlg) 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.
CTCPClientDlg::CTCPClientDlg(CWnd* pParent /*=NULL*/) : CDialog(CTCPClientDlg::IDD, pParent) { ... } This is the a standard C++ constructer, no changes are needed.
CTCPClientDlg::~CTCPClientDlg() { 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 CTCPClientDlg::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 CTCPClientDlg::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 TCP
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 CTCPClientDlg::OnQueryDragIcon() { ... } These function are more standard MFC fare. They were created by App
Wizard.
void CTCPClientDlg::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 CTCPClientDlg::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 TCP 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_STREAM
will create a TCP socket. Refer to Quinn & Shute pp. 50 - 53.
//-- Create TCP socket if ((sock_Send = socket(AF_INET, SOCK_STREAM,
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);
Now use the destination address structure to connect to the server.
The server must be running on the destination machine and listening on
the same port before this function is called! Refer to Quinn &
Shute pp. 61 - 62.
//-- Connect to destination if (connect(sock_Send, (sockaddr*) &addr_Dest,
sizeof(addr_Dest)) < 0) AfxMessageBox("Error:
connect() failed.");
Once the connection is established we can go ahead and send the message.
Refer to Quinn & Shute pp. 64 - 66.
//-- Send it if (send(sock_Send, szMessage, 256, 0) == SOCKET_ERROR) AfxMessageBox("Error:
send() failed.");
Now we are done with this socket, so we need to close it. Refer to
Quinn & Shute pp. 70 - 71.
closesocket(sock_Send); }