Tài liệu hạn chế xem trước, để xem đầy đủ mời bạn chọn Tải xuống
1
/ 33 trang
THÔNG TIN TÀI LIỆU
Thông tin cơ bản
Định dạng
Số trang
33
Dung lượng
238,21 KB
Nội dung
Part IV ■ Programming with the WinSock Class Library 272 P2/Vol.6 Programming WinSock #30594-1 tullis 11.14.94 CH14 LP #3 } void CMainView::OnInitialUpdate() { // get pointer to list box used for status messages CListBox *plb = (CListBox *)GetDlgItem(IDC_LIST_STATUS); // initialize the WinSock object m_pWinSock = new CWinSock; if (m_pWinSock->Startup() == CWINSOCK_NOERROR) plb->InsertString(0, “WinSock initialized”); else { plb->InsertString(0, “WinSock initialization failed”); delete m_pWinSock; m_pWinSock = NULL; return; } // initialize the stream socket object m_pStreamSrv = new CStreamSocket(this, WM_USER_STREAMSRV); if (m_pStreamSrv->CreateSocket(2000) == CWINSOCK_NOERROR) plb->InsertString(0, “Stream server created (port 2000)”); else { plb->InsertString(0, “Stream server creation failed”); delete m_pStreamSrv; m_pStreamSrv = NULL; } } BEGIN_MESSAGE_MAP(CMainView, CFormView) //{{AFX_MSG_MAP(CMainView) ON_MESSAGE(WM_USER_STREAMSRV, OnStreamSrv) ON_MESSAGE(WM_USER_STREAM, OnStream) //}}AFX_MSG_MAP END_MESSAGE_MAP() ///////////////////////////////////////////////////////////////////////////// // CMainView message handlers ///////////////////////////////////////////////////////////////////////////// // CMainView::OnStreamSrv() // // Receives messages from the stream server object. // LONG CMainView::OnStreamSrv(WPARAM wParam, LPARAM lParam) { // get pointer to list box used for status messages CListBox *plb = (CListBox *)GetDlgItem(IDC_LIST_STATUS); switch (wParam) { Listing 14.6. continued Chapter 14 ■ Sample Applications 273 P2/Vol.6 Programming WinSock #30594-1 tullis 11.14.94 CH14 LP #3 case CWINSOCK_READY_TO_ACCEPT_CONNECTION: // make sure the server is not already servicing a client if (m_pStream != NULL) { plb->InsertString(0, “Already servicing a client”); break; } // accept the client connection int nStatus; m_pStream = new CStreamSocket(this, WM_USER_STREAM); nStatus = m_pStreamSrv->Accept(m_pStream); if (nStatus != CWINSOCK_NOERROR) { delete m_pStream; m_pStream = NULL; plb->InsertString(0, “Error accepting client connection”); break; } else plb->InsertString(0, “Accepted client connection”); break; default: break; } return 0L; } ///////////////////////////////////////////////////////////////////////////// // CMainView::OnStream() // // Receives messages from the connected stream object. // LONG CMainView::OnStream(WPARAM wParam, LPARAM lParam) { 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); switch (wParam) { case CWINSOCK_DONE_WRITING: // lParam = pointer to data that was sent pDataWritten = (LPVOID)lParam; wsprintf(pszMessage, “Data sent (%s)”, pDataWritten); plb->InsertString(0, pszMessage); free(pDataWritten); break; continues Part IV ■ Programming with the WinSock Class Library 274 P2/Vol.6 Programming WinSock #30594-1 tullis 11.14.94 CH14 LP #3 case CWINSOCK_ERROR_WRITING: // lParam = pointer to data that generated error sending pDataWritten = (LPVOID)lParam; wsprintf(pszMessage, “Error sending data (%s)”, pDataWritten); plb->InsertString(0, pszMessage); free(pDataWritten); break; case CWINSOCK_DONE_READING: // lParam = # data chunks in queue pDataRead = m_pStream->Read(&nLen); wsprintf(pszMessage, “Data received (%s)”, pDataRead); plb->InsertString(0, pszMessage); // echo the data back to the sender if (m_pStream->Write(nLen, pDataRead) != CWINSOCK_NOERROR) { wsprintf(pszMessage, “Error sending data (%s)”, pDataRead); plb->InsertString(0, pszMessage); free(pDataRead); } break; case CWINSOCK_ERROR_READING: break; case CWINSOCK_YOU_ARE_CONNECTED: // print out client information nStatus = m_pStream->GetPeerName(&sin); if (nStatus == CWINSOCK_NOERROR) { memcpy(&in, &sin.sin_addr.s_addr, 4); wsprintf(pszMessage, “Connected to client %s, %d”, inet_ntoa(in), ntohs(sin.sin_port)); plb->InsertString(0, pszMessage); } else plb->InsertString(0, “Error getting client name”); break; case CWINSOCK_LOST_CONNECTION: // client closed the connection m_pStream->DestroySocket(); delete m_pStream; m_pStream = NULL; plb->InsertString(0, “Client closed connection”); break; default: break; } return 0L; } Listing 14.6. continued Chapter 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, OnStream() is called with wParam 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 // ///////////////////////////////////////////////////////////////////////////// // CMainView form view #ifndef __AFXEXT_H__ #include <afxext.h> #endif #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 Part IV ■ Programming with the WinSock Class Library 276 P2/Vol.6 Programming WinSock #30594-1 tullis 11.14.94 CH14 LP #3 protected: CMainView(); // protected constructor used by dynamic creation // Form Data public: //{{AFX_DATA(CMainView) enum { IDD = IDD_DIALOG_MAIN }; // NOTE: the ClassWizard will add data members here //}}AFX_DATA // Attributes public: // Operations public: // Implementation protected: virtual ~CMainView(); 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 // #include “stdafx.h” #include “cseclnt.h” #include “mainview.h” #include “servdlg.h” #ifdef _DEBUG #undef THIS_FILE static char BASED_CODE THIS_FILE[] = __FILE__; #endif ///////////////////////////////////////////////////////////////////////////// // CMainView IMPLEMENT_DYNCREATE(CMainView, CFormView) Listing 14.7. continued Chapter 14 ■ Sample Applications 277 P2/Vol.6 Programming WinSock #30594-1 tullis 11.14.94 CH14 LP #3 CMainView::CMainView() : CFormView(CMainView::IDD) { //{{AFX_DATA_INIT(CMainView) // NOTE: the ClassWizard will add member initialization here //}}AFX_DATA_INIT // initialize class variables m_pWinSock = NULL; m_pStream = NULL; (*m_pszBuf) = ‘\0’; } CMainView::~CMainView() { // free the stream and WinSock objects if (m_pStream) { m_pStream->DestroySocket(); delete m_pStream; m_pStream = NULL; } if (m_pWinSock) { m_pWinSock->Shutdown(); delete m_pWinSock; m_pWinSock = NULL; } } void CMainView::DoDataExchange(CDataExchange* pDX) { CFormView::DoDataExchange(pDX); //{{AFX_DATA_MAP(CMainView) // NOTE: the ClassWizard will add DDX and DDV calls here //}}AFX_DATA_MAP } void CMainView::OnInitialUpdate() { // 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; if (m_pWinSock->Startup() == CWINSOCK_NOERROR) plb->InsertString(0, “WinSock initialized”); else { continues Part 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) while (1) { CServerDlg dlg; dlg.DoModal(); if (dlg.m_stringServer.GetLength() < sizeof(m_pszServer)) { lstrcpy(m_pszServer, dlg.m_stringServer); break; } else AfxMessageBox(“Host name or IP address too long”); } // initialize the stream socket object m_pStream = new CStreamSocket(this, WM_USER_STREAM); if (m_pStream->CreateSocket() == CWINSOCK_NOERROR) plb->InsertString(0, “Stream created”); else { plb->InsertString(0, “Stream creation failed”); delete m_pStream; m_pStream = NULL; } // connect the client to the server if (m_pStream->Connect(m_pszServer, 2000) == CWINSOCK_NOERROR) plb->InsertString(0, “Stream connect attempt made”); else { plb->InsertString(0, “Stream connect attempt failed”); delete m_pStream; m_pStream = NULL; } } BEGIN_MESSAGE_MAP(CMainView, CFormView) //{{AFX_MSG_MAP(CMainView) ON_MESSAGE(WM_USER_STREAM, OnStream) ON_WM_TIMER() //}}AFX_MSG_MAP END_MESSAGE_MAP() ///////////////////////////////////////////////////////////////////////////// // CMainView message handlers ///////////////////////////////////////////////////////////////////////////// Listing 14.8. continued Chapter 14 ■ Sample Applications 279 P2/Vol.6 Programming WinSock #30594-1 tullis 11.14.94 CH14 LP #3 // CMainView::OnStream() // // Receives messages from the stream object. // LONG CMainView::OnStream(WPARAM wParam, LPARAM lParam) { 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); switch (wParam) { case CWINSOCK_DONE_WRITING: // lParam = pointer to data that was sent pDataWritten = (LPVOID)lParam; wsprintf(pszMessage, “Data sent (%s)”, pDataWritten); plb->InsertString(0, pszMessage); (*m_pszBuf) = ‘\0’; // same as (*pDataWritten) = ‘\0’; break; case CWINSOCK_ERROR_WRITING: // lParam = pointer to data that generated error sending pDataWritten = (LPVOID)lParam; wsprintf(pszMessage, “Error sending data (%s)”, pDataWritten); plb->InsertString(0, pszMessage); (*m_pszBuf) = ‘\0’; // same as (*pDataWritten) = ‘\0’; break; case CWINSOCK_DONE_READING: // lParam = # data chunks in queue pDataRead = m_pStream->Read(&nLen); wsprintf(pszMessage, “Data received (%s)”, pDataRead); plb->InsertString(0, pszMessage); free(pDataRead); break; case CWINSOCK_ERROR_READING: break; case CWINSOCK_YOU_ARE_CONNECTED: plb->InsertString(0, “Connected to server”); break; case CWINSOCK_LOST_CONNECTION: // server closed the connection m_pStream->DestroySocket(); delete m_pStream; m_pStream = NULL; plb->InsertString(0, “Server closed connection”); break; default: break; } continues Part IV ■ Programming with the WinSock Class Library 280 P2/Vol.6 Programming WinSock #30594-1 tullis 11.14.94 CH14 LP #3 return 0L; } void CMainView::OnTimer(UINT nIDEvent) { 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) { (*m_pszBuf) = ‘\0’; wsprintf(pszMessage, “Error sending data (%s)”, m_pszBuf); plb->InsertString(0, pszMessage); } } CFormView::OnTimer(nIDEvent); } 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 Chapter 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. [...]... represented an entire // database command structure continues p2/v6 SN8 Programming WinSock #30 594 -1 tullis 11.14 .94 CH15 LP #3 29 1 29 2 Part IV s Programming with the WinSock Class Library Listing 15 .2 continued if (nLen != sizeof(dbcmd)) { plb–>InsertString(0, “This client cannot handle partial blocks”); free(pDataRead); return; } // copy the data to a database command structure memcpy(&dbcmd, pDataRead,... database command and call FillAndSendDBCmd(), which assigns a unique identifier to the database command and sends it to the server The database command has the following structure: typedef struct tagDBCOMMAND { int nID; // int nCommand; // char szFile[DBBUFSIZE]; // char szSection[DBBUFSIZE]; // char szEntry[DBBUFSIZE]; // char szValue[DBBUFSIZE]; // } DBCOMMAND, FAR * LPDBCOMMAND; database command identifier... allocate memory for database command structure LPDBCOMMAND pdb = (LPDBCOMMAND)malloc(sizeof(DBCOMMAND)); if (pdb == NULL) plb–>InsertString(0, “Cannot allocate memory for command”); else { memset(pdb, 0, sizeof(DBCOMMAND)); p2/v6 SN8 Programming WinSock #30 594 -1 tullis 11.14 .94 CH15 LP #3 Chapter 15 s Practical Client/Server Database Application UpdateData(TRUE); FillAndSendDBCmd(pdb, m_stringFile,... client’s traffic light feature is easily tested After the client is connected to the server for 10 and then 20 seconds, the client’s status should go from green to yellow to red, and back to green HEARTBEAT_TEST ifdef p2/v6 SN8 Programming WinSock #30 594 -1 tullis 11.14 .94 CH15 LP #3 29 9 300 Part IV s Programming with the WinSock Class Library Listing 15.5 SRVVIEW.H for INISRV // srvview.h : header... informational message // get pointer to list box used for status messages CListBox *plb = (CListBox *)GetDlgItem(IDC_LIST_STATUS); continues p2/v6 SN8 Programming WinSock #30 594 -1 tullis 11.14 .94 CH15 LP #3 29 3 29 4 Part IV s Programming with the WinSock Class Library Listing 15 .2 continued switch (wParam) { case CWINSOCK_DONE_WRITING: // lParam = pointer to data that was sent // should never happen but make... datagram”); delete m_pDatagram; m_pDatagram = NULL; m_pStream–>DestroySocket(); delete m_pStream; m_pStream = NULL; return; } continues p2/v6 SN8 Programming WinSock #30 594 -1 tullis 11.14 .94 CH15 LP #3 28 9 29 0 Part IV s Programming with the WinSock Class Library Listing 15 .2 continued // connect the client to the server if (m_pStream–>Connect(m_pszServer, STREAM_PORT) == CWINSOCK_NOERROR) plb–>InsertString(0,... m_hRed = AfxGetApp()–>LoadIcon(IDI_ICON_RED); continues p2/v6 SN8 Programming WinSock #30 594 -1 tullis 11.14 .94 CH15 LP #3 28 7 28 8 Part IV s Programming with the WinSock Class Library Listing 15 .2 continued m_hYellow = AfxGetApp()–>LoadIcon(IDI_ICON_YELLOW); m_hGreen = AfxGetApp()–>LoadIcon(IDI_ICON_GREEN); } CMainView::~CMainView() { // free the stream and WinSock objects if (m_pStream) { m_pStream–>DestroySocket();... initialize the WinSock object m_pWinSock = new CWinSock; if (m_pWinSock–>Startup() == CWINSOCK_NOERROR) plb–>InsertString(0, “WinSock initialized”); else { continues p2/v6 SN8 Programming WinSock #30 594 -1 tullis 11.14 .94 CH15 LP #3 29 7 29 8 Part IV s Programming with the WinSock Class Library Listing 15.4 continued plb–>InsertString(0, “WinSock initialization failed”); delete m_pWinSock; m_pWinSock = NULL;... 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 p2/v6 SN8 Programming WinSock #30 594 -1 tullis 11.14 .94 CH15 LP #3 28 5 28 6 Part IV s Programming with the WinSock Class Library Listing 15.1 MAINVIEW.H for INICLNT // mainview.h : header file // /////////////////////////////////////////////////////////////////////////////... “Received command from client”); p2/v6 SN8 Programming WinSock #30 594 -1 tullis 11.14 .94 CH15 LP #3 Chapter 15 s Practical Client/Server Database Application // verify that the data represented an entire // database command structure if (nLen != sizeof(DBCOMMAND)) { plb–>InsertString(0, “This server cannot handle partial blocks”); free(pDataRead); return; } // copy the data to a database command structure . entire // database command structure continues p2/v6 SN8 Programming WinSock #30 594 -1 tullis 11.14 .94 CH15 LP #3 Part IV ■ Programming with the WinSock Class Library 29 2 if (nLen != sizeof(dbcmd)) . is called, the stream and datagram sockets are destroyed and the WinSock subsystem is shut down. p2/v6 SN8 Programming WinSock #30 594 -1 tullis 11.14 .94 CH15 LP #3 Part IV ■ Programming with the. initialize the WinSock object Listing 15 .2. continued Chapter 15 ■ Practical Client/Server Database Application 28 9 p2/v6 SN8 Programming WinSock #30 594 -1 tullis 11.14 .94 CH15 LP #3 m_pWinSock = new