WSAEventSelect模型是个非阻塞模型。在学习这个模型的时候发现示例代码并未为每一个连接建立FD_WRITE事件监视,只是监视了RD_READ/FD_CLOSE事件。而是直接这么使用了send操作:
/*
* 发送数据
*/
BOOL CClientSocket::Send(const u_short type, const CString &strData)
{
ASSERT(!strData.IsEmpty());
int nErrCode; //返回值
PACKETHDR packetHdr; //定义包头 packetHdr.type = type; //类型
packetHdr.len = strData.GetLength();//数据长度 //发送包头
nErrCode = send(m_s, (char*)&packetHdr, PACKETHDRLEN, 0);
if (SOCKET_ERROR == nErrCode)
{
AfxMessageBox("发送用户列表错误!");
return FALSE;
} //发送包体
nErrCode = send(m_s, strData, packetHdr.len, 0);
if (SOCKET_ERROR == nErrCode)
{
AfxMessageBox("发送用户列表错误!");
return FALSE;
} return TRUE;
}这里我有些不解,这能够保证send()函数操作一次性完成么?如果万一发生WSAEWOULDBLOCK错误怎么办?这个模型本来就是非阻塞异步模型。这里的用法是不是有些问题?应该如何使用呢?
/*
* 发送数据
*/
BOOL CClientSocket::Send(const u_short type, const CString &strData)
{
ASSERT(!strData.IsEmpty());
int nErrCode; //返回值
PACKETHDR packetHdr; //定义包头 packetHdr.type = type; //类型
packetHdr.len = strData.GetLength();//数据长度 //发送包头
nErrCode = send(m_s, (char*)&packetHdr, PACKETHDRLEN, 0);
if (SOCKET_ERROR == nErrCode)
{
AfxMessageBox("发送用户列表错误!");
return FALSE;
} //发送包体
nErrCode = send(m_s, strData, packetHdr.len, 0);
if (SOCKET_ERROR == nErrCode)
{
AfxMessageBox("发送用户列表错误!");
return FALSE;
} return TRUE;
}这里我有些不解,这能够保证send()函数操作一次性完成么?如果万一发生WSAEWOULDBLOCK错误怎么办?这个模型本来就是非阻塞异步模型。这里的用法是不是有些问题?应该如何使用呢?
函数开始为了避免多次接收recv()函数触发的FD_READ事件,清除了监测事件列表,但是并没有再所有的return分支,重新注册事件FD_READ/FD_CLOSE。比如下面的两个分支,虽然是异常分支,但是程序并没有终止运行,事件有可能二次触发,那么这里取消过之后没有重新监视,也就说异常发生后永远不可能二次捕获事件了,监视列表已经为空。if ( 0 == reVal)
{
return FALSE;
}
else if (SOCKET_ERROR == reVal)//网络错误
{
int nErrCode = WSAGetLastError();
if (WSAEWOULDBLOCK == nErrCode)
{
return TRUE;
}
AfxMessageBox("接收用户列表错误!");
return FALSE;
}/*
* 接收数据
*/
BOOL CClientSocket::Recv( void )
{
int reVal; //返回值 //首先取消网络事件
WSAEventSelect(m_s, m_hEvent, 0); //获取数据包体的长度
PACKETHDR packetHdr;
reVal = recv(m_s, (char*)&packetHdr, PACKETHDRLEN, 0);
if ( 0 == reVal)
{
return FALSE;
}
else if (SOCKET_ERROR == reVal)//网络错误
{
int nErrCode = WSAGetLastError();
if (WSAEWOULDBLOCK == nErrCode)
{
return TRUE;
}
AfxMessageBox("接收用户列表错误!");
return FALSE;
}
else
{ // 处理接收到的数据
CString strUserInfo; //用户信息
int nTotalLen = 0; //已经读取字符数量
int nDataLen = packetHdr.len; //数据长度
int nReadLen = 0; //每次读取字符数量
while ( nTotalLen != nDataLen )
{
char cRecv; //接收字符
nReadLen = recv(m_s, &cRecv, 1,0); //每次接收一个字符
if (SOCKET_ERROR == nReadLen) //网络错误
{
if (WSAEWOULDBLOCK == WSAGetLastError())
{
continue;
}
AfxMessageBox(_T("读取客户端数据失败!"));
reVal = FALSE;
}else if (0 == nReadLen)
{
AfxMessageBox(_T("客户端关闭了连接!"));
reVal = FALSE;
}else if (nReadLen > 0)
{
if ('<' == cRecv) //开始字符
{
strUserInfo.Empty();
}else if ('>' == cRecv) //结束字符
{
break;
}else
{
strUserInfo += cRecv; //添加字符
}
nTotalLen += nReadLen;
}
} //重新注册网络事件
WSAEventSelect(m_s, m_hEvent, FD_READ|FD_CLOSE); //更新用户列表
return (m_pServDlg->UpdateUserList(strUserInfo, this));
}
return TRUE;
}
FD_WRITE,或者send()遇到WSAEWOULDBLOCK后,系统buffer又可用send()可再次发送时候会触发FD_WRITE事件。但是,不可忽视的是send()函数操作不能确认一定成功,send()是可能导致FD_WRITE事件发生的。
1。使用connect或者WSAConnect一个套接字首次建立了连接
2。使用accept或者WSAAccept套接字被接受以后
3。若send,WSASend,sento或者WSASendTo操作失败,返回了WSAEWOULDBLOCK错误,而且缓冲区的空间变得可用时