是不是一直在寻找SOCKET这样的功能,所有的,对我是说所有的,都是在限定的时间范围返回结果。SOCKET I/O模型中SELECT模型提供了这样的可能,当然一些超时还要经过一些技巧才行。这是我重构的自己完全动手的第2个可重用类。由于加了#define TIME_INFINITE 0xFFFFFFFF //阻塞模式哈,我第一个包装了的类CBTSocket类可以丢到水桶里去,我可怜的BT~我会记得你的,没有你怎么有我的ST了。
首先是套接字的CREATE哈,CREATE就不用超时了吧根本没这个必要了,因为他马上就返回的
然后LISTEN看来也没必要,也是直接返的,BIND看来也没必要了。接下来有用武之地了。首先是CONNECT超时,这个VCKBASE的精华区有篇文章,取其精华COPY过来实现之哈哈变成了以下方式
int CSTSocket::Connect(const char* szIP, unsigned short nPort, int nSec, int nUSec)
{
if(m_sSocket == NULL)
return RET_ERRSOCKET;
if( strlen(szIP) == 0 || nPort == 0 )
return RET_BADPARAM;
m_addr.sin_addr.s_addr = inet_addr(szIP);
if(INADDR_NONE == m_addr.sin_addr.s_addr)
{
return RET_BADPARAM;
}
m_addr.sin_family = AF_INET;
m_addr.sin_port = htons( nPort );
if(nSec == TIME_INFINITE)
{
if(connect(m_sSocket, (SOCKADDR*)&m_addr, sizeof(m_addr)) == SOCKET_ERROR )
{
return WSAGetLastError();
}
}
else
{
//设置为非阻塞方式连接
unsigned long ul = 1;
int nRet = ioctlsocket(m_sSocket, FIONBIO, (unsigned long*)&ul);
if(nRet == SOCKET_ERROR)
{
return WSAGetLastError();
}
FD_SET fd = {1, m_sSocket};
TIMEVAL tv = {nSec, nUSec};
connect(m_sSocket,(SOCKADDR*)&m_addr, sizeof(m_addr));
nRet = select(0, 0, &fd, 0, &tv);
if(nRet == SOCKET_ERROR)
{
return WSAGetLastError();
}
if(nRet == 0) {
return RET_TIMEOUT;
}
//设回阻塞模式
ul = 0 ;
nRet = ioctlsocket(m_sSocket, FIONBIO, (unsigned long*)&ul);
if(nRet == SOCKET_ERROR)
{
return WSAGetLastError();
}
}
return RET_SUCCESS;
}
接下来是ACCEPT,这个好象多此一举,不过自然能实现我就实现吧,闲着也是闲着
int CSTSocket::Accept(CSTSocket &sock, int nSec, int nUSec)
{
if(m_sSocket == NULL)
return RET_ERRSOCKET;
SOCKET s;
sockaddr_in addr;
int nAddrSize = sizeof(addr);
FD_SET fd = {1, m_sSocket};
TIMEVAL tv = {nSec, nUSec};
int nRet;
//阻塞模式
if(nSec != TIME_INFINITE)
{
nRet = select(0, &fd, NULL, NULL, &tv);
if(nRet == SOCKET_ERROR)
{
return WSAGetLastError();
}
if(nRet == 0)
{
return RET_TIMEOUT;
}
}
if((s = accept(m_sSocket, (SOCKADDR*)&addr, &nAddrSize)) == INVALID_SOCKET )
{
return WSAGetLastError();
}
sock.SetContent(s, addr);
return RET_SUCCESS;
}
开始在网上找时候并没有找到相关的,但看到SDK
readfds:If listening, a connection is pending, accept will succeed
于是我如上事项,也成功了。
接下来是发送,因为有个缓冲区可以轮刷~
int CSTSocket::Send(const char *szData, int nLen, int nSec, int nUSec)
{
if(m_sSocket == NULL)
return RET_ERRSOCKET;
if( szData == NULL || nLen == 0 )
return RET_BADPARAM;
int nRet = 0;
int nSent = 0;
int nLeft = 0;
int nIdx = 0;
nLeft = nLen;
if(nSec == TIME_INFINITE)
{
while(nLeft > 0)
{
nSent = send(m_sSocket, szData + nIdx, nLeft, 0);
if(nSent == SOCKET_ERROR)
{
return WSAGetLastError();
}
if(nSent == 0)
{
return RET_SOCKCLOSED;
}
nLeft -= nSent;
nIdx += nSent;
}
}
else
{
FD_SET fd = {1, m_sSocket};
TIMEVAL tv = {nSec, nUSec};
while(nLeft > 0)
{
nRet = select(0, NULL, &fd, NULL, &tv);
if (SOCKET_ERROR == nRet)
{
return WSAGetLastError();
}
if(nRet == 0)
{
return RET_TIMEOUT;
}
nSent = send(m_sSocket, szData + nIdx, nLeft, 0);
if(nSent == SOCKET_ERROR)
{
return WSAGetLastError();
}
if(nSent == 0) {
return RET_SOCKCLOSED;
}
nLeft -= nSent;
nIdx += nSent;
}
}
return RET_SUCCESS;
}
接下来接受,一次接受,
int CSTSocket::Recv(char *szData, int nLen, int *pnRecvLen, int nSec, int nUSec)
{
if(m_sSocket == NULL)
return RET_ERRSOCKET;
if( szData == NULL )
return RET_BADPARAM;
//假如不是阻塞等待的话
if(nSec != TIME_INFINITE)
{
int nRet;
FD_SET fd = {1, m_sSocket};
TIMEVAL tv = {nSec, nUSec};
nRet = select(0, &fd, NULL, NULL, &tv);
if (SOCKET_ERROR == nRet)
{
return WSAGetLastError();
}
if(nRet == 0)
{
return RET_TIMEOUT;
}
}
*pnRecvLen = recv(m_sSocket, szData, nLen, 0);
if (*pnRecvLen == SOCKET_ERROR)
{
return WSAGetLastError();
}
if (0 == *pnRecvLen)
{
return RET_SOCKCLOSED;
}
return RET_SUCCESS;
}
最后加了个函数,接受固定子节数的,如果接受不到,最后会超时。
www.lwkl.net/SELECT模型SOCKET.rar
经过测试写了个简单的服务器和客户机
谢谢大家捧场希望大家喜欢,有错误请告知。
写完这个类,测试成功后,我又找到了久违的快感~哈~~~~~~~~~~~`