Multithreading 在select()调用中被阻止时建立新连接

Multithreading 在select()调用中被阻止时建立新连接,multithreading,sockets,select,networking,tcp,Multithreading,Sockets,Select,Networking,Tcp,我有两个线程: 线程A: 这是select()循环。为读取操作执行套接字处理,例如接受新连接、接收数据 while (1) { FD_ZERO(&fdReadSet); numActiveSockets = 0; for (std::unordered_map<SOCKET, TcpSocket*>::iterator it = m_sock_table.begin(); it != m_sock_table.end(); it++) {

我有两个线程:

线程A: 这是select()循环。为读取操作执行套接字处理,例如接受新连接、接收数据

while (1) {

    FD_ZERO(&fdReadSet);

    numActiveSockets = 0;

    for (std::unordered_map<SOCKET, TcpSocket*>::iterator it = m_sock_table.begin(); it != m_sock_table.end(); it++)
    {
        numActiveSockets++;
        FD_SET(it->first, &fdReadSet);
    }

    int ret;
    bool hasListen = false;

    if (( ret = select(numActiveSockets, &fdReadSet, NULL, NULL, NULL)) == SOCKET_ERROR) {
        printf("Select Failed, Error code = %d\n", WSAGetLastError());
        return -1;
    }

    for (std::unordered_map<SOCKET, TcpSocket*>::iterator it = m_sock_table.begin(); it != m_sock_table.end(); it++)
    {
        if (FD_ISSET(it->first, &fdReadSet))
        {
            if (it->first == TcpSocket::m_listen_sock)
            {
                if (!hasListen)
                {
                    sockaddr_in sock_addr;
                    int sockLength = sizeof(sock_addr);

                    SOCKET sock = accept(it->first, (sockaddr *) &sock_addr, &sockLength);
                    TcpSocket * socket = new TcpSocket();
                    socket->m_sock = sock;
                    m_sock_table[sock] = socket;

                    it = m_sock_table.begin();
                    hasListen = true;
                }
            }
            else
            {
                char * buffer = it->second->GetWriteBuffer();
                int numRead = recv(it->first, buffer, SOCKET_BUFFER_SIZE, 0);

                if (numRead == SOCKET_ERROR)
                {
                    int err = WSAGetLastError();
                    if (err == WSAECONNRESET)
                    {
                        printf("Connection [%i]: RESET Received. Closing Socket\n", it->first);
                        closesocket(it->first);
                        it = socketVector.erase(it->first);  // iterator invalidated after erase
                    }
                    else
                    {
                        printf("Recv Failed. Error code = %d\n", err);
                        return -1;
                    }
                }
                else if (numRead == 0)//connection close
                { 
                    printf("Connection [%i]: Graceful exit. Closing Socket\n", it->first);
                    closesocket(it->first);
                    it = socketVector.erase(it->first);  // iterator invalidated after erase

                }
                else {
                    /* Process received data */

                }

            }
        }
    }
}
while(1){
FD_ZERO(&fdReadSet);
numActiveSockets=0;
for(std::无序_映射::迭代器it=m_sock_table.begin();it!=m_sock_table.end();it++)
{
numActiveSockets++;
FD_集(it->first和fdReadSet);
}
int ret;
bool hasleen=false;
if((ret=select(numativesockets,&fdReadSet,NULL,NULL,NULL))==SOCKET\u错误){
printf(“选择失败,错误代码=%d\n”,WSAGetLastError());
返回-1;
}
for(std::无序_映射::迭代器it=m_sock_table.begin();it!=m_sock_table.end();it++)
{
if(FD_ISSET(it->first和fdReadSet))
{
if(it->first==TcpSocket::m\u listen\u sock)
{
如果(!hasleen)
{
sockaddr_中的sockaddr_;
int sockLength=sizeof(sock_addr);
SOCKET sock=accept(它->first,(sockaddr*)和sock\u addr,&sockLength);
TcpSocket*套接字=新的TcpSocket();
socket->m_sock=sock;
m_sock_table[sock]=插座;
it=m_sock_table.begin();
hasListen=true;
}
}
其他的
{
char*buffer=it->second->GetWriteBuffer();
int numRead=recv(it->first,buffer,SOCKET\u buffer\u SIZE,0);
if(numRead==SOCKET\u错误)
{
int err=WSAGetLastError();
if(err==WSAECONNRESET)
{
printf(“连接[%i]:已收到重置。正在关闭套接字\n”,它->第一次);
闭合插座(它->第一个);
it=socketVector.erase(it->first);//迭代器在擦除后无效
}
其他的
{
printf(“Recv失败。错误代码=%d\n”,错误);
返回-1;
}
}
else if(numRead==0)//连接关闭
{ 
printf(“连接[%i]:优雅退出。关闭套接字\n”,它->第一个);
闭合插座(它->第一个);
it=socketVector.erase(it->first);//迭代器在擦除后无效
}
否则{
/*处理接收到的数据*/
}
}
}
}
}
线程B: 允许应用程序执行connect()以建立新连接。如果connect()成功,它将把返回的套接字添加到m\u sock\u表中

我有一个名为m_sock_table的套接字表,其中包含所有套接字。我使用此m_sock_表初始化要在select()中使用的fdReadSet

------------问题-----------------

如果线程A被select()阻塞,同时线程B通过connect()建立新连接,应用程序将无法从新连接接收数据,因为fdReadset未使用新连接的套接字更新


解决这个问题的好办法是什么?或者设计从一开始就是错误的?

您可以使用除了中断系统调用之外没有任何作用的信号:

#include <signal.h>
void do_nothing() { }

struct sigaction sa;
sa.sa_handler = do_nothing;
sigemptyset(sa.sa_mask);
#ifdef SA_INTERRUPT
sa.sa_flags = SA_INTERRUPT;
#else
sa.sa_flags = 0;
#endif
sigaction(SIGUSR1, &sa, 0);

在上述情况下,select将返回一个EINTR错误——因此检查该错误并循环(将新连接添加到集合中)。

什么事件导致添加新连接?如果选择带超时的(),则可以向表中添加新连接。然后将超时设置为创建连接和提供连接之间可接受的延迟。谢谢。我最终使用了你的方法。如果您愿意发布一个答案,我很高兴将其标记为已接受。不幸的是,信号在POSIX之外是不可移植的,否则这是一个很好的方法,唯一的缺点是它唤醒的线程比需要的多(我认为)。如果只需要将其另外移植到win32系统,您可以使用WSAEventSelect()实现类似的功能。@UlrichEckhardt:在win32上,您根本不应该使用select,因为它不可靠。您应该改为使用
WaitForMultipleObjects
,其中一个对象是一个消息队列,告诉您向要等待的对象集中添加更多对象…在win32上select()在什么意义上不可靠?很抱歉,我应该澄清我在Windows系统中工作。我想在windows中没有办法像这样中断select()调用吗?
/* need only be done once, but needed in every thread other than A */
sigset_t sigs;
sigemptyset(&sigs);
sigaddset(&sigs, SIGUSR1)
pthread_sigmask(SIG_BLOCK, &sigs, 0);

/* each time we create a new connection */
kill(getpid, SIGUSR1);