一、概念
1.同步方式与异步方式
同步方式:发送方不等接收方响应,便接着发送下一个数据包的通信方式
异步方式:发送方发出数据,等收到接收方发回的响应后,才发送下一个数据包的通信方式
2.阻塞与非阻塞方式
阻塞套接字:执行此套接字的网络调用时,直到成功才返回,否则一直阻塞在此网络调用上。例如,调用Receive函数读取网络缓冲区中的数据,如果没有数据到达,将一直挂在Receive函数调用上,直到读到一些数据,此函数调用才返回。
非阻塞套接字:执行此套接字的网络调用时,不管是否执行成功,都立即返回。例如,调用Receive函数读取网络缓冲区中的数据,不管是否读到数据都立即返回,而不会一直挂在此函数上。
在实际Windows网络通信软件开发中,异步非阻塞套接字是用得最多的。(C/S)就是异步非阻塞模式。CAsyncSocket就是一个异步类,它封装了异步非阻塞Socket的基本功能,而CAsyncSocke的派生类CSocket则提供了阻塞的工作方式。
二、Socket事件
在实际应用中,程序员一般不直接使用CAsyncSocket类,而是自己定义它的派生类,主要原因就是需要捕获Socket激活的事件,如Socket连接建立,数据接收完毕等。在CAsyncSocket类中,为响应各种事件,定义了一系列可重载的函数,包括OnAccept,OnClose,OnConnect,OnReceive,OnSend
事件 响应函数 事件描述
FD_ACCEPT OnAccept 通知侦听套接字,对方程序的连接请求正在等待被接受
FD_CLOSE OnClose 连接的另一端应用程序已经关闭它的Socket或者连接已丢失,收到此通知的Socket应该关闭
FD_CONNECT OnConnect 通知连接套接字,对方的连接已经完成,可以通过Socket发送或接收数据
FD_READ OnReceive 表示Socket连接的数据已经接收到,可调用Receive函数接收
FD_WRITE OnSend 表示通过Socket已经准备好发送数据,连接完成即可调用此函数
三、Socket事件的激发的控制
默认情况下,CAsyncSocket类会调用所有的可重载函数,而CSocket一个也不调用。在实际应用中,往往需要控制Socket对特定事件函数的调用,可采用如下两种方式实现对这些事件函数激发的控制。
1.通过CAsyncSocket类的Create函数控制
此方法只适合于CAsyncSocket类及其派生类。在CAsyncSocket类的Create函数中,其第三个参数lEvent为一个标志值,可以指明需要激发哪个事件,其可能取值及要激发的事件如上所示。
BOOL CASyncSocket::Create(UINT nSocketPort=0, int nSocketType = SOCK_STREAM,
long lEvent = FD_ READ|FD_ WRITE|FD_OOB|FD ACCEPT|FD_CLOSE|FD_CONNECT,
LPCTSTR 1pszSOCketAddress=NULL);
2.通过CAsyncSelect函数控制
该方法适用于CAsyncSocket和CSocket的派生类。用组合标记定义激发哪些事件。
调用AsyncSelect函数的典型代码可表示如下:
if (m_ sock .AsyncSelect(FD_ READ|FD_ CONNECT)==SOCKET_ ERROR);
{
//错误处理
}else
//成功后的代码
默认情况下,AsyncSelect函数激发所有的事件函数。如果想要关闭所有事件的激发,可以采用如下代码:
m_sock.AsyncSelect(0)
四、Socket信息的获取与处理
有时,应用程序需要知道Socket的状态信息,如对方程序的网络地址和端口、Socket是否处于阻塞状态等。
1.获取Socket的地址
当一个Socket与另一个程序的Socket连接后,通过调用GetPeerName函数,获取连接的程
序的网络地址和端口。GetPeerName函数的原型如下:
BOOL GetPeerName(CString& rPeerAddress,UINT& rPeerPort);
两个参数分别用于记录连接方的网络地址和端口。
典型的GetPeerName函数的调用方法如下:
CString sAddress;
UINT iPort;
if(SOCKET_ERROR == m_socket.GetPeerName(&sAddress,&iPort))
{
//错误错误处理代码
}else
{
//成功获取代码
}
如果采用无连接通信,可以使用GetSocketName函数获取本方Socket的网络地址和端口。其使用方法与GetPeerName函数相同。
2. Socket闭塞处理
使用CSocket类时,所有Socket通信函数自动闭塞所有线程处理,直到它完成为止。正如有连接通信实例中见到的,如果调用了Socket上的Connect函数,此函数直到连接完成和Socket超时后才能返回线程的控制。Accept, Receive和Send函数也是如此。如果需要在函数返回前中断它们,该如何处理呢?
相比CAsyncSocket类,CSocket类提供了属性函数IsBlocking,用来确定一个阻塞调用是否在进行;CancelBlockingCall函数,用来取消一个当前在进行中的阻塞调用;重载函数OnMessagePending,当等待完成一个阻塞调用时,调用此函数来处理悬而未决的消息。下面对这3个函数进行详细介绍。
(1).BOOL CSocket::IsBlocking():如果该套接字是阻塞的,则返回非零值;否则返回O.此成员函数用来确定一个阻塞的调用是否正在进行中。
(2)BOOL CSocket::OnMessagePending( ):如果消息被处理了,则返回非零值;否则返回O.重载这个函数来查找来自Windows的特别消息,并在套接字中对它们作出响应。这是一个高级的可重载函数。当套接字在转发Windows消息时,框架调用OnMessagePending来处理应用程序感兴趣的消息。
(3)void CSocket::CancelBlockingCall( ):此成员函数用来取消一个当前在进行中的阻塞调用。这个函数取消该套接字的任何未完的阻塞操作。
除了Accept,取消任何操作都会使该套接字处于一种不确定的状态。
MFC,TCP_Srv网络步骤
1.CAsyncSocket m_sockSend //声明一个发送套接字
CSocket m_SockListen //声明一个监听套接字
2.m_SockListen.Create(m_SrvPort,SOCK_STREAM,0);
3.m_SockListen.bind(m_SrvPort,m_strSrvAddr);
4.if(m_SockListen.Listen())
5.m_SockListen.Accept(m_sockSend) //当有连接进入时,才返回
6.int iSend = m_sockSend.Send(szSend,10,0);
MFC,TCP_Client步骤
1.CSocket m_SockReceive
2.if(m_SockReceive.Create())
3.m_SockReceive.Connect(m_strSvrIP,m_nSvrPort);
4.int iRecv = m_SockReceive.Receive(szRecv,sizeof(szRecv),0);
MFC,UDP_Srv步骤
1.CAsyncSocket m_socket
2.m_socket.Create.Create(m_SrvPort,SOCK_DGRAM,0);
3.m_socket.bind(m_SrvPort,m_strSrvAddr);
4.int iSend = m_socket.SendTo(szSend,sizeof(szSend),m_ClientPort,m_strClientIP,0);
MFC,UDP_Client步骤
1.CAsyncSocket m_socket
2.m_socket.Create(m_ClientPort,SOCK_DGRAM,0);
3.m_socket.bind(m_ClientPort,m_strClientAddr);
4.int iRecv = m_socket.RecvFrom(szRecv,sizeof(szRecv),m_SrvIP,m_strSvrPort,0);