winSocket 类

来源:互联网 发布:广联达认证网络大赛 编辑:程序博客网 时间:2024/05/28 05:14

18.4 | WinSock类

在Visual C++的MFC类库中,提供了两个与WinSock相关的类,分别为CAsyncSocket类和CSocket类。这两个类对WinSock API进行了封装,使得开发Windows Socket应用程序变得简单了。本节将针对CAsyncSocket和CSocket介绍Windows Socket应用程序的开发。

18.4.1  CAsyncSocket类介绍

CAsyncSocket类对WinSock API进行了低级封装,它提供的许多方法直接对应于低层的API函数。在使用CAsyncSocket时,首先需要调用构造函数创建CAsyncSocket对象,然后调用Create方法创建套接字句柄,对于服务器端的套接字,需要调用Listen方法使其处于监听模式,对于客户端套接字,需要调用Connect方法连接服务器。下面介绍CAsyncSocket的主要方法和事件。

(1)Create方法

Create方法用于创建一个Windows 套接字。

语法:

BOOL Create( UINT nSocketPort = 0, int nSocketType = SOCK_STREAM, long lEvent = FD_READ |

FD_WRITE | FD_OOB | FD_ACCEPT | FD_CONNECT | FD_CLOSE,

LPCTSTR lpszSocketAddress = NULL );

Create方法参数说明如表18.9所示。

表18.9                          Create方法参数说明

参 数 名 称

参 数 描 述

nSocketPort

标识套接字端口

nSocketType

标识套接字类型,默认为流式套接字

lEvent

表示套接字能够接受的事件

lpszSocketAddress

表示套接字的网络地址

(2)GetLastError方法

GetLastError方法用于获得最后一次操作失败的状态信息。

语法:

static int GetLastError( );

(3)GetPeerName方法

GetPeerName方法用于获得套接字连接的IP地址。

语法:

BOOL GetPeerName( CString& rPeerAddress, UINT& rPeerPort );

BOOL GetPeerName( SOCKADDR* lpSockAddr, int* lpSockAddrLen );

GetPeerName方法参数说明如表18.10所示。

表18.10                       GetPeerName方法参数说明

参 数 名 称

参 数 描 述

rPeerAddress

用于接收函数返回的IP地址

rPeerPort

用于记录端口号

lpSockAddr

是一个sockaddr结构指针,用于记录套接字名称

lpSockAddrLen

用于确定lpSockAddr的大小

(4)Accept方法

Accept方法用于接受一个套接字的连接。

语法:

virtual BOOL Accept( CAsyncSocket& rConnectedSocket, SOCKADDR* lpSockAddr = NULL, int*

lpSockAddrLen = NULL );

rConnectedSocket:当前连接的套接字引用。

lpSockAddr:一个SOCKADDR结构指针,用于记录套接字地址。

lpSockAddrLen:确定lpSockAddr的大小。

(5)Bind方法

Bind方法将本机地址关联到套接字上。

语法:

BOOL Bind( UINT nSocketPort, LPCTSTR lpszSocketAddress = NULL );

BOOL Bind ( const SOCKADDR* lpSockAddr, int nSockAddrLen );

Bind方法参数说明如表18.11所示。

表18.11                          Bind方法参数说明

参 数 名 称

参 数 描 述

nSocketPort

标识套接字端口

lpszSocketAddress

标识IP地址

lpSockAddr

是一个SOCKADDR结构指针,该结构记录了套接字的地址信息

nSockAddrLen

确定lpSockAddr的大小

(6)Close方法

Close方法用于关闭套接字。

语法:

virtual void Close( );

说明:

Close方法释放套接字描述符,因此,在调用该方法后,再对其进行访问,会导致错误。当CAsyncSocket对象被释放时,会自动调用Close方法。

(7)Connect方法

Connect方法用于建立一个套接字连接。

语法:

BOOL Connect( LPCTSTR lpszHostAddress, UINT nHostPort );

BOOL Connect( const SOCKADDR* lpSockAddr, int nSockAddrLen );

Connect方法参数说明如表18.12所示。

表18.12                         Connect方法参数说明

参 数 名 称

参 数 描 述

lpszHostAddress

是主机的IP地址或网址

nHostPort

标识当前应用程序的端口

lpSockAddr

是一个SOCKADDR结构指针,该结构标识套接字地址信息

nSockAddrLen

确定lpSockAddr的大小

(8)Listen方法

Listen方法用于监听套接字的连接请求。

语法:

BOOL Listen( int nConnectionBacklog = 5 );

nConnectionBacklog:表示等待连接的最大队列长度。

(9)Receive方法

Receive方法用于从一个套接字上接收数据。

语法:

virtual int Receive( void* lpBuf, int nBufLen, int nFlags = 0 );

lpBuf:是接收数据的缓冲区。

nBufLen:确定缓冲区的长度。

nFlags:确定函数的调用模式,可选值如下。

