请问select的意义、以及非阻塞式socket

canghaixiaoao2 2008-08-05 10:53:05
在SOCKET编程中,总是看到有select的影子:
如:
//非阻塞式SOCKET+select
while(1)
{
FD_ZERO(&fdread);
FD_SET(m_ServerSocket, &fdread);
ret = select(0, &fdread, NULL, NULL, &timeout);
if(SOCKET_ERROR == ret)
{
closesocket(m_ServerSocket);
break;
}
if(ret > 0 )
{
if(FD_ISSET(m_ServerSocket, &fdread))
{
recvlen = recv(m_ServerSocket, recvbuf, sizeof(recvbuf), 0);
if(SOCKET_ERROR == recvlen)
{
closesocket(m_ServerSocket);
break;
}
else
{
//recv成功
}
}
}
}

为什么要用非阻塞式SOCKET+select来实现呢?用阻塞式的recv不一样就可以实现了吗?反正都是在等。
//阻塞式SOCKET
recvlen = recv(m_ServerSocket, recvbuf, sizeof(recvbuf), 0);
if(SOCKET_ERROR == recvlen)
{
closesocket(m_ServerSocket);
break;
}
else
{
//recv成功
}
请教这两者的区别?
...全文
531 7 打赏 收藏 转发到动态 举报
写回复
用AI写文章
7 条回复
切换为时间正序
请发表友善的回复…
发表回复
yuwen123441 2008-08-05
  • 打赏
  • 举报
回复
select 安全!功能多!
gavin1203 2008-08-05
  • 打赏
  • 举报
回复
采用阻塞时,当有N个客户端时,就要开N个线程,对资源是极大的浪费!
采用模型可以在一个线程里边处理多个网络事件,极大的节省了资源!
select效率比较低下,并不是经常采用的,高性能的服务器程序,一般采用完成端口模型!

如果你要自己写程序的话,可以考虑BindIoCompletionPort线程池,用起来比较方便...
sun007700 2008-08-05
  • 打赏
  • 举报
回复
“为什么要用非阻塞式SOCKET+select来实现呢?用阻塞式的recv不一样就可以实现了吗?反正都是在等。 ”
1.阻塞式的容易造成线程堵塞。
2.程序退出时,阻塞式容易当机。
3.没有select安全,因为,select可以检查m_ServerSocket的状态,是否可读,可写,异常,出错等。timeout可以设置时间。具体可看msdn。
Tolirry 2008-08-05
  • 打赏
  • 举报
回复
BOOL CContextSocketServer::InitFDSet(void)
{
m_fdsWrite.fd_count = 0;
m_fdsRead.fd_count = 0;
m_fdsExcept.fd_count = 0;

FD_SET(m_hServer, &m_fdsRead);
FD_SET(m_hServer, &m_fdsExcept);

CContextClient* pContext = m_pContextHead;
while (pContext != NULL)
{
FD_SET(pContext->m_hClient, &m_fdsRead);
FD_SET(pContext->m_hClient, &m_fdsWrite);
FD_SET(pContext->m_hClient, &m_fdsExcept);

pContext = pContext->m_pContextNext;
}

return TRUE;
}
Tolirry 2008-08-05
  • 打赏
  • 举报
回复
BOOL CContextSocketServer::OnStartProcess(void)
{
WSADATA wsaD;
if (::WSAStartup(MAKEWORD(2, 2), &wsaD) != 0)
return FALSE;

m_hServer = ::socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
if (m_hServer == INVALID_SOCKET)
return FALSE;

sockaddr_in addrName;
memset(&addrName, NULL, sizeof(addrName));
addrName.sin_family = AF_INET;
addrName.sin_addr.S_un.S_addr = INADDR_ANY;
addrName.sin_port = ::htons(m_wPort);
sockaddr* pAddrName = reinterpret_cast<sockaddr*>(&addrName);
if (::bind(m_hServer, pAddrName, sizeof(addrName)) == SOCKET_ERROR)
{
::closesocket(m_hServer);
m_hServer = INVALID_SOCKET;
return FALSE;
}

if (::listen(m_hServer, SOMAXCONN) == SOCKET_ERROR)
{
::closesocket(m_hServer);
m_hServer = INVALID_SOCKET;
return FALSE;
}

return TRUE;
}

