Socket Programming with MFC (Part 1)






4.18/5 (57 votes)
Nov 10, 2005
4 min read

451443

21389
An article on Socket Programming with MFC

Introduction
Socket Programming is a very interesting activity in most of the programming languages. It's a nice activity to write Servers and Clients that communicate over a network. In Windows Platform, socket communications are based on Microsoft Winsock architecture. Windows supports both stream based (TCP) and Datagram based (UDP) socket communication. Windows APIs are available for socket programming, but many find it difficult to work with them.
So here I am going to explain the easiest way to do socket programming using MFC socket classes. This will not only make your job easy, but will also reduce the development time for your network applications. You can develop a custom made socket class that you can reuse in all the network based applications that you write. I will explain these concepts by developing an Echo Server application.
The MFC Socket Classes
For Socket programming, MFC provides two built in classes named CAsyncSocket
and CSocket
. CSocket
is inheriting its functionality from CAsyncSocket
. CAsyncSocket
class provides several notification functions ,that will be called automatically upon occurrence of the socket events. Moreover, it acts as the base class for the complete event driven socket communication.
We can create our own customised socket classes by inheriting from CAsyncSocket
class, which will serve our application specific needs. At first socket environment needs to be initialized by calling the AfxSocketInit()
function.
Initializing Sockets
To initialize sockets, we need to call the function AfxSocketInit()
. It is usually called from the InitInstance()
function of the MFC application. If we are using a wizard to generate the application, checking the option "use Windows Sockets" will automatically do this job for us. It returns a value to indicate success or failure of the call.
BOOL CEchoServerApp::InitInstance()
{....
if( AfxSocketInit() == FALSE)
{
AfxMessageBox("Sockets Could Not Be Initialized");
return FALSE;
}
...
}
Creating Server Sockets
To create a Server socket, we need to declare a variable of type CAsyncSocket
or our own class derived from CAsyncSocket
or CSocket
. Then we must call the create()
function with the port to be listened as argument. It returns a value to indicate success or failure of the call:
UpdateData(TRUE);
m_sListener.Create(m_port);
if(m_sListener.Listen()==FALSE)
{
AfxMessageBox("Unable to Listen on that port, please try another port");
m_sListener.Close();
return;
}
Creating Client Sockets
To create a Client socket, first we need to declare a variable of type CAsyncSocket
or our own class derived from CAsyncSocket
or CSocket
. Then we must call the create()
function without any arguments. It returns a value to indicate success or failure of the call:
m_sConnected.Create();
m_sConnected.Connect("server ip",port);
Listening for Incoming Connections
For making the server socket listen to the Specific port, we must call the function Listen()
. It returns a value to indicate success or failure of the call:
if( m_sListener.Listen()== FALSE)
{
AfxMessageBox("Unable to Listen on that port, please try another port");
m_sListener.Close();
return;
}
Accepting Connections
The incoming connection must be accepted to another socket (not the listening socket). It is done by calling the Accept
function with the second socket as argument.
void CEchoServerDlg::OnAccept()
{
CString strIP;
UINT port;
if(m_sListener.Accept(m_sConnected))
{
m_sConnected.GetSockName(strIP,port);
m_status="Client Connected,IP :"+ strIP;
m_sConnected.Send("Connected To Server",strlen("Connected To Server"));
UpdateData(FALSE);
}
else
{
AfxMessageBox("Cannot Accept Connection");
}
}
Sending Data
The data to be sent is kept in a buffer and a pointer to it and its length is passed to the send
function:
m_sConnected.Send(pBuf,iLen);
Receiving Data
Data is received by calling the function receive(buffer,maxlength)
:
void CEchoServerDlg::OnReceive()
{
char *pBuf =new char [1025];
CString strData;
int iLen;
iLen=m_sConnected.Receive(pBuf,1024);
if(iLen == SOCKET_ERROR)
{
AfxMessageBox("Could not Receive");
}
else
{
pBuf[iLen]=NULL;
strData=pBuf;
m_recieveddata.Insert(m_recieveddata.GetLength(),strData);
//display in server
UpdateData(FALSE);
m_sConnected.Send(pBuf,iLen); //send the data back to the Client
delete pBuf;
}
}
Shutting Down Connection
m_sConnected.ShutDown(0); Stops Sending Of Data
m_sConnected.ShutDown(1); Stops Receiving of data
m_sConnected.ShutDown(2); Stops Both Sending and Receiving of Data
Closing Connection
m_sConnected.Close();
Socket Events
Several functions are used for notification of events. They are to be overridden by descendant classes, in the form of protected
member functions.
OnAccept()
indicates that a connection is pending to be accepted-
OnClose()
indicates that application at the other end has closed the socket -
OnConnect()
indicates that Connection establishment is complete -
OnSend()
indicates that Data is being sent OnReceive()
indicates that Data is being receivedOnOutofBandData()
indicates that out of band data (urgent message) has arrived
Deriving Our Own Socket Class from CAsyncSocket
For deriving our own socket class, go to class view and add a new class and set its base class as CAsyncSocket
. With the help of classwizard, add notification functions as shown below:
class MyEchoSocket : public CAsyncSocket
{ // Attributes
public:
// Operations
public:
MyEchoSocket();
virtual ~MyEchoSocket();
// Overrides
public:
void SetParentDlg(CDialog *pDlg);// ClassWizard generated virtual function overrides
//{{AFX_VIRTUAL(MyEchoSocket)
public:
virtual void OnAccept(int nErrorCode);
virtual void OnClose(int nErrorCode);
virtual void OnConnect(int nErrorCode);
virtual void OnOutOfBandData(int nErrorCode);
virtual void OnReceive(int nErrorCode);
virtual void OnSend(int nErrorCode);
//}}AFX_VIRTUAL // Generated message map functions
//{{AFX_MSG(MyEchoSocket)
// NOTE - the ClassWizard will add and remove member functions here. //}}AFX_MSG
protected:
private:
CDialog * m_pDlg;
};
Setting The "Parent Dialog"
The socket class's SetParentDlg
function is called to ensure that notifications are received to the dialog class when socket events occur:
m_sListener.SetParentDlg(this);
m_sConnected.SetParentDlg(this);
Linking Socket Events With Dialog Class Member Functions
In the dialog class, add the appropriate functions like:
void OnReceive();
void OnClose();
void OnAccept();
void OnConnect();
These functions are called from the socket class as shown below:
void MyEchoSocket::OnAccept(int nErrorCode)
{
// TODO: Add your specialized code here and/or call the base class
if(nErrorCode==0)
{
((CEchoServerDlg*)m_pDlg)->OnAccept();
}
CAsyncSocket::OnAccept(nErrorCode);
}
Using the Code
The attached project is ready to be compiled and executed. Just open with Visual C++ 6.0 and compile. It is an EchoServer that echoes back whatever data is sent to it. You can use a telnet client to connect to it. The sending and receiving functionality can be tested easily from telnet console.
History
- Version 1.0, Part 1 of MFC Socket Programming [ 10/11/2005 ]