1. Trang chủ
  2. » Công Nghệ Thông Tin

Networking and Network Programming 2 TCP/IP phần 6 doc

33 270 1

Đ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

Thông tin cơ bản

Định dạng
Số trang 33
Dung lượng 315,65 KB

Các công cụ chuyển đổi và chỉnh sửa cho tài liệu này

Nội dung

a single data buffer is used for // incoming and outgoing data // LONG CMainView::OnAsyncSelectWPARAM wParam, LPARAM lParam { char pszMessage[100]; // informational message static char

Trang 1

/////////////////////////////////////////////////////////////////////////////// CMainView message handlers

/////////////////////////////////////////////////////////////////////////////// CMainView::OnAsyncSelect()

//

// Receives data from a client and echoes the data back to the sending client.// While there is data yet to be sent back to the sending client, the server// will not receive any more data (a single data buffer is used for

// incoming and outgoing data)

//

LONG CMainView::OnAsyncSelect(WPARAM wParam, LPARAM lParam)

{

char pszMessage[100]; // informational message

static char pBuf[101]; // send/recv buffer

int nBytesRecv; // number of bytes received

int nBytesSent; // number of bytes sent

static int nBytesToSend = 0; // number of bytes to send

int nError; // WinSock error

static SOCKADDR_IN addrFrom; // address of client

static int nAddrFromLen = sizeof(addrFrom); // length of client address struct static IN_ADDR inFrom; // IP address of client

// get pointer to list box used for status messages

CListBox *plb = (CListBox *)GetDlgItem(IDC_LIST_STATUS);

// check for an error

// send the data

nBytesSent = sendto(m_s, pBuf, nBytesToSend, 0,

Trang 2

// don’t do anything we’ll get another FD_WRITE soon

nError = WSAGetLastError();

if (nError != WSAEWOULDBLOCK)

{

wsprintf(pszMessage, “Error %d sending data to %s, %d”,

nError, inet_ntoa(inFrom), ntohs(addrFrom.sin_port));

plb->InsertString(0, pszMessage);

nBytesToSend = 0;

// just in case the FD_READ was called but it didn’t read

// because the buffer still contained data to send

wsprintf(pszMessage, “Data sent (%s) to %s, %d”,

pBuf, inet_ntoa(inFrom), ntohs(addrFrom.sin_port));

// if there are still bytes waiting to be sent back (echoed)

// to the client, don’t do anything here (the FD_WRITE handler will

// generate FD_READ when it is through with sending)

if (nBytesToSend == 0)

{

// receive data

nBytesRecv = recvfrom(m_s, pBuf, 100, 0, (LPSOCKADDR)&addrFrom, &nAddrFromLen);

// check for receive error

if (nBytesRecv == SOCKET_ERROR)

{

// if the error is just that the receive would block,

// don’t do anything we’ll get another FD_READ soon

Trang 3

pBuf[nBytesToSend] = ‘\0’;

wsprintf(pszMessage, “Data received (%s) from %s, %d”,

pBuf, inet_ntoa(inFrom), ntohs(addrFrom.sin_port));

plb->InsertString(0, pszMessage);

// just in case the FD_WRITE was called but it didn’t have

// any data to send at that time (it has data to send now)

PostMessage(WM_USER_ASYNC_SELECT, m_s, WSAMAKESELECTREPLY(FD_WRITE, 0)); }

Datagram Echo Client DECLIENT

The datagram echo client, DECLIENT, follows the same basic outline as DESERV It also uses a CFormView object as its main interface The primary difference lies in the implementation of the CMainView object.

The header file for the CMainView object is shown in Listing 8.16 Its implementation is shown in Listing 8.17 This object performs most of the work for the DECLIENT application CMainView::OnInitialUpdate() is called soon after the object is created This function is responsible for creating a socket, waiting to send and receive data, and setting a timer to be used for sending data When data is ready to be received or data can be sent, the CMainView::OnAsyncSelect() member function is called due to the message mapping of the user-defined message WM_USER_ASYNC_SELECT The

CMainView::OnTimer() function is called every five seconds to format a string to send to the echo server.

Listing 8.16 MAINVIEW.H for DECLIENT.

// mainview.h : header file

Trang 4

class CMainView : public CFormView

{

DECLARE_DYNCREATE(CMainView)

public:

SOCKET m_s; // socket

SOCKADDR_IN m_addr; // address to send to

IN_ADDR m_in; // IP address of address to send to

int m_nBytesToSend; // number of bytes to send

char m_pBuf[101]; // buffer to send

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 OnAsyncSelect(WPARAM wParam, LPARAM lParam);

afx_msg void OnTimer(UINT nIDEvent);

//}}AFX_MSG

DECLARE_MESSAGE_MAP()

};

/////////////////////////////////////////////////////////////////////////////

#define WM_USER_ASYNC_SELECT (WM_USER + 1)

Listing 8.17 MAINVIEW.CPP for DECLIENT.

// mainview.cpp : implementation file

Trang 5

void CMainView::OnInitialUpdate()

{

// get pointer to list box used for status messages

CListBox *plb = (CListBox *)GetDlgItem(IDC_LIST_STATUS);

// prompt for server information (IP and port)

Listing 8.17 continued

Trang 6

FD_READ | FD_WRITE) == SOCKET_ERROR)

plb->InsertString(0, “Datagram echo client could not do async select”);

char pszMessage[100]; // informational message

static char pBuf[101]; // send/recv buffer

int nBytesRecv; // number of bytes received

int nBytesSent; // number of bytes sent

int nError; // WinSock error

// get pointer to list box used for status messages

CListBox *plb = (CListBox *)GetDlgItem(IDC_LIST_STATUS);

// check for an error

Trang 7

// is there any data to send?

if (m_nBytesToSend != 0)

{

// send the data

nBytesSent = sendto(m_s, m_pBuf, m_nBytesToSend, 0,

(LPSOCKADDR)&m_addr, sizeof(m_addr));

// check for send error

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

nError = WSAGetLastError();

if (nError != WSAEWOULDBLOCK);

{

wsprintf(pszMessage, “Error %d sending data to %s, %d”,

nError, inet_ntoa(m_in), ntohs(m_addr.sin_port));

wsprintf(pszMessage, “Data sent (%s) to %s, %d”,

m_pBuf, inet_ntoa(m_in), ntohs(m_addr.sin_port));

nBytesRecv = recvfrom(m_s, pBuf, 100, 0, NULL, NULL);

// check for receive error

if (nBytesRecv == SOCKET_ERROR)

{

// if the error is just that the receive would block,

// don’t do anything we’ll get another FD_READ soon

Trang 8

pBuf, inet_ntoa(m_in), ntohs(m_addr.sin_port));

// Timer to generate a string to send Won’t send

// unless the previous string is completely sent

Running the Datagram Echo Server and Client

A sample sequence of events of running the datagram echo server and client is as

fol-lows:

Run DESERV It displays on which port it’s waiting for data to arrive.

Run DECLIENT on the same or a different computer It prompts for the IP

address and port DESERV is using.

In five seconds the timer will trigger in DECLIENT, causing

CMainView::OnTimer() to get called No bytes are waiting to be sent yet, so the

outgoing buffer is filled and a WinSock FD_WRITE message is faked to trigger the

sending of the data This has to be done because the real FD_WRITE may have

been missed or it may have occurred when there was nothing to send yet.

CMainView::OnAsyncSelect() is called in DECLIENT with an FD_WRITE

event There are bytes to be sent, so an attempt to transmit them to the

Trang 9

datagram echo server is made If the attempt succeeds and bytes are written, the number of bytes to send is reset to 0 (zero) If there was an error sending but the error was simply that the send would block, the count of bytes to send is retained because you’ll get a real FD_WRITE eventually.

Assuming that DECLIENT sends a buffer, CMainView::OnAsyncSelect() is called in DESERV with an FD_READ notice If the data is read successfully, a byte count is recorded, the originator of the data is noted, and a fake FD_WRITE

message is generated to force the sending of the just-received data back to the originator (DECLIENT).

CMainView::OnAsyncSelect() is called in DESERV with an FD_WRITE event There are bytes to be sent, so an attempt to transmit them to the datagram echo client is made If the attempt succeeds and bytes are written, the number

of bytes to send is reset to 0 (zero) If there was an error sending but the error was simply that the send would block, the count of bytes to send is retained because you’ll get a real FD_WRITE eventually.

Assuming that DESERV sends a buffer, CMainView::OnAsyncSelect() is called

in DECLIENT with an FD_READ notice and the client reads the echoed data Another timer goes off in DECLIENT and the process repeats.

Figure 8.9 shows DESERV and DECLIENT running on the same computer, which has the IP address 166.78.16.150 The server and client were assigned ports 1059 and

1060, respectively Notice that after each application initialized, it received an FD_WRITE

notification WinSock is simply telling the application that the socket is in a writeable state The applications ignore the message unless they have bytes to send.

Trang 10

Stream Echo Client and Server

These programs, SESERV and SECLIENT, demonstrate the use of nonblocking stream

sockets They use the following WinSock functions: WSAStartup() , WSACleanup() ,

WSAGetLastError() , socket() , closesocket() , bind() , connect() , accept() ,

WSAAsyncSelect() , send() , recv() , getsockname() , ntohs() , and inet_ntoa() The

SESERV server application accepts a connection from a client, receives data, and sends

the data back to the client The SECLIENT client application connects to the server,

sends data to the server, and receives the echoed reply.

Stream Echo Server SESERV

These programs are generated using Visual C++’s AppWizard feature Implementation

is similar to DESERV, with the primary difference being in the CMainView object.

CMainView ’s header is shown in Listing 8.18 Listing 8.19 shows the implementation of

the CMainView object When CMainView::OnInitialUpdate() is called, soon after the

CMainView object is created, it creates a socket, binds it to a name, and waits for a

con-nection request When a concon-nection is requested, data is ready to be received, or data

can be sent, the CMainView::OnAsyncSelect() member function is called due to the

message mapping of the user-defined message WM_USER_ASYNC_SELECT This

stream socket server application communicates with only one client at a time due to the

single m_sClient variable in the CMainView class Once m_sClient is connected, all other

requests to connect are ignored until the connection is closed by the originating client.

Listing 8.18 MAINVIEW.H for SESERV.

// mainview.h : header file

SOCKET m_s; // socket to listen for connections on

SOCKADDR_IN m_addr; // address of socket to listen on

SOCKET m_sClient; // socket to client

continues

Trang 11

enum { IDD = IDD_DIALOG_MAIN };

// NOTE: the ClassWizard will add data members here

#define WM_USER_ASYNC_SELECT (WM_USER + 1)

Listing 8.19 MAINVIEW.CPP for SESERV.

// mainview.cpp : implementation file

IMPLEMENT_DYNCREATE(CMainView, CFormView)

CMainView::CMainView()

Listing 8.18 continued

Trang 12

char pszMessage[100]; // informational message

// get pointer to list box used for status messages

CListBox *plb = (CListBox *)GetDlgItem(IDC_LIST_STATUS);

// create the socket and prepare it

// for receiving data

// bind the socket, allowing WinSock to assign the service port

m_addr.sin_family = AF_INET; // Internet address family

m_addr.sin_port = 0; // let WinSock assign a port

m_addr.sin_addr.s_addr = htonl(INADDR_ANY); // any network interface

continues

Trang 13

if (bind(m_s, (LPSOCKADDR)&m_addr, sizeof(m_addr)) == SOCKET_ERROR) plb->InsertString(0, “Stream echo server could not bind socket”); else

{

// find out the port number WinSock assigned

SOCKADDR_IN addr;

int nAddrLen = sizeof(addr);

if (getsockname(m_s, (LPSOCKADDR)&addr, &nAddrLen) == SOCKET_ERROR) plb->InsertString(0, “Stream echo server could not get socket’s port”); else

{

wsprintf(pszMessage, “Stream echo server using port %d”,

ntohs(addr.sin_port));

plb->InsertString(0, pszMessage);

// do the asynchronous select to wait for a connection

if (WSAAsyncSelect(m_s, m_hWnd, WM_USER_ASYNC_SELECT, FD_ACCEPT) == SOCKET_ERROR)

plb->InsertString(0, “Stream echo server could not do async select”); else

/////////////////////////////////////////////////////////////////////////////// CMainView::OnAsyncSelect()

//

// Receives data from a client and echoes the data back to the sending client.// While there is data yet to be sent back to the sending client, the server// will not receive any more data (A single data buffer is used for

// incoming and outgoing data)

//

LONG CMainView::OnAsyncSelect(WPARAM wParam, LPARAM lParam)

{

char pszMessage[100]; // informational message

static char pBuf[101]; // send/recv buffer

Listing 8.19 continued

Trang 14

int nBytesRecv; // number of bytes received

int nBytesSent; // number of bytes sent

static int nTotalBytesToSend;// total number of bytes to send

static int nBytesToSend = 0; // number of bytes to send

int nError; // WinSock error

static SOCKADDR_IN addrFrom; // address of client

static int nAddrFromLen = sizeof(addrFrom); // length of client address struct

static IN_ADDR inFrom; // IP address of client

// get pointer to list box used for status messages

CListBox *plb = (CListBox *)GetDlgItem(IDC_LIST_STATUS);

// check for an error

Trang 15

// send the data

nBytesSent = send(m_sClient, &pBuf[nTotalBytesToSend - nBytesToSend], nBytesToSend, 0);

// check for send error

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

nError = WSAGetLastError();

if (nError != WSAEWOULDBLOCK)

{

wsprintf(pszMessage, “Error %d sending data to %s, %d”,

nError, inet_ntoa(inFrom), ntohs(addrFrom.sin_port));

wsprintf(pszMessage, “Data sent (%s) to %s, %d”,

pBuf, inet_ntoa(inFrom), ntohs(addrFrom.sin_port));

// there are more bytes to send so we’ll trigger

// the FD_WRITE event again

PostMessage(WM_USER_ASYNC_SELECT, m_s,

WSAMAKESELECTREPLY(FD_WRITE, 0));

}

// just in case the FD_READ was called but it didn’t read

// because the buffer still contained data to send

if (nBytesToSend == 0)

PostMessage(WM_USER_ASYNC_SELECT, m_sClient,

Listing 8.19 continued

Trang 16

// if there are still bytes waiting to be sent back (echoed)

// to the client, don’t do anything here (the FD_WRITE handler will

// generate FD_READ when it is through with sending)

if (nBytesToSend == 0)

{

// receive data

nBytesRecv = recv(m_sClient, pBuf, 100, 0);

// check for receive error

if (nBytesRecv == SOCKET_ERROR)

{

// if the error is just that the receive would block,

// don’t do anything we’ll get another FD_READ soon

wsprintf(pszMessage, “Data received (%s) from %s, %d”,

pBuf, inet_ntoa(inFrom), ntohs(addrFrom.sin_port));

plb->InsertString(0, pszMessage);

// just in case the FD_WRITE was called but it didn’t have

// any data to send at that time (it has data to send now)

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