我有一些关于select函数的问题,我写了这段代码:
void TCPSerwer::sel()
{
struct timeval tv = {1, 0};
fd_set temp_list = m_RecvList;
//if(select(m_fdmax + 1, &temp_list, NULL, NULL, &tv) == SOCKET_ERROR)
if(select(0, &temp_list, NULL, NULL, &tv) == SOCKET_ERROR)
{
perror( "select" );
exit( 1 );
}
for(int i = 0; i <= m_fdmax; i++ )
{
if(FD_ISSET(i, &temp_list))
{
// New connection
if(i == m_ListenSocket)
{
acceptClient();
}
// Data from client
else
{
PacketHeader header;
int nbytes = recv(i, (char*)(&header), sizeof(PacketHeader),
// Error
if(nbytes < 0)
{
disconnectClient(i);
}
// success
else
{
std::cout << "type: " << header.type << " len: " << header.length << std::endl;
}
}
}
}
}
我可以先给arg选择功能,但我不能这样做,但为什么呢?为什么一个应该给第一个arg选择? m_fdmax是套接字的最大数量,但此代码在没有此arg的情况下工作。
接下来的问题是,为什么选择需要超时?当我不给这个arg时,选择标记所有套接字作为可读的套接字,但选择在套接字没有任何数据可读时执行此操作。当我给这个arg我没有这个问题。但为什么呢?
如果m_fdmax是套接字的最大数量,当我关闭连接时,我必须找到下一个最高数量的套接字,对吗?我应该这样做:
int size = m_ClientVector.size();
for(int i = 0; i < size; i++)
{
if(m_ClientVector[i] > m_fdmax)
m_fdmax = m_ClientVector[i];
}
答案 0 :(得分:1)
我可以先给arg选择功能,但我不能这样做,但为什么呢?为什么一个应该给第一个arg选择? m_fdmax是套接字的最大数量,但此代码在没有此arg的情况下工作。
Read the documentation。 Windows上的select()
函数会忽略第一个参数,因此传递给它的内容无关紧要。
接下来的问题是,为什么选择需要超时?
NEED 超时,但如果需要,您可以 OPTIONALLY 提供超时。这样,如果在超时之前未达到请求的套接字状态,select()
仍然可以退出,并且不会无限期地使调用线程死锁,从而允许它执行其他操作。
当我没有给这个arg选择标记时,所有套接字都是可以读取的套接字,但是当套接字无法读取任何数据时选择这样做。
如果未提供超时,select()
将无限期等待,直到请求的套接字状态实际发生。如果套接字有数据要读取,则可以将其标记为可读,但如果另一方已gracefully
断开连接,则也可以将其标记为可读。随后对recv()
的调用将告诉您哪种情况(recv()
错误时返回-1,断开时返回0,数据上返回0> 0)。再次,read the documentation。
如果m_fdmax是套接字的最大数量,我必须在关闭连接时找到下一个最高套接字数,对吗?
如果你想计算最高的套接字号(Windows不关心,但其他平台都这样做),那么每次调用select()
时,你都必须重新计算最高的套接字号,或者至少每当你重新准备fd_set
结构时(每次你调用select()
时都需要这样做)。
我应该这样做
在Windows上,没有。在其他平台上,是的。
话虽如此,请在Windows上尝试使用此代码:
void TCPSerwer::sel()
{
struct timeval tv = {1, 0};
fd_set temp_list = m_RecvList;
int ret = select(0, &temp_list, NULL, NULL, &tv);
if (ret == SOCKET_ERROR)
{
perror( "select" );
exit( 1 );
}
if (ret == 0) // timeout
return;
for(u_int i = 0; i < temp_list.fd_count; ++i)
{
SOCKET s = temp_list.fd_array[i];
// New connection
if (s == m_ListenSocket)
{
acceptClient();
continue;
}
// Data from client
PacketHeader header;
char *pheader = (char*) &header;
int nbytes = 0;
do
{
ret = recv(s, pheader, sizeof(PacketHeader)-nbytes, 0);
// success
if (ret > 0)
nbytes += ret;
}
while ((ret > 0) && (nbytes < sizeof(PacktHeader)));
// Error or disconnect
if (nbytes < sizeof(PacktHeader))
{
disconnectClient(i);
continue;
}
// success
std::cout << "type: " << header.type << " len: " << header.length << std::endl;
}
}
答案 1 :(得分:0)
关于超时:
select
可以使用struct timeval
超时。如果您传递NULL
指针,select
将等到事件发生。如果您将地址传递给struct timeval
,即使没有事件,select
也会返回(在您的代码中,select
将每秒返回一次)。
关于fdmax:是的,您必须找到最高的套接字,并且您的代码段是正确的。
其他:您的代码中没有FD_SET
。通常,套接字在循环中设置(通过FD_SET
so),找到最高的套接字。
编辑:我的错误我没有在您的代码中看到fd_set temp_list = m_RecvList;
。我们需要更多代码才能使用select
分析您的问题。
答案 2 :(得分:0)
感谢您的帮助,我想在Windows和Linux上使用此代码,现在我这样做: 当我有新连接时:
bool TCPSerwer::acceptClient()
{
SOCKET new_client = accept(m_ListenSocket, 0, 0);
if(new_client == INVALID_SOCKET)
return false;
m_ClientVector.push_back(new_client);
// Add to FD
FD_SET(new_client, &m_RecvList);
if(new_client > m_fdmax)
m_fdmax = new_client;
return true;
}
当我想关闭连接时:
void TCPSerwer::disconnectClient(const SOCKET& client)
{
int size = m_ClientVector.size();
for(int i = 0; i < size; i++)
{
if(m_ClientVector[i] == client)
{
closesocket(m_ClientVector[i]);
// Delete from FD
FD_CLR(m_ClientVector[i], &m_RecvList);
m_ClientVector.erase(m_ClientVector.begin() + i);
break;
}
}
// re-calculateing the highest socket number
size = m_ClientVector.size();
for(int i = 0; i < size; i++)
{
if(m_ClientVector[i] > m_fdmax)
m_fdmax = m_ClientVector[i];
}
}
我向你提出一个问题 Remy Lebeau ,你的recv函数看起来:
recv(s, pheader, sizeof(PacketHeader)-nbytes, 0);
但是recv会保存数据吗? Meybe应该看起来:
recv(s, pheader + nbytes, sizeof(PacketHeader)-nbytes, 0);