Chapter 14 ■ Sample Applications 273P2/Vol.6 Programming WinSock #30594-1 tullis 11.14.94 CH14 LP #3 LPVOID pDataWritten; // pointer to data that is completely written LPVOID pDataRead
Trang 1Part IV ■ Programming with the WinSock Class Library
// get pointer to list box used for status messages
CListBox *plb = (CListBox *)GetDlgItem(IDC_LIST_STATUS);
// initialize the WinSock object
m_pWinSock = new CWinSock;
// initialize the stream socket object
m_pStreamSrv = new CStreamSocket(this, WM_USER_STREAMSRV);
/////////////////////////////////////////////////////////////////////////////// CMainView::OnStreamSrv()
// get pointer to list box used for status messages
CListBox *plb = (CListBox *)GetDlgItem(IDC_LIST_STATUS);
switch (wParam)
{
Listing 14.6 continued
Trang 2Chapter 14 ■ Sample Applications 273
P2/Vol.6 Programming WinSock #30594-1 tullis 11.14.94 CH14 LP #3
LPVOID pDataWritten; // pointer to data that is completely written
LPVOID pDataRead; // pointer to data just read
int nLen; // length
char pszMessage[1000];// informational message
SOCKADDR_IN sin; // Internet address of client
IN_ADDR in; // IP address of client
int nStatus; // error status
// get pointer to list box used for status messages
CListBox *plb = (CListBox *)GetDlgItem(IDC_LIST_STATUS);
Trang 3Part IV ■ Programming with the WinSock Class Library
// echo the data back to the sender
if (m_pStream->Write(nLen, pDataRead) != CWINSOCK_NOERROR)
Trang 4Chapter 14 ■ Sample Applications 275
P2/Vol.6 Programming WinSock #30594-1 tullis 11.14.94 CH14 LP #3
Stream Echo Client CSECLNT
The stream echo client, CSECLNT, is a reimplementation of the SECLIENT program
described in Chapter 8 It uses a CFormView -derived object as its main interface The
header file for the CMainView object is shown in Listing 14.7 Its implementation is shown
in Listing 14.8 This object performs most of the work for the CSECLNT application.
OnInitialUpdate() is called soon after the object is created This function is
respon-sible for starting the WinSock subsystem, creating a client stream socket, prompting
for the host name or IP address of the CSESRV stream echo server, and setting a
five-second interval timer used for data writes When the server accepts the client’s
connection request on port 2000, O n S t r e a m ( ) is called with w P a r a m set to
CWINSOCK_YOU_ARE_CONNECTED When the five-second timer goes off, OnTimer() is called.
If there is no data waiting to be sent—denoted by the first byte of the outgoing buffer
m_pszBuf containing a NULL —an outgoing data stream is formatted and the stream socket
object’s Write() member function is called to send data to the designated server When
the write completes, OnStream() is called with wParam set to CWINSOCK_DONE_WRITING
The first byte of m_pszBuf is set to NULL to indicate that the buffer is available The
CMainView object is continually waiting for its previously sent data to be echoed back.
When data arrives on the stream socket, OnStream() is triggered with wParam set to
CWINSOCK_DONE_READING The data is read and the read buffer is then freed When the
client application is closed, CMainView ’s destructor is called, destroying the stream socket
object and shutting down the WinSock subsystem.
Listing 14.7 MAINVIEW.H for CSECLNT.
// mainview.h : header file
#include “cwinsock.h” // Windows Sockets classes
class CMainView : public CFormView
{
DECLARE_DYNCREATE(CMainView)
private:
CWinSock * m_pWinSock; // WinSock sub-system startup/.shutdown
CStreamSocket * m_pStream; // Stream socket to receive from
char m_pszBuf[100]; // buffer to send
char m_pszServer[100]; // host name or IP address of stream server
continues
Trang 5Part IV ■ Programming with the WinSock Class Library
enum { IDD = IDD_DIALOG_MAIN };
// NOTE: the ClassWizard will add data members here
virtual void DoDataExchange(CDataExchange* pDX); // DDX/DDV support
virtual void OnInitialUpdate();
// Generated message map functions
//{{AFX_MSG(CMainView)
afx_msg LONG OnStream(WPARAM wParam, LPARAM lParam);
afx_msg void OnTimer(UINT nIDEvent);
//}}AFX_MSG
DECLARE_MESSAGE_MAP()
};
/////////////////////////////////////////////////////////////////////////////
#define WM_USER_STREAM (WM_USER + 1)
Listing 14.8 MAINVIEW.CPP for CSECLNT.
// mainview.cpp : implementation file
IMPLEMENT_DYNCREATE(CMainView, CFormView)
Listing 14.7 continued
Trang 6Chapter 14 ■ Sample Applications 277
P2/Vol.6 Programming WinSock #30594-1 tullis 11.14.94 CH14 LP #3
// start the timer used to trigger the socket writes
SetTimer(1, 5000, NULL); // 5 second timer
// get pointer to list box used for status messages
CListBox *plb = (CListBox *)GetDlgItem(IDC_LIST_STATUS);
// initialize the WinSock object
m_pWinSock = new CWinSock;
Trang 7Part IV ■ Programming with the WinSock Class Library
278
P2/Vol.6 Programming WinSock #30594-1 tullis 11.14.94 CH14 LP #3
plb->InsertString(0, “WinSock initialization failed”);
delete m_pWinSock;
m_pWinSock = NULL;
return;
}
// prompt for server information
// (host name or IP address of stream server)
// initialize the stream socket object
m_pStream = new CStreamSocket(this, WM_USER_STREAM);
/////////////////////////////////////////////////////////////////////////////
Listing 14.8 continued
Trang 8Chapter 14 ■ Sample Applications 279
P2/Vol.6 Programming WinSock #30594-1 tullis 11.14.94 CH14 LP #3
LPVOID pDataWritten; // pointer to data that is completely written
LPVOID pDataRead; // pointer to data just read
int nLen; // length
char pszMessage[1000];// informational message
// get pointer to list box used for status messages
CListBox *plb = (CListBox *)GetDlgItem(IDC_LIST_STATUS);
Trang 9Part IV ■ Programming with the WinSock Class Library
static int nSendCount = 1; // used to generate unique message
char pszMessage[1000]; // informational message
// make sure we are not sending out of a bad stream socket
if (m_pStream == NULL)
return;
// get pointer to list box used for status messages
CListBox *plb = (CListBox *)GetDlgItem(IDC_LIST_STATUS);
// send the buffer unless the previous send hasn’t completed yet
if ((*m_pszBuf) == ‘\0’)
{
wsprintf(m_pszBuf, “Hello %d”, nSendCount);
++nSendCount;
// be sure to send terminating NULL character
if (m_pStream->Write(lstrlen(m_pszBuf) + 1, m_pszBuf) != CWINSOCK_NOERROR)
Running the Stream Echo Server and Client
Following is a sample sequence of events that occur when the stream echo client and server are run:
1 Run CSESRV.
2 Run CSECLNT on the same or a different computer It prompts for the host name or IP address CSESRV is using A connection to the server is attempted.
3 CSESRV’s CMainView::OnStreamSrv() is called with the
CWINSOCK_READY_TO_ACCEPT_CONNECTION event and, if the m_pStream socket is not yet connected to a client, a connection attempt is made.
4 When the server’s connection accept succeeds, OnStream() is called with wParam
set to CWINSOCK_YOU_ARE_CONNECTED
5 CDECLNT’s CMainView::OnStream() is also called with the
CWINSOCK_YOU_ARE_CONNECTED event.
Listing 14.8 continued
Trang 10Chapter 14 ■ Sample Applications 281
P2/Vol.6 Programming WinSock #30594-1 tullis 11.14.94 CH14 LP #3
6 In five seconds, the timer will trigger in CSECLNT, causing
CMainView::OnTimer() to get called No bytes are waiting to be sent yet, so the
outgoing buffer is filled and written to the connected server.
7 CMainView::OnStream() is called in CSECLNT with a CWINSOCK_DONE_WRITING
notice The outgoing buffer is then marked as unused so that it may be used
with the next triggering of CMainView::OnTimer()
8 CMainView::OnStream() is called in CSESRV with a CWINSOCK_DONE_READING
notice The data is read and immediately echoed back to the client.
9 CMainView::OnStream() is called in CSESRV with a CWINSOCK_DONE_WRITING
notice The data is then freed.
10 CMainView::OnStream() is called in CSECLNT with a CWINSOCK_DONE_READING
notice The echoed data is read and then freed.
11 Another timer goes off in CSECLNT and the process repeats.
If CSECLNT is closed first, CMainView::OnStream() is called in CSESRV with a
CWINSOCK_LOST_CONNECTION notice If CSESRV is closed first, CMainView::OnStream()
is called in CSECLNT with a CWINSOCK_LOST_CONNECTION notice.
Summary
This chapter demonstrates the use of the CWinSock , CDatagramSocket , and CStreamSocket
objects These objects are designed to make socket programming easier for you.
It is hoped that the comparison of these sample programs with those of Chapter 8 proves
that the design goals of the WinSock class library, described in Chapter 9, are met A
comparison also reveals one of the limitations of the CDatagramSocket and CStreamSocket
objects These objects don’t have the capability of letting the WinSock subsystem
as-sign an unused port to a server socket Instead, the port must be specified in terms of its
port number or service name, in this case to port number 2000.
The next chapter uses the WinSock class library objects in a sample client-server
data-base environment.
Trang 11Chapter 15 ■ Practical Client/Server Database Application 283
p2/v6 SN8 Programming WinSock #30594-1 tullis 11.14.94 CH15 LP #3
15
Practical Client/ Server Database Application
Practical Client/ Server Database Application
Trang 12p2/v6 SN8 Programming WinSock #30594-1 tullis 11.14.94 CH15 LP #3
Part IV ■ Programming with the WinSock Class Library
The client (INICLNT) and server (INISRV) use the CDatagramSocket and CStreamSocket
objects In previous chapters, the server examples could handle only one client tion at a time This server is more realistic in that it can service several clients simulta- neously The server is implemented as a Multiple Document Interface application in which each window represents a client connection The client is a simple Single Docu- ment Interface application A stream socket connection is used to transmit database commands and responses between the client and server A datagram socket is used to
connec-send a heartbeat message from the server to the client The heartbeat is a block of data
that is sent between the client and server to let each know that the other side is still tive and able to communicate This heartbeat allows the client program to show a visual indication of the client/server link status.
ac-The “database” in this example uses Windows’ built-in INI file facility An INI file is used to maintain configuration information for an application An example INI file is shown below:
[boot] is called the section 386grabber is an example of an entry or key ajvga.3gr is its
value The GetPrivateProfileString() and WritePrivateProfileString() SDK tions are used to read and write an INI file string value, respectively.
func-Client INICLNT
The client application, INICLNT, uses a CFormView -derived object as its main face The header file for the CMainView object is shown in Listing 15.1, which appears later in this section Its implementation is shown in Listing 15.2, which also appears later in this section This object performs most of the work for the INICLNT applica- tion OnInitialUpdate() is called soon after the object is created This function is re- sponsible for starting the WinSock subsystem, creating a client stream socket, creating
inter-a server dinter-atinter-agrinter-am socket for the heinter-artbeinter-at, prompting for the host ninter-ame or IP inter-address
of the INISRV server, and setting a timer interval used for the heartbeats When the
Trang 13Chapter 15 ■ Practical Client/Server Database Application 285
p2/v6 SN8 Programming WinSock #30594-1 tullis 11.14.94 CH15 LP #3
server accepts the client’s connection request, OnStream() is called with wParam set to
CWINSOCK_YOU_ARE_CONNECTED
Command Identifier
The user enters the read or write parameters into the appropriate fields and then selects
either the Read or Write button, triggering O n C l i c k e d B u t t o n R e a d ( ) or
OnClickedButtonWrite() , respectively These functions format the database command
and call FillAndSendDBCmd() , which assigns a unique identifier to the database
com-mand and sends it to the server The database comcom-mand has the following structure:
typedef struct tagDBCOMMAND
{
int nID; // database command identifier
int nCommand; // database command
char szFile[DBBUFSIZE]; // INI file
char szSection[DBBUFSIZE]; // section of INI file
char szEntry[DBBUFSIZE]; // entry within section of INI file
char szValue[DBBUFSIZE]; // value of entry within section of INI file
} DBCOMMAND, FAR * LPDBCOMMAND;
ClassWizard is used to limit the number of bytes that may be entered into the data entry
fields of CMainView This limit is set to 40 to match the DBBUFSIZE value used in the
preceding DBCOMMAND definition.
Possible commands for the client are DB_READ and DB_WRITE A unique identifier is
as-signed because there could be multiple outstanding database requests; the client and
server operate totally asynchronously The identifier may be used to correlate the
com-mand and its asynchronous response Once the comcom-mand is successfully sent, OnStream()
is called with wParam set to CWINSOCK_DONE_WRITING When the server is done processing
the database command, it sends a response to the client, triggering OnStream() with wParam
set to CWINSOCK_DONE_READING The HandleRead() function processes the database
re-sponse sent by the server.
Heartbeat Link Status Indicator
This application has a timer that’s used to keep track of missing heartbeat messages.
The OnDatagram() function handles reception of the heartbeats sent from the connected
server If one heartbeat message is missed, the traffic light status indicator changes to a
yellow light Missing two or more heartbeats causes the light to turn red If the
heart-beats are received without fail, the light remains green This provides for a visual cue as
to the state of the server.
When CMainView ’s destructor is called, the stream and datagram sockets are destroyed
and the WinSock subsystem is shut down.
Trang 14p2/v6 SN8 Programming WinSock #30594-1 tullis 11.14.94 CH15 LP #3
Part IV ■ Programming with the WinSock Class Library
286
Listing 15.1 MAINVIEW.H for INICLNT.
// mainview.h : header file
//
/////////////////////////////////////////////////////////////////////////////// CMainView form view
CWinSock * m_pWinSock; // WinSock sub–system startup/.shutdown
CStreamSocket * m_pStream; // Stream socket to receive from
CDatagramSocket * m_pDatagram; // Datagram socket to receive heartbeat
char m_pszServer[100]; // host name or IP address of stream server int m_nHeartbeat; // heartbeat count
HICON m_hRed, m_hYellow, m_hGreen; // link status icons
void HandleRead();
void FillAndSendDBCmd(LPDBCOMMAND pdb, LPCSTR szFile,
LPCSTR szSection, LPCSTR szEntry, LPCSTR szValue = NULL);
virtual void DoDataExchange(CDataExchange* pDX); // DDX/DDV support
virtual void OnInitialUpdate();
// Generated message map functions
//{{AFX_MSG(CMainView)
afx_msg LONG OnStream(WPARAM wParam, LPARAM lParam);
Trang 15Chapter 15 ■ Practical Client/Server Database Application 287
p2/v6 SN8 Programming WinSock #30594-1 tullis 11.14.94 CH15 LP #3
afx_msg LONG OnDatagram(WPARAM wParam, LPARAM lParam);
afx_msg void OnTimer(UINT nIDEvent);
afx_msg void OnClickedButtonRead();
afx_msg void OnClickedButtonWrite();
//}}AFX_MSG
DECLARE_MESSAGE_MAP()
};
/////////////////////////////////////////////////////////////////////////////
#define WM_USER_STREAM (WM_USER + 1)
#define WM_USER_DATAGRAM (WM_USER + 2)
Listing 15.2 MAINVIEW.CPP for INICLNT.
// mainview.cpp : implementation file
Trang 16p2/v6 SN8 Programming WinSock #30594-1 tullis 11.14.94 CH15 LP #3
Part IV ■ Programming with the WinSock Class Library
// start the timer used to keep track of heartbeats
SetTimer(1, HEARTBEAT_DELAY, NULL);
// get pointer to list box used for status messages
CListBox *plb = (CListBox *)GetDlgItem(IDC_LIST_STATUS);
// initialize the WinSock object
Listing 15.2 continued