Visual C++ Workshop
Session 12
R. Eckert
MFC NETWORK COMMUNICATION; WEB BROWSING
NETWORK COMMUNICATION BETWEEN COMPUTERS WITH TCP/IP
Server App: Waits for other apps on other computers to open a connection
Client App: Attempts to open a connection
When connection is established, data can be exchanged
Sockets:
Basic object used to perform network communication
Used to read/write messages travelling between applications
(Like a file pointer in file I/O)
A communication "endpoint"
Windows support for sockets in the Winsock API
MFC encapsulates this in the CAsyncSocket base class
Provides complete, event-driven socket communications
Making a socket connection to another app:
Specify IP address of computer other app is running on
and the port it's listening on
Like telephone communication:
(Dial number and extension)
CAsyncSocket class support:
Can define our own derived m_sock class
Objects constructed from this class will have socket functionality
Assume objects: m_sock (listening socket) & m_dsock (data socket)
Creating a Socket:
Server: m_sock.Create(listen port #);
Client: m_dsock.Create();
Making a Connection:
Server: (Step 1) m_sock.Listen();
(Step 2) m_sock.Accept(m_dsock);
Client: m_dsock.Connect("IPnameOrAddress", port #)
Server is listening
Connection attempt--> "request connection messsage" event to server
Event triggers call to Server's overridable m_sock.OnAccept()
==>It should call its Accept(m_dsock) method
==>data socket is created automatically & connected to client
Result: "connection accepted msg" event to client (he can now send)
Event triggers call to Client's overridable m_dsock.OnConnect()
Sending/receiving text data:
Sender invokes m_d.Send(lpszDataSring,length)
When data is received, receiver gets "data received msg" event
Event triggers call to receiver's overridable m_dsock.OnReceive() method
==> Receiver should invoke m_dsock.Receive(lpszBuf,length)
Fills the buffer with the received data
Closing a connection:
Either party can invoke m_dsock.Close()
==>other party gets "close connection msg" event
Event triggers call to overridable to m_dsock.OnClose()
==>Should call m_dsock.Close()
Sends a "close connection msg" event to other party...
who should do the same thing
ClientSock and ServeSock Applications <Show programs in action>
MFC AppWizard Dialog-based applications
After network connection is made, either can send or receive strings
Need to define CMySock class derived from CAsyncSocket
m_sock object from this class will be receiving msgs over the network
In response it must call functions defined in its parent dialog box
So need member variable that points to the parent window:
CDialog m_pWnd
Need member function that will provide the parent window pointer:
void CMySock::SetParent(CDialog *pWnd)
{
m_pWnd = pWnd;
}
Must override the following base class virtual member functions
(Want corresponding functions to be called in parent dialog box)
OnConnect() <Client only>
OnAccept() <Server only>
OnReceive()
OnSend()
OnClose()
Preparing the ServeSock Server App (details)
MFC AppWizard Dialog-based application
Be sure to include "Support for Windows Sockets" (Step 2)
Controls:
Static Control: "ListenPort"
Single-line Edit box: IDC_ESERVPORT
Button: "Connect", IDC_BCONNECT
Button: "Send", IDC_BSEND
Button: "Close", IDC_BCLOSE
Static Control: "Message:"
Single-line Edit box: IDC_EMSG
Static Control: "Sent:"
List box: IDC_LSENT
Static Control: "Received:"
List box: IDC_LRECVD
Use ClassWizard to add variables to the controls:
IDC_BCONNECT: m_cConnect, Control, CButton
IDC_BSEND: m_cSend, Control, CButton
IDC_BCLOSE: m_cClose, Control, CButton
IDC_EMSG: m_sMsg, Value, CString
IDC_EMSG: m_cMsg, Control, CEdit
IDC_ESERVPORT: m_iPort, Value, int
IDC_ESERVPORT: m_cPort, Control, CEdit
IDC_LRECVD: m_cRecvd, Control, CListBox
IDC_LSENT: m_cSent, Control, CListBox
Use ClassWizard to add Message Map handler functions for
BN_CLICK messages for each new button:
IDC_BCONNECT --> OnBconnect()
IDC_BSEND --> OnBsend()
IDC_BCLOSE --> OnBclose()
Create the Socket Class: CMySock
Right Click on Parent folder (ServeSock Classes) in Classview
Select: New Class, Name: CMySock, Base Class: CAsyncSocket
Add private member variable to CMySock:
name: m_pWnd
type: CDialog*
Add public member function to CMySock:
void SetParent(CDialog* pWnd)
Edit SetParent() by adding the line:
m_pWnd = pWnd;
Add protected void member functions to override virtual functions in CMySock for:
OnAccept()
OnReceive()
OnClose()
For each, parameter in declaration should be: int nErrorCode
Make sure "virtual" check box is checked
Edit each of these new functions following pattern of OnAccept() given here:
void CMySock::OnAccept(int nErrorCode)
{
if(nErrorCode==0)
//Call parent dialog's OnAccept() function
((CServeSockDlg*)m_pWnd)->OnAccept();
}
Include the header file for the dialog box in new CMySock class:
#include "ServeSockDlg.h" in MySock.cpp
Add two private CMySock variables to CServeSockDlg class:
m_listensock
m_datasock
Add the following public void member functions to CServeSockDlg class:
OnAccept()
OnReceive()
OnClose()
Add initialization code to CServeSockDlg's OnInitDialog() function:
// TODO: Add extra initialization here
m_iPort=3000; //unused port number
UpdateData(FALSE); //send data to controls
m_cSend.EnableWindow(FALSE); //disable Send and Close buttons
m_cClose.EnableWindow(FALSE);
m_cMsg.EnableWindow(FALSE); //disable Edit box
m_datasock.SetParent(this); //the 2 MySocket objects
m_listensock.SetParent(this);
Edit CServeSockDlg's OnBConnect() handler function:
// TODO: Add your control notification handler code here
UpdateData(TRUE); //get string from control
m_listensock.Create(m_iPort); //create socket
m_listensock.Listen(); //listen on socket
m_cPort.EnableWindow(FALSE); //disable Port Edit box
Edit CServeSockDlg's OnAccept():
m_listensock.Accept(m_datasock); //accept connection, create data socket
m_cConnect.EnableWindow(FALSE); //disable Connect button
m_cSend.EnableWindow(TRUE); //enable Send and Close buttons
m_cClose.EnableWindow(TRUE);
m_cMsg.EnableWindow(TRUE); //enable Edit box
Edit CServeSockDlg's OnBSend():
// TODO: Add your control notification handler code here
int iLen;
int iSent;
UpdateData(TRUE); //get string from control
if (m_sMsg!="")
{ //send it out the socket
iLen=m_sMsg.GetLength();
iSent=m_datasock.Send(LPCTSTR(m_sMsg),iLen);
if (iSent==SOCKET_ERROR)
{MessageBox("Server send error");}
else
{ //add string to Sent box
m_cSent.AddString(m_sMsg);
UpdateData(FALSE); //send data to control
}
}
Edit CServeSockDlg's OnReceive():
char *pBuf = new char[1025];
int iBufSize=1024;
int iRecvd;
CString sRecvd;
iRecvd=m_datasock.Receive(pBuf,iBufSize); //get string from socket
if(iRecvd==SOCKET_ERROR)
{}
else
{ //add string to Received box
pBuf[iRecvd]=0; //terminating NULL
sRecvd=pBuf;
m_cRecvd.AddString(sRecvd);
UpdateData(FALSE); //send data to control
}
Edit CServeSockDlg's OnClose():
m_datasock.Close(); //close the data socket
m_cSend.EnableWindow(FALSE); //disable controls
m_cClose.EnableWindow(FALSE);
m_cMsg.EnableWindow(FALSE();
Edit CServeSockDlg's OnBclose():
OnClose(); //call function that closes the data socket
Preparing the ClientSock Client Application
Most of project same as ServerSock
Some Differences:
Dialog box needs "Server Name" Edit Control:
(So user can specify DNS name or IP address & enable/disable it)
Only need one CMySock object: m_datasock
OnInitDialog() should be changed--extra initialization
Need an OnConnect() function instead of OnAccept()
OnBConnect() will be changed
OnClose() will be changed
Running the ServeSock and ClientSock:
Applications will only work if computer(s) have TCP/IP installed and configured correctly
Be sure the Server connects before a client tries to connect
Test first with both applications on the same machine
Servername = "loopback" (default as programmed)
Then try with server on one machine and client on another
Client must enter:
correct "name" or "IP address" of computer server is running on
Port number Server is listening on
One problem: The server application can handle only one connection at a time.
A second application trying to connect to connected server will cause server to crash.
One soution: A third socket object could be used to send a rejection message to other
clients attempting to connect if another client is still connected.
HyperText Markup Language (HTML) and Web Browsing in Windows
MFC Support for Web Browsing
New in VC++ 6
HTML--
Scripting Language to create Web Pages
VC++ Support for HTML--
WebBrowser Control
ActiveX component provides access to Inernet Explorer browsing components
MFC CHtmlView class
Wraps WebBrowser control into package that fits into Doc/View arch.
Provides easy access to Internet Explorers functions
Views derived from CHtmlView are basic Web browser windows
Requires Microsoft Internet Explorer 4.0 (or later) installed
Some CHtmlView member functions:
Navigate ("URL")
GoBack()
GoHome()
GoSearch()
Stop()
Refresh()
LoadFromResource()
CString GetLocationURL()
BOOL GetBusy()
Adding DHTML Documents to Windows Programs:
1. As a resource
Insert / Resource / HTML / New / Enter HTML Script
Load using LoadFromResource()
2. Load from a file
Navigate("file_name")
3. Download from a remote site
Navigate("URL")
HtmlSample Application <Show program in action>
MFC AppWizard, SDI Application
Step 6 (checkered flag):
Select View Class (HtmlSampleView)
Select CHtmlView from Base Class combo box
Click Finish & OK
Add Pop-up Menu Item "Browse" with items:
"URL": IDM_URL (-->Dialog box w/ edit control to enter URL)
"File": IDM_FILE (--> Common file dialog box to get file name)
"Home": IDM_HOME (--> Go to browser's home page)
"Resource": IDM_RES (--> Load html page from pgm's resources)
Insert Simple HTML page resource:
Insert / Resource / HTML / New
Add something like the following text to the editor window that appears:
<html>
<body>
<h3> This page was stored as a resource for this program</h3>
<h2> My favorite Web page is:</h2>
<A HREF="http://www.binghamton.edu">
Binghamton University Home Page</>
</body>
</html>
This will generate resource IDR_HTML1 in a file called html1.htm
Insert a "URL" Dialog Box Resource (IDD_URL_DLG):
Static Control: "Enter URL"
Edit Box: "IDC_EFILE
Use ClassWizard to add a new Dialog box class:
CUrlDlg based on CDialog
Use ClassWizard to add a CString member variable to CUrlDlg:
IDC_EURL: variable name = m_EURL
Use ClassWizard to add menu command message map handler functions in CHtmlSampleView:
IDM_URL: OnUrl()
IDM_FILE: OnFile()
IDM_HOME: OnHome()
IDM_RES: OnRes()
Edit each of the handler functions:
OnUrl():
// TODO: Add your command handler code here
CUrlDlg dlg;
if (dlg.DoModal()==IDOK) //Invoke URL dialog box
Navigate(dlg.m_eURL);
OnFile():
// TODO: Add your command handler code here
CFileDialog fdlg(TRUE);
CString sFileName;
if (fdlg.DoModal()==IDOK) //Invoke common file dlg box
{
sFileName=fdlg.GetPathName(); //Get file name
Navigate(sFileName);
}
OnHome():
// TODO: Add your command handler code here
GoHome();
OnRes():
// TODO: Add your command handler code here
LoadFromResource(IDR_HTML1);
Edit the code in OnInitialUpdate()
(The hardcoded Microsoft VC++ initial URL has moved!)
Change the line:
Navigate2(_T("http://www.microsoft.com/visualc/"),NULL,NULL);
to:
Navigate("http://www.SomeValidURL");
Here SomeValidURL would be some web address you know is OK
Add #include "UrlDlg.h" near top of htmlSampleView.cpp
Build and run the application--
Make sure you're connected to the Internet
Computer must have Microsoft Internet Explorer installed