BOOL CContextSocketServer::OnThreadProcess(void)
{
HANDLE hEvents[] =
{
GetStopEvent(),
m_hMsgEvent,
};
DWORD dwEvents = sizeof(hEvents)/sizeof(HANDLE);

::WaitForMultipleObjects(dwEvents, hEvents, FALSE, 100);

InitFDSet();

timeval tv = {0, 500};
timeval* pTV = NULL;
if (m_pContextHead == NULL)
pTV = &tv;

int nRet = ::select(NULL, &m_fdsRead, &m_fdsWrite, &m_fdsExcept, pTV);
if (nRet == SOCKET_ERROR || nRet == 0)
return FALSE;

// Client connect, accept it.
if (FD_ISSET(m_hServer, &m_fdsRead))
{
CContextClient* pContext = new CContextClient;
sockaddr* pAddr = reinterpret_cast<sockaddr*>(&pContext->m_addrClient);
int nAddrLen = sizeof(sockaddr);
pContext->m_hClient = ::accept(m_hServer, pAddr, &nAddrLen);
pContext->m_hMsgEvent = m_hMsgEvent;

DWORD dwNonBlock = 1;
nRet = ::ioctlsocket(pContext->m_hClient, FIONBIO, &dwNonBlock);

if (pContext->m_hClient != INVALID_SOCKET
&& nRet != SOCKET_ERROR)
{
AddNewContext(pContext);
OnAccept(pContext);
}
else
{
delete pContext;
}
}

// Error occur
if (FD_ISSET(m_hServer, &m_fdsExcept))
{
OnServerExcept();
}

CContextClient* pContext = m_pContextHead;
while (pContext != NULL)
{
SOCKET hClient = pContext->m_hClient;

// Can read
BOOL bDeleteContext = FALSE;
while (FD_ISSET(hClient, &m_fdsRead))
{
DWORD dwRecvBufSize = NULL;
char* pBuf = reinterpret_cast<char*>(&dwRecvBufSize);
nRet = ::recv(hClient, pBuf, sizeof(DWORD), NULL);
pBuf = NULL;

if (nRet == NULL || nRet == SOCKET_ERROR)
{
bDeleteContext = TRUE;
break;
}

if (nRet == sizeof(DWORD))
{
pBuf = new char[dwRecvBufSize];
}

if (nRet == sizeof(DWORD)
&& pBuf != NULL)
{
nRet = ::recv(hClient, pBuf, dwRecvBufSize, NULL);
if (nRet == NULL || nRet == SOCKET_ERROR)
{
bDeleteContext = TRUE;
break;
}

DWORD dwRecvSize = nRet;
while (nRet != dwRecvBufSize)
{
nRet = ::recv(hClient, pBuf+nRet, dwRecvBufSize-nRet, NULL);
dwRecvSize += nRet;

if (nRet == NULL || nRet == SOCKET_ERROR)
{
bDeleteContext = TRUE;
break;
}
}

if (nRet == dwRecvBufSize)
{
OnReceive(pContext, pBuf, dwRecvBufSize);
}

if (pBuf != NULL)
{
delete pBuf;
pBuf = NULL;
}

if (nRet == NULL || nRet == SOCKET_ERROR)
{
bDeleteContext = TRUE;
break;
}
}

m_fdsRead.fd_count = 0;
FD_SET(hClient, &m_fdsRead);
::select(NULL, &m_fdsRead, &m_fdsWrite, &m_fdsExcept, NULL);
}

// Disconnect
if (bDeleteContext)
{
OnDisconnect(pContext);

CContextClient* pContextDel = pContext;
pContext = pContext->m_pContextNext;
DeleteContext(pContextDel);
continue;
}

// Can write
if (FD_ISSET(hClient, &m_fdsWrite))
{
CTransMsg* pTransMsg = NULL;
while (pContext->GetTransMsgHead(&pTransMsg, FALSE))
{
DWORD dwMsgBufSize = pTransMsg->GetBufferSize();
DWORD dwSendBufSize = dwMsgBufSize + sizeof(DWORD);
BYTE* pBtBuf = new BYTE[dwSendBufSize];
if (pBtBuf == NULL)
break;

char* pChBuf = reinterpret_cast<char*>(pBtBuf);
*reinterpret_cast<DWORD*>(pBtBuf) = dwMsgBufSize;

pTransMsg->SerializeTo(pBtBuf+sizeof(DWORD), dwMsgBufSize);

nRet = 0;
DWORD dwSendSize = NULL;
while (nRet!=SOCKET_ERROR && dwSendSize!=dwSendBufSize)
{
char* pSend = pChBuf+dwSendSize;
DWORD dwSend = dwSendBufSize-dwSendSize;
nRet = ::send(hClient, pSend, dwSend, NULL);
dwSendSize += nRet;
}

if (pBtBuf != NULL)
{
delete pBtBuf;
pBtBuf = NULL;
}

if (nRet == dwSendBufSize)
{
pContext->GetTransMsgHead(&pTransMsg, TRUE);
OnSend(pContext, pTransMsg);
}

if (nRet == SOCKET_ERROR)
{
break;
}
}
}

// Except
if (FD_ISSET(hClient, &m_fdsExcept))
{
OnClientExcept(pContext);

CContextClient* pContextDel = pContext;
pContext = pContext->m_pContextNext;
DeleteContext(pContextDel);
continue;
}

pContext = pContext->m_pContextNext;
}

return TRUE;
}

BOOL CContextSocketServer::OnStopProcess(void)
{
while (m_pContextHead != NULL)
{
DeleteContext(m_pContextHead);
}

if (m_hServer != INVALID_SOCKET)
{
::closesocket(m_hServer);
m_hServer = INVALID_SOCKET;
}

::WSACleanup();

return TRUE;
}
DarknessTM 2008-08-05
  • 打赏
  • 举报
回复
使用select 一般是大量连接的场合
这种时候无限制的开 线程 是不合理的,更影响整体性能,线程的切换开销太大。
danscort2000 2008-08-05
  • 打赏
  • 举报
回复
选择非阻塞,是因为效率高,但是难控制,使用阻塞,编写容易,但是容易导致线程死等待,或者开很多线程来实现,这对服务器是个问题
至于select的使用
是因为对多个SOCKET同时检测效率高,注意,是同时检测多个,
并不是什么安全问题
单独对一个SOCKET进行检测,开销和RECV函数基本是相同的[非阻塞]

18,356

社区成员

发帖
与我相关
我的任务
社区描述
VC/MFC 网络编程
c++c语言开发语言 技术论坛(原bbs)
社区管理员
  • 网络编程
加入社区
  • 近7日
  • 近30日
  • 至今
社区公告
暂无公告

试试用AI创作助手写篇文章吧