l     MSG_PEEK用来查看传来的数据,在序列前端的数据会被复制一份到返回缓冲区中,但是这个数据不会从序列中移走。

l     MSG_OOB用来处理Out-Of-Band数据。

(10)ReceiveFrom方法

ReceiveFrom方法用于接收面向无连接传递的数据(数据报)。

语法:

int ReceiveFrom( void* lpBuf, int nBufLen, CString& rSocketAddress, UINT& rSocketPort,

int nFlags = 0 );

int ReceiveFrom( void* lpBuf, int nBufLen, SOCKADDR* lpSockAddr, int* lpSockAddrLen,

int nFlags = 0 );

ReceiveFrom方法参数说明如表18.13所示。

表18.13                       ReceiveFrom方法参数说明

参 数 名 称

参 数 描 述

lpBuf

是接收数据的缓冲区

nBufLen

是缓冲区的大小

rSocketAddress

用于接收数据报的目的地(IP地址)

rSocketPort

用于记录端口号

lpSockAddr

是一个SOCKADDR结构指针,用于记录套接字地址信息

lpSockAddrLen

确定lpSockAddr的大小

nFlags

标识函数调用方式

(11)Send方法

Send方法用于发送数据到连接的套接字上。

语法:

virtual int Send( const void* lpBuf, int nBufLen, int nFlags = 0 );

lpBuf:标识要发送数据的缓冲区。

nBufLen:确定缓冲区的大小。

nFlags:标识函数调用方法。

(12)SendTo方法

SendTo方法既可以在面向连接的套接字上发送数据,也可以在面向无连接的套接字上发送数据。

语法:

int SendTo( const void* lpBuf, int nBufLen, UINT nHostPort, LPCTSTR lpszHostAddress =

NULL, int nFlags = 0 );

int SendTo( const void* lpBuf, int nBufLen, const SOCKADDR* lpSockAddr, int nSockAddrLen,

int nFlags = 0 );

SendTo方法参数说明如表18.14所示。

表18.14                         SendTo方法参数说明

参 数 名 称

参 数 描 述

lpBuf

标识要发送数据的缓冲区

nBufLen

确定缓冲区大小

nHostPort

确定主机端口号

lpszHostAddress

确定主机地址(可以是域名或IP地址)

lpSockAddr

是一个SOCKADDR结构指针,用于确定主机套接字地址信息

nSockAddrLen

确定lpSockAddr的大小

nFlags

标识函数调用方式

(13)ShutDown方法

ShutDown方法用于在套接字上断开数据的发送或接收。

语法:

BOOL ShutDown( int nHow = sends );

nHow:确定ShutDown函数的行为,0表示不允许接收,1表示不允许发送,2表示不允许接收和发送。

(14)OnAccept事件

OnAccept事件在套接字接受连接请求时触发。

语法:

virtual void OnAccept( int nErrorCode );

nErrorCode:标识错误代码。

(15)OnClose事件

OnClose事件在连接的套接字被关闭时触发。

语法:

virtual void OnClose( int nErrorCode );

nErrorCode:标识错误代码。

(16)OnConnect事件

OnConnect事件在套接字被连接后触发。

语法:

virtual void OnConnect( int nErrorCode );

nErrorCode:标识错误代码。

(17)OnOutOfBandData事件

OnOutOfBandData事件在接收数据的套接字收到Out-Of-Band数据时触发。

语法:

virtual void OnOutOfBandData( int nErrorCode );

nErrorCode:标识错误代码。

(18)OnReceive事件

OnReceive事件在套接字有数据被接收时触发。

语法:

virtual void OnReceive( int nErrorCode );

nErrorCode:标识错误代码。

(19)OnSend事件

OnSend事件在套接字发送数据时触发。

语法:

virtual void OnSend( int nErrorCode );

nErrorCode:标识错误代码。

18.4.2  CSocket类介绍

CSocket类派生于CAsyncSocket,对Windows Socket API进行更高层次的封装。它支持同步操作,可以单独使用,但通常情况下与CSocketFile、 CArchive类一起实现数据的发送和接收。下面介绍CSocket的主要方法。

(1)Create方法

Create方法用于创建一个套接字。

语法:

BOOL Create( UINT nSocketPort = 0, int nSocketType = SOCK_STREAM, LPCTSTR

lpszSocketAddress = NULL );

nSockPort:确定套接字端口号。

nSocketType:确定套接字类型。

lpszSocketAddress:确定套接字IP地址。

(2)Attach方法

Attach方法将套接字句柄关联到CSocket对象上。

语法:

BOOL Attach( SOCKET hSocket );

hSocket:标识套接字句柄。

(3)FromHandle方法

FromHandle方法根据套接字句柄获得CSocket对象指针。

语法:

static CSocket* PASCAL FromHandle( SOCKET hSocket );

hSocket:标识套接字句柄。

(4)IsBlocking方法

IsBlocking方法用于判断套接字是否处于阻塞状态。如果返回值为零,表示处于非阻塞状态;非零,表示处于阻塞状态。

