Networking and Network Programming 2 TCP/IP phần 8 ppt

33 189 1
Networking and Network Programming 2 TCP/IP phần 8 ppt

Đang tải... (xem toàn văn)

Tài liệu hạn chế xem trước, để xem đầy đủ mời bạn chọn Tải xuống

Thông tin tài liệu

p2v6 Prog. WinSock #30594-1 tullis 11.14.94 CH12 LP #3 Part III ■ WinSock Class Library 238 pointer to the data is returned and the integer pointed to by pnLen contains the number of bytes in the data buffer returned. On error, NULL is returned. ///////////////////////////////////////////////////////////////////////////// // CStreamSocket::Read() // // Read data that has been received by the socket. // // This function takes a pointer to an integer that will be filled // with the length of the data read. // // A pointer to the data is returned on success. The application // using this object must free this pointer. NULL is returned on failure. // LPVOID CStreamSocket::Read(LPINT pnLen) { LPVOID pData = NULL; // check to see if there is data to retrieve if (!m_listRead.IsEmpty()) { // remove the stream data from the list LPSTREAMDATA pStreamData = (LPSTREAMDATA)m_listRead.RemoveHead(); pData = pStreamData–>pData; *pnLen = pStreamData–>nLen; delete pStreamData; } return pData; } CStreamSocket::OnWinSockEvent() The OnWinSockEvent() member function handles the asynchronous event notification messages sent by the WinSock subsystem. The WinSock events of interest are FD_READ, FD_WRITE, and FD_CLOSE. If the socket is a server, interest is also expressed in the FD_ACCEPT event. For clients, FD_CONNECT is the additional event of interest. Interest in these events is registered by the call to WSAAsyncSelect() in the CreateSocket() member func- tion. The Microsoft Foundation Class message map facility is used to map the CWINSOCK_EVENT_NOTIFICATION message to the OnWinSockEvent() function. The mes- sage map looks like the following: // message map BEGIN_MESSAGE_MAP(CStreamSocket, CWnd) //{{AFX_MSG_MAP(CStreamSocket) //}}AFX_MSG_MAP ON_MESSAGE(CWINSOCK_EVENT_NOTIFICATION, OnWinSockEvent) END_MESSAGE_MAP() OnWinSockEvent() checks for errors and, if there are none, executes an appropriate message handler. For the FD_ACCEPT, FD_CONNECT, and FD_CLOSE events, a message is simply re- layed to the parent of the stream object. Before the FD_CLOSE message is relayed to the Chapter 12 ■ CStreamSocket 239 p2v6 Prog. WinSock #30594-1 tullis 11.14.94 CH12 LP #3 application by the sending of the CWINSOCK_LOST_CONNECTION message, a check is made for additional data arriving on the socket. The CWINSOCK_LOST_CONNECTION message isn’t sent to the application until all queued data is received and processed. ///////////////////////////////////////////////////////////////////////////// // CStreamSocket::OnWinSockEvent() // // Called when there is an asynchronous event on the socket. // LONG CStreamSocket::OnWinSockEvent(WPARAM wParam, LPARAM lParam) { // check for an error if (WSAGETSELECTERROR(lParam) != 0) return 0L; // what event are we being notified of? switch (WSAGETSELECTEVENT(lParam)) { case FD_READ: return HandleRead(wParam, lParam); break; case FD_WRITE: return HandleWrite(wParam, lParam); break; case FD_ACCEPT: // tell the parent window that a client would like to connect // to the server socket m_pParentWnd–>PostMessage(m_uMsg, CWINSOCK_READY_TO_ACCEPT_CONNECTION); break; case FD_CONNECT: // tell the parent window that the socket has connected m_pParentWnd–>PostMessage(m_uMsg, CWINSOCK_YOU_ARE_CONNECTED); break; case FD_CLOSE: // check for more data queued on the socket // (don’t tell the application that the socket is closed // until all data has been read and notification has been posted) if (HandleRead(wParam, lParam)) { // fake the close event to try again PostMessage(CWINSOCK_EVENT_NOTIFICATION, wParam, lParam); break; } // tell the parent window that the socket is closed m_pParentWnd–>PostMessage(m_uMsg, CWINSOCK_LOST_CONNECTION); break; default: // this should never happen ASSERT(0); break; } return 0L; } p2v6 Prog. WinSock #30594-1 tullis 11.14.94 CH12 LP #3 Part III ■ WinSock Class Library 240 CStreamSocket::HandleRead() The HandleRead() member function handles the asynchronous FD_READ event notifica- tion messages sent by the WinSock subsystem. This function is called when WinSock thinks a read from the socket will succeed. The first portion of this function allocates memory for the data and the data’s length. A recv() is then attempted. If the receive is successful, the data is added to the read queue. If everything goes OK, the m_uMsg mes- sage is posted to the application window that owns this stream socket object, with wParam set to CWINSOCK_DONE_READING and lParam set to the number of data buffers waiting to be read. When the application receives this message, it should call the Read() member function. If there is an error in receiving the data, wParam is set to CWINSOCK_ERROR_READING. If data is received and the CWINSOCK_DONE_READING message is sent to the application, a 1 is returned by HandleRead(), otherwise, 0 is returned. This differentiation is used by OnWinSockEvent()’s FD_CLOSE handler to let it know when all data received on the socket is completely processed. ///////////////////////////////////////////////////////////////////////////// // CStreamSocket::HandleRead() // // Called when there is an asynchronous read event on the socket. // // If the read was successful, the data and its length are stored // in the read queue. Upon a successful read, the application // window using this object is then notified with the m_uMsg message // (wParam set to CWINSOCK_DONE_READING; lParam set to the number of // data chunks in the read queue). At this point, the application // should call Read(). If the read fails for some reason, the m_uMsg // is sent with wParam set to CWINSOCK_ERROR_READING. // LONG CStreamSocket::HandleRead(WPARAM wParam, LPARAM lParam) { while (1) { // allocate memory for incoming data LPVOID pData = malloc(READ_BUF_LEN); LPSTREAMDATA pStreamData = new STREAMDATA; if ((pData == NULL) || (pStreamData == NULL)) { // free anything that was allocated if (pData != NULL) free(pData); pData = NULL; if (pStreamData != NULL) delete pStreamData; pStreamData = NULL; // tell the parent that a possible data read failed m_pParentWnd–>PostMessage(m_uMsg, CWINSOCK_ERROR_READING); // fake the event to try again PostMessage(CWINSOCK_EVENT_NOTIFICATION, m_s, WSAMAKESELECTREPLY(FD_READ, 0)); Chapter 12 ■ CStreamSocket 241 p2v6 Prog. WinSock #30594-1 tullis 11.14.94 CH12 LP #3 break; } // receive data int nBytesRead = recv(m_s, (LPSTR)pData, READ_BUF_LEN, 0); if (nBytesRead == SOCKET_ERROR) { // free memory for incoming data free(pData); pData = NULL; delete pStreamData; pStreamData = NULL; // if the error is just that the read would block, // don’t do anything; we’ll get another FD_READ soon m_nLastError = WSAGetLastError(); if (m_nLastError == WSAEWOULDBLOCK) m_nLastError = 0; else // tell the parent that a data read failed m_pParentWnd–>PostMessage(m_uMsg, CWINSOCK_ERROR_READING); break; } // make sure some data was read if (nBytesRead == 0) { // free memory for incoming data free(pData); pData = NULL; delete pStreamData; pStreamData = NULL; break; } // add the data to the list pStreamData–>pData = pData; pStreamData–>nLen = nBytesRead; TRY { m_listRead.AddTail(pStreamData); } CATCH (CMemoryException, e) { free(pData); pData = NULL; delete pStreamData; pStreamData = NULL; // tell the parent that a data read failed m_pParentWnd–>PostMessage(m_uMsg, CWINSOCK_ERROR_READING); break; } END_CATCH // tell the parent that data has been read p2v6 Prog. WinSock #30594-1 tullis 11.14.94 CH12 LP #3 Part III ■ WinSock Class Library 242 m_pParentWnd–>PostMessage(m_uMsg, CWINSOCK_DONE_READING, (LPARAM)m_listRead.GetCount()); // 1 is returned if there is data so CStreamSocket::OnWinSockEvent()’s // FD_CLOSE handler will know when the socket can really be closed return 1L; break; } return 0L; } CStreamSocket::HandleWrite() The HandleWrite() member function handles the asynchronous FD_WRITE event notifi- cation messages sent by the WinSock subsystem. This function is called when WinSock thinks a write out of the socket will succeed. The first portion of this socket checks to see whether there is any data waiting to be sent from the write queue. This queue is added to by the application calling the Write() member function. If there is data in the write queue, a send() is attempted. If the send() would block, the data is retained to have another send attempted at a later time. If the send() fails with an error other than WSAEWOULDBLOCK, the data is removed from the write queue and the m_uMsg message is sent to the application window with wParam set to CWINSOCK_ERROR_WRITING and lParam the pointer to the data that was unsuccessfully sent. If the send() succeeds but not all of the bytes are sent, a pointer into the buffer is retained until the next time HandleWrite() is executed. When the entire buffer is successfully sent, wParam is set to CWINSOCK_DONE_WRITING and lParam is the data pointer. When the application receives this message notification, it’s safe to free or reuse the storage space pointed to by the pointer returned in lParam. ///////////////////////////////////////////////////////////////////////////// // CStreamSocket::HandleWrite() // // Called when there is an asynchronous write event on the socket. // // If there is data in the write queue waiting to be sent, // a WinSock send is attempted. If the send is successful, // a m_uMsg message is sent to the application window with // wParam set to CWINSOCK_DONE_WRITING and lParam set to the // address of the data that was sent. On send failure, // wParam is set to CWINSOCK_ERROR_WRITING and lParam set to // the address of the data which couldn’t be sent. In either // case, the application may free the pointer pointing to // the data or reuse that data buffer. It is possible for the // entire amount of data to not be sent in one call to send(). // In this case, an attempt is made to send the remaining portion // of that block of data the next time HandleWrite() is invoked. // // Chapter 12 ■ CStreamSocket 243 p2v6 Prog. WinSock #30594-1 tullis 11.14.94 CH12 LP #3 LONG CStreamSocket::HandleWrite(WPARAM wParam, LPARAM lParam) { LPSTREAMDATA pStreamData; // pointer to stream data structure LPVOID pData; // pointer to buffer to send int nLen; // total length of buffer to send static LPVOID pDataRemaining = NULL; // pointer into buffer to send static int nLenRemaining = 0; // number of bytes left to send while (1) { // check to see if there is any data to send if (m_listWrite.IsEmpty()) break; // if we are not in the middle of another buffer send, // get data and data length from the write queue pStreamData = (LPSTREAMDATA)m_listWrite.GetHead(); // not RemoveHead() pData = pStreamData–>pData; nLen = pStreamData–>nLen; if (pDataRemaining == NULL) { pDataRemaining = pData; nLenRemaining = nLen; } // send the data BOOL bRemove = FALSE; // remove data from queue? int nBytesSent = send(m_s, (LPCSTR)pDataRemaining, nLenRemaining, 0); if (nBytesSent == SOCKET_ERROR) { // if the error is just that the send would block, // don’t do anything; we’ll get another FD_WRITE soon m_nLastError = WSAGetLastError(); if (m_nLastError == WSAEWOULDBLOCK) m_nLastError = 0; else { bRemove = TRUE; m_pParentWnd–>PostMessage(m_uMsg, CWINSOCK_ERROR_WRITING, (LPARAM)pData); } } else { // if data was sent, we must still check to see // if all the bytes were sent if (nBytesSent == nLenRemaining) { bRemove = TRUE; m_pParentWnd–>PostMessage(m_uMsg, CWINSOCK_DONE_WRITING, (LPARAM)pData); } else { // the complete buffer was not sent so adjust // these values accordingly pDataRemaining = (LPVOID)((LPCSTR)pDataRemaining + nBytesSent); nLenRemaining = nLenRemaining – nBytesSent; } p2v6 Prog. WinSock #30594-1 tullis 11.14.94 CH12 LP #3 Part III ■ WinSock Class Library 244 } // if the data was completely sent or there was // a real error, remove the data from the queue if (bRemove) { delete pStreamData; m_listWrite.RemoveHead(); pDataRemaining = NULL; nLenRemaining = 0; } // if there is more data to send, trigger this FD_WRITE handler if (!m_listWrite.IsEmpty()) PostMessage(CWINSOCK_EVENT_NOTIFICATION, m_s, WSAMAKESELECTREPLY(FD_WRITE, 0)); break; } return 0L; } CStreamSocket::DestroySocket() The DestroySocket() member function removes any data queued up on the read or write queues, closes the socket, and destroys the hidden window that’s used for WinSock messages. It’s implemented as follows: ///////////////////////////////////////////////////////////////////////////// // CStreamSocket::DestroySocket() // // Close the socket, remove any queued data, // and destroy the hidden window. // int CStreamSocket::DestroySocket() { int nStatus = CWINSOCK_NOERROR; // make sure the socket is valid if (m_s == INVALID_SOCKET) nStatus = CWINSOCK_PROGRAMMING_ERROR; else { // remove any data in the write queue while (!m_listWrite.IsEmpty()) { LPSTREAMDATA pStreamData = (LPSTREAMDATA)m_listWrite.RemoveHead(); LPVOID pData = pStreamData–>pData; delete pStreamData; m_pParentWnd–>PostMessage(m_uMsg, CWINSOCK_ERROR_WRITING, (LPARAM)pData); } Chapter 12 ■ CStreamSocket 245 p2v6 Prog. WinSock #30594-1 tullis 11.14.94 CH12 LP #3 // remove any data in the read queue while (!m_listRead.IsEmpty()) { LPSTREAMDATA pStreamData = (LPSTREAMDATA)m_listRead.RemoveHead(); free(pStreamData–>pData); delete pStreamData; } // close the socket and initialize variables closesocket(m_s); InitVars(); // destroy the hidden window DestroyWindow(); } return nStatus; } CStreamSocket::LastError() The LastError() member function is implemented as an in-line function. It simply re- turns the m_nLastError value that contains the last WinSock error message generated by the CStreamSocket object. This function should be called whenever a CStreamSocket member function returns CWINSOCK_WINSOCK_ERROR. Application Responsibility The goal of this object is to enable the rapid development of a networked application using stream sockets. The public interface to the CStreamSocket object consists of the following functions: CreateSocket(), DestroySocket(), Read(), Write(), Connect(), Accept(), GetPeerName(), and LastError(). The application must provide a certain level of support for the stream object. The ap- plication must provide a message handler to receive messages sent from the object. Also, the stream object’s constructor requires a pointer to the application window object and a message. A sample call to a stream object constructor looks like the following: ps = new CStreamSocket(this, WM_USER + 1); An entry must be made in the message map to associate the WM_USER + 1 message to an application member function. BEGIN_MESSAGE_MAP(CMainFrame, CFrameWnd) //{{AFX_MSG_MAP(CMainFrame) //}}AFX_MSG_MAP ON_MESSAGE(WM_USER + 1, OnWinSockEvent) END_MESSAGE_MAP() p2v6 Prog. WinSock #30594-1 tullis 11.14.94 CH12 LP #3 Part III ■ WinSock Class Library 246 The function that handles the WM_USER + 1 message, OnWinSockEvent in this case, must have handlers for six different wParam values. In the following code snippet, m_ps is a member variable of the CMainFrame class that points to a CStreamSocket object. The following code may be used as a template for your stream socket object message handler: LONG CMainFrame::OnWinSockEvent(WPARAM wParam, LPARAM lParam) { LPVOID pDataWritten; LPVOID pDataRead; int nLen; switch (wParam) { case CWINSOCK_DONE_WRITING: // lParam = pointer to data that was sent pDataWritten = (LPVOID)lParam; // the data storage space pointed to by pDataWritten // may now be freed or reused break; case CWINSOCK_ERROR_WRITING: // lParam = pointer to data that generated error sending pDataWritten = (LPVOID)lParam; // the data storage space pointed to by pDataWritten // may now be freed or reused break; case CWINSOCK_DONE_READING: // lParam = # data chunks in queue pDataRead = m_ps–>Read(&nLen); // the data storage space pointed to by pDataRead // may be freed after your processing is complete break; case CWINSOCK_ERROR_READING: break; case CWINSOCK_LOST_CONNECTION: // the other side of the socket closed the connection break; // the following handler is required for a client only case CWINSOCK_YOU_ARE_CONNECTED: break; // the following handler is required for a server only case CWINSOCK_READY_TO_ACCEPT_CONNECTION: // Accept() may now be called break; default: break; } return 0L; } Allocating the stream socket object doesn’t make the socket available for communica- tion. The CreateSocket() member function must be called first. If the socket is to act Chapter 12 ■ CStreamSocket 247 p2v6 Prog. WinSock #30594-1 tullis 11.14.94 CH12 LP #3 as a server, it must be bound to a specific port or service name. To do that, call the func- tion in one of the following ways: int nPort; char pszServiceName[100]; int nStatus; assign port or service name nStatus = m_ps–>CreateSocket(nPort); nStatus = m_ps–>CreateSocket(pszServiceName); If this socket isn’t a server, simply call CreateSocket(), as in: nStatus = m_ps–>CreateSocket(); A client must connect to a server before it can send or receive data. A connection is made by specifying a host specifier. There are five possible ways to call Connect(): char pszHostName[100]; char pszHostIP[100]; int nPort; char pszServiceName[100]; SOCKADDR_IN sin; int nStatus; assign destination specifiers nStatus = m_ps–>Connect(pszHostName, nPort); nStatus = m_ps–>Connect(pszHostIP, nPort); nStatus = m_ps–>Connect(pszHostName, pszServiceName); nStatus = m_ps–>Connect(pszHostIP, pszServiceName); nStatus = m_ps–>Connect(&sin); A server must accept a connection from a client before data transfer can take place. When a connection is accepted, a new CStreamSocket object is used. The Accept() member function is called in response to the CWINSOCK_READY_TO_ACCEPT_CONNECTION message from the server stream socket: CStreamSocket *psClient; // will communicate with the client int nStatus; psClient = new CStreamSocket(this, WM_USER + 2); // m_ps is the server socket nStatus = m_ps–>Accept(psClient); If a server wishes to know the Internet address of the client on the other side of an ac- cepted connection, GetPeerName() is used. This function is called from the socket passed to the Accept() call, as in: SOCKADDR_IN sin; // address of client on other side of socket int nStatus; nStatus = psClient–>GetPeerName(&sin); [...]... development of a networked application using stream communication The next chapter wraps up the class library Chapters 14 through 16 use the CStreamSocket object in fully functional programs p2v6 Prog WinSock #30594-1 tullis 11.14.94 CH 12 LP #3 Chapter 13 s Bringing It All Together 24 9 13 Bringing It All Bringing It All Together Together p2/v6—sn8 Programming WinSock #30594-1 Casey 11.15.94 CH13 LP #3 25 0 Part... m_pszBuf, m_pszServer, 20 00) != CWINSOCK_NOERROR) { (*m_pszBuf) = ‘\0’; continues P2/Vol.6 Programming WinSock #30594-1 tullis 11.14.94 CH14 LP #3 26 7 26 8 Part IV s Programming with the WinSock Class Library Listing 14.4 continued wsprintf(pszMessage, “Error sending data (%s)”, m_pszBuf); plb->InsertString(0, pszMessage); } } CFormView::OnTimer(nIDEvent); } Running the Datagram Echo Server and Client Following... Chapters 9 through 13 p2/v6—sn8 Programming WinSock #30594-1 Casey 11.15.94 CH13 LP #3 IV Programming with the WinSock Class Library 14 Sample Applications Using the Class Library 15 Practical Client/Server Database Application 16 Finger Application in a Heterogeneous UNIX Environment P2/V6/Q7 Programming Winsock 305941 aw 11.15.94 Parts LP #3 Chapter 14 s Sample Applications 25 7 14 Sample Sample Applications... lParam = pointer to data that was sent pDataWritten = (LPVOID)lParam; wsprintf(pszMessage, “Data sent (%s)”, pDataWritten); continues P2/Vol.6 Programming WinSock #30594-1 tullis 11.14.94 CH14 LP #3 26 1 26 2 Part IV s Programming with the WinSock Class Library Listing 14 .2 continued plb->InsertString(0, pszMessage); free(pDataWritten); break; case CWINSOCK_ERROR_WRITING: // lParam = pointer to data that... as in the following sample, results in that shown in Figure 13 .2: if ( WinSock function fails ) CWinSockErrorBox(WSAGetLastError(), “Contact your software distributor for technical support”); FIGURE 13.1 CWinSockErrorBox() p2/v6—sn8 Programming WinSock #30594-1 Casey 11.15.94 CH13 LP #3 25 3 25 4 Part III s WinSock Class Library FIGURE 13 .2 CWinSockErrorBox() with second parameter Implementation Details... that sends either datagrams or stream data, and a server that receives them and sends them back to the client Datagram Echo Client and Server These programs, CDESRV and CDECLNT, demonstrate the use of the CDatagramSocket object The CDESRV server application receives data and sends it back to the client The CDECLNT client application sends data to the server and receives the echoed reply These programs... MAINVIEW.CPP for CDESRV // mainview.cpp : implementation file // #include “stdafx.h” #include “cdesrv.h” #include “mainview.h” continues P2/Vol.6 Programming WinSock #30594-1 tullis 11.14.94 CH14 LP #3 25 9 26 0 Part IV s Programming with the WinSock Class Library Listing 14 .2 continued #ifdef _DEBUG #undef THIS_FILE static char BASED_CODE THIS_FILE[] = FILE ; #endif /////////////////////////////////////////////////////////////////////////////... WSAENETDOWN: lstrcat(pszError, Network is down”); break; case WSAENETUNREACH: lstrcat(pszError, Network is unreachable”); break; case WSAENETRESET: lstrcat(pszError, Network dropped connection on reset”); break; case WSAECONNABORTED: lstrcat(pszError, “Software caused connection abort”); break; case WSAECONNRESET: lstrcat(pszError, “Connection reset by peer”); break; p2/v6—sn8 Programming WinSock #30594-1... 11.15.94 Parts LP #3 Chapter 14 s Sample Applications 25 7 14 Sample Sample Applications Applications P2/Vol.6 Programming WinSock #30594-1 tullis 11.14.94 CH14 LP #3 25 8 Part IV s Programming with the WinSock Class Library This chapter presents the reimplementation of two of the samples described in Chapter 8, “Sample Applications.” This time, the WinSock class library, developed in the preceding chapters,... data is read and immediately echoed back to the client 6 CMainView::OnDatagram() is called in CDESRV with a CWINSOCK_DONE_WRITING notice The data is then freed 7 CMainView::OnDatagram() is called in CDECLNT with a CWINSOCK_DONE_READING notice The echoed data is read and then freed 8 Another timer goes off in CDECLNT and the process repeats Stream Echo Client and Server These programs, CSESRV and CSECLNT, . Applications 25 7 P2/Vol.6 Programming WinSock #30594-1 tullis 11.14.94 CH14 LP #3 14 14 Sample Applications Sample Applications Part IV ■ Programming with the WinSock Class Library 25 8 P2/Vol.6 Programming. 13 ■ Bringing It All Together 24 9 p2/v6—sn8 Programming WinSock #30594-1 Casey 11.15.94 CH13 LP #3 13 13 Bringing It All Together Bringing It All Together p2/v6—sn8 Programming WinSock #30594-1. break; } return 0L; } p2v6 Prog. WinSock #30594-1 tullis 11.14.94 CH 12 LP #3 Part III ■ WinSock Class Library 24 0 CStreamSocket::HandleRead() The HandleRead() member function handles the asynchronous

Ngày đăng: 13/08/2014, 22:21

Từ khóa liên quan

Tài liệu cùng người dùng

Tài liệu liên quan