CAsyncSocket类是VC++的MFC里一个对WinSock API封装得很低级的一个类。适合于既想利用WinSock API的灵活性,又想享受MFC里的消息事件机制带来的方便情况下使用,但是CAsyncSocket不支持IPX/SPX协议。笔者最近在开发一个基于IPX/SPX协议传送SPX包的程序时候,重载了CAsyncSocket类的Create,Bind,Connect,Socket,Accept这五个成员函数,成功地使CAsyncSocket类支持了IPX/SPX协议。下面谈谈具体实现。
我们先看看AsyncSocket::Create()这个函数的定义
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);
第一个参数是套接字的端口,第二个参数是套接字的类型,默认为流式套接字。第三个参数是针对套接字产生的事件,第四个参数是套接字的地址,默认为NULL。下面是这个函数的实现:
BOOL CAsyncSocket::Create(UINT nSocketPort, int nSocketType,
long lEvent, LPCTSTR lpszSocketAddress)
{
if (Socket(nSocketType, lEvent))
{
if (Bind(nSocketPort,lpszSocketAddress))
return TRUE;
int nResult = GetLastError();
Close();
WSASetLastError(nResult);
}
return FALSE;
}
请仔细看上面的代码,其实Create函数就调用了两个函数,一个是Socket,一个是Bind.从上面这段代码里,我们还看不出是CAsyncSocket类对TCP/IP和IPX/SPX协议的处理不同之处。
那么我们就接下来看看Socket函数
BOOL Socket(int nSocketType=SOCK_STREAM, long lEvent =
FD_READ | FD_WRITE | FD_OOB | FD_ACCEPT | FD_CONNECT | FD_CLOSE,
int nProtocolType = 0, int nAddressFormat = PF_INET);
接下来是Socket函数的实现
BOOL CAsyncSocket::Socket(int nSocketType, long lEvent,
int nProtocolType, int nAddressFormat)
{
ASSERT(m_hSocket == INVALID_SOCKET);
m_hSocket = socket(nAddressFormat,nSocketType,nProtocolType);
if (m_hSocket != INVALID_SOCKET)
{
CAsyncSocket::AttachHandle(m_hSocket, this, FALSE);
return AsyncSelect(lEvent);
}
return FALSE;
}
看清楚了,里面有一句
m_hSocket=socket(nAddressFormat,nSocketType,nProtocolType);
我想熟悉WinSock API编程的朋友一定会知道,其实这就是WinSock API里创建一个新的套接字。第一个参数nAddressFormat就是套接字使用的协议族,在winsock2.h里我们可以找到
#define PF_INET AF_INET
#define PF_IPX AF_IPX
这两个定义,PF_INET就是指我们新创建的套接字使用TCP/IP协议,PF_IPX就是使用IPX/SPX协议。
而第二个参数nSocketType默认的就是流式套接字,第三个参数nProtocolType是指定使用的协议类型。
在winsock2.h里有这样的定义
#define IPPROTO_IP 0
默认的参数0就是IP包。
在使用默认参数调用Create函数情况下,即
CAsyncSocket::Create();
到了CAsyncSocket::Socket函数
m_hSocket=socket(nAddressFormat,nSocketType,nProtocolType);
这一句这里就变成了
m_hSocket=socket(AF_INET,SOCK_STREAM,IPROTO_IP);
我想现在大家都已经很清楚了CAsyncSocket类之所以不支持IPX/SPX协议问题就在这一句上面,因为他默认的创建一个基于TCP/IP的流式或者数据包套接字。我们要让CAsyncSocket类支持IPX/SPX,首先就要修改这里。
而在WinSock API里,创建一个基于IPX/SPX协议传送SPX包的流式套接字应该是这样
SOCKET sdServer;
SOCKADDR_IPX IPXAddr;
int addrlen=sizeof(SOCKADDR_IPX);
sdServer=socket(AF_IPX,SOCK_STREAM,NSPROTO_SPX);
ZeroMemory(&IPXAddr,sizeof(SOCKADDR_IPX));
IPXAddr.safamily=AF_IPX;
IPXAddr.sa_socket=hotns(9000);
bind(sdServer,(PSOCKADDR)&IPXAddr,sizeof(SOCKADDR_IPX);
IPX应用程序通过bind把本地地址与套接字绑定在一起,绑定端口是9000。我们不需要在SOCKADDR_IPX里指定网络号和节点地址,bind函数会自动利用系统上第一个可用IPX网络接口来填充这些SOCKADDR_IPX结构里的相应字段。
所以我们在自己的类里重载的Socket函数应该是这样
BOOL Socket(int flg,int nSocketType=SOCK_STREAM, long lEvent =
FD_READ | FD_WRITE | FD_OOB | FD_ACCEPT | FD_CONNECT | FD_CLOSE,
int nProtocolType = NSPROTO_SPX, int n