语法:

BOOL IsBlocking( );

(5)CancelBlockingCall方法

CancelBlockingCall方法用于取消阻塞模式。

语法:

void CancelBlockingCall( );

说明:

当套接字发送或接收数据时,它将处于阻塞模式,直到其操作完成,才将控制权返回给程序。调用CancelBlockingCall方法将取消套接字的阻塞模式,将控制权立即返回给程序。

18.4.3  使用WinSock类设计网络聊天室

在18.3.3节中介绍了如何使用套接字函数设计网络聊天室,下面将使用WinSock类来设计一个网络聊天程序。程序依然由两个实例组成,即服务器端和客户端。服务器端接受连接并转发信息,客户端负责连接服务器,并发送信息。

服务器端程序设计步骤如下。

*  ch1808实例位置:mr/18/sl/08

(1)创建一个基于对话框的工程,设计对话框资源如图18.16所示。

图18.16  服务器端资源设计窗口

(2)从CSocket类派生一个新类CClientSocket,在头文件中引用对话框的头文件和afxsock.h头文件,并对对话框类进行前导声明。

(3)处理“监听”按钮的单击事件,开始监听客户端。

void CServerDlg::OnOK()

{

    this->UpdateData();

    m_pSocket = new CServerSocket(this);

    if (!m_pSocket->Create(70))

    {

        MessageBox("套接字创建失败");

        delete m_pSocket;

        m_pSocket = NULL;

        return;

    }

    if (!m_pSocket->Listen())

        MessageBox("监听失败");

}

(4)在对话框中添加AcceptConnect方法,用于接受客户端的连接。

void CServerDlg::AcceptConnect()

{

    CClientSocket* socket = new CClientSocket(this);

    //接受客户端的连接

    if (m_pSocket->Accept(*socket))

        m_socketlist.AddTail(socket);

    else

        delete socket;

}

(5)在对话框中添加ReceiveData方法,用于接收客户端传来的数据。

void CServerDlg::ReceiveData(CClientSocket* socket)

{

    char bufferdata[BUFFERSIZE];

    //接收客户端传来的数据

    int result = socket->Receive(bufferdata,BUFFERSIZE);

    bufferdata[result] = 0;

    POSITION pos = m_socketlist.GetHeadPosition();

    //将数据发送给每个客户端

    while (pos!=NULL)

    {

        CClientSocket* socket = (CClientSocket*)m_socketlist.GetNext(pos);

        if (socket != NULL)

            socket->Send(bufferdata,result);

    }

}

客户端程序设计步骤如下。

*  ch1809实例位置:mr/18/sl/09

(1)创建一个基于对话框的工程,设置对话框资源如图18.17所示。

(2)在应用程序的InitInstance方法中初始化套接字。

//初始化套接字

WSADATA wsd;

AfxSocketInit(&wsd);

(3)从CSocket类中派生一个子类CMysocket。在头文件中引用Afxsock.h头文件,目的是使用CSocket类;引用主对话框的头文件,并对主对话框进行前导声明,因为在CMysocket类中需要定义主对话框类指针。

(4)处理“发送”按钮的单击事件,发送数据到服务器。

图18.17  客户端窗口设计

void CClientDlg::OnButtonsend()

{

    // TODO: Add your control notification handler code here

    CString str,temp;

    m_info.GetWindowText(str);

    if (str.IsEmpty()|m_name.IsEmpty())

        return;

    temp.Format("%s说: %s",m_name,str);

    int num = pMysocket->Send(temp.GetBuffer(temp.GetLength()),temp.GetLength());

    m_info.SetWindowText("");

    m_info.SetFocus();

}

(5)在主对话框中定义一个CMysocket对象指针。添加ReceiveData成员方法,用于接收服务器传来的数据。

void CClientDlg::ReceiveData()

{

    char buffer[200];

    //接收传来的数据

    int factdata =  pMysocket->Receive(buffer,200);

   

    buffer[factdata] = '/0';

    CString str;

    str.Format("%s",buffer);

    int i = m_list.GetCount();

    //将数据添加到列表框中

    m_list.InsertString(m_list.GetCount(),str);

}

(6)处理“连接”按钮的单击事件,连接服务器。

void CClientDlg::OnButtonjoin()

{

    // TODO: Add your control notification handler code here

    UpdateData(true);

    CString servername = m_servername; //读取服务器名称

    int port;

    port = 70; //获取端口

   

    if  (! pMysocket->Connect(servername,port)) //连接服务器

    {

        MessageBox("连接服务器失败!");

        return;

    }

    CString str;

    str.Format("%s----->%s",m_name,"进入聊天室");

    int num = pMysocket->Send(str.GetBuffer(0),str.GetLength());

}

运行程序,效果如图18.18、图18.19、图18.20所示。

图18.18  服务器端窗口

    

图18.19  客户端窗口1                             图18.20  客户端窗口2