【问题标题】:First time using select(), maybe a basic question?第一次使用 select(),也许是一个基本问题?
【发布时间】:2010-06-10 15:49:59
【问题描述】:

我已经使用 select() 在此服务器上工作了几天。它的作用是,我有两个客户端数组(一个是“供应商”,另一个是“消费者”),服务器的任务是检查供应商是否有东西要发送给消费者,并且如果是肯定的,发送它。

服务器的第二部分是,当消费者收到供应商的信息时,他们会向发送信息的同一供应商发送确认消息。

当客户端连接时,它会被识别为“未定义”,直到它发送一条带有单词“供应商”或“消费者”(西班牙语,因为我来自那里)的消息,当服务器将它放入正确的客户端数组。

好吧,服务器做什么在这里并不是很重要。重要的是,我用两个不同的“for”循环来完成这两个部分,这就是我遇到问题的地方。当第一个用户连接到服务器时(无论是供应商还是消费者),服务器都会卡在第一个或第二个循环中,而不是继续执行。因为这是我第一次使用 select(),所以我可能会遗漏一些东西。你们能帮我什么忙吗?

非常感谢。

for(;;)
{
    rset=allset;
    nready=select(maxfd+1,&rset,NULL,NULL,NULL);

    if (FD_ISSET(sockfd, &rset))
    {
        clilen=sizeof(cliente);
        if((connfd=accept(sockfd,(struct sockaddr *)&cliente,&clilen))<0)
        {
            printf("Error");
        }

        IP=inet_ntoa(cliente.sin_addr);
        for(i=0;i<COLA;i++)
        {
            if(indef[i]<0)
            {
                indef[i]=connfd;
                IPind[i]=IP;
                break;
            }
        }

        FD_SET(connfd,&allset);     
        if(connfd > maxfd)
        {
            maxfd=connfd;
        }
        if(i>maxii)
        {
            maxii=i;
        }
        if(--nready<=0)
        {    continue; }
    }// Fin ISSET(sockfd)

    for(i=0;i<=maxii;i++)
    {
        if((sockfd1=indef[i])<0)
        { continue; } //!

        if(FD_ISSET(sockfd1,&rset))
        {
            if((n=read(sockfd1,comp,MAXLINE))==0)
            {
                close(sockfd1);
                FD_CLR(sockfd1,&allset);
                indef[i]=-1;
                printf("Cliente indefinido desconectado \n");
            }
            else
            {
                comp[n]='\0';
                if(strcmp(comp,"suministrador")==0)
                {
                    for(j=0;j<=limite;j++)
                    {
                        if(sumi[j]<0)
                        {
                            IPsum[j]=IPind[i];
                            sumi[j]=indef[i];
                            indef[i]=-1;
                            if(j>maxis)
                            {
                                maxis=j;
                            }
                            break;
                        }
                    }
                }
                else if(strcmp(comp,"consumidor")==0)
                {
                    for(o=0;j<=limite;j++)
                    {
                        if(consum[o]<0)
                        {
                            IPcons[o]=IPind[i];
                            consum[o]=indef[i];
                            indef[o]=-1;
                            if(o>maxic)
                            {
                                maxic=o;
                            }
                            break;
                        }
                    }
                }

                if(--nready <=0)
                {
                    break;
                }
            }
        }
    }//fin bucle for maxii
    for(i=0;i<=maxis;i++)
    {
        if((sockfd2=sumi[i])<0)
        {    continue; }

        if(FD_ISSET(sockfd2,&rset))
        {
            if((n=read(sockfd2,buffer2,MAXLINE))==0)
            {
                close(sockfd2);
                FD_CLR(sockfd2,&allset);
                sumi[i]=-1;
                printf("Suministrador desconectado \n");
            }
            else
            {
                buffer2[n]='\0';
                for(j=0;j<=maxic;j++)
                {
                    if((sockfd3=consum[j])<0)
                    {    continue; }
                    else    
                    {
                        strcpy(final,IPsum[i]);
                        strcat(final,":");
                        strcat(final,buffer2);
                        write(sockfd3,final,sizeof(final));
                        respuesta[i]=1;
                    }
                }
                break; // ?
            }
        }
    }//fin for maxis

    for(i=miniic;i<=maxic;i++)
    {
        if((sockfd4=consum[i])<0)
        {    continue; }

        if(FD_ISSET(sockfd4,&rset))
        {
            if((n=read(sockfd4,buffer3,MAXLINE))==0)
            {
                close(sockfd4);
                FD_CLR(sockfd4,&allset);
                consum[i]=-1;
                printf("Consumidor desconectado \n");
            }
            else
            {
                buffer3[n]='\0';
                IP2=strtok(buffer3,":");
                obj=strtok(NULL,":");
                for(j=0;j<100;j++)
                {
                    if((strcmp(IPsum[j],IP2)==0) && (respuesta[j]==1))
                    {
                        write(sumi[j],obj,sizeof(obj));
                        miniic=i+1;
                        respuesta[j]=0;
                        break;                           
                    }
                }
            }
        }
    }

【问题讨论】:

  • 你的代码格式很糟糕。很多额外的空行。有时 { 括号与控制语句在同一行,有时则不在。有时你根本没有括号。改进编程的第一种方法是应用一致的格式。
  • 我已根据更普遍接受的布局重新格式化您的代码。

标签: c sockets select client-server


【解决方案1】:

嗯,我认为你的逻辑完全错误。它应该看起来更像这样(警告,未经测试的伪代码):

for (;;)
{
   // First, set up the fd_sets to specify the sockets we want to be notified about
   fd_set readSet;  FD_CLR(&readSet);
   fd_set writeSet; FD_CLR(&writeSet);
   int maxFD = -1;
   for (int i=0; i<num_consumers; i++)
   {
      if (consumer_sockets[i] > maxFD) maxFD = consumer_sockets[i];
      FD_SET(consumer_sockets[i], &readSet);
      if (consumer_has_data_he_wants_to_send[i]) FD_SET(consumer_sockets[i], &writeSet);
   }
   for (int i=0; i<num_producers; i++)
   {
      if (producer_sockets[i] > maxFD) maxFD = producer_sockets[i];
      FD_SET(producer_sockets[i], &readSet);
      if (producer_has_data_he_wants_to_send[i]) FD_SET(producer_sockets[i], &writeSet);
   }

   // Now we block in select() until something is ready to be handled on a socket
   int selResult = select(maxFD+1, &readSet, &writeSet, NULL, NULL);
   if (selResult < 0) {perror("select"); exit(10);}

   for (int i=0; i<num_consumers; i++)
   {
      if (FD_ISSET(consumer_sockets[i], &readSet)
      {
         // There is some incoming data ready to be read from consumer_socket[i], so recv() it now
         [...]
      }
      if (FD_ISSET(consumer_sockets[i], &writeSet)
      {
         // There is buffer space in consumer_socket[i] to hold more outgoing
         // data for consumer_socket[i], so send() it now
         [...]
      }
   }
   for (int i=0; i<num_producers; i++)
   {
      if (FD_ISSET(&producer_sockets[i], &readSet)
      {
         // There is some data ready to be read from producer_socket[i], so recv() it now
         [...]
      }
      if (FD_ISSET(producer_sockets[i], &writeSet)
      {
         // There is buffer space in producer_socket[i] to hold more outgoing
         // data for producer_socket[i], so send() it now
         [...]
      }
   }
}

请注意,要真正正确地执行此操作,您需要将所有套接字设置为非阻塞 I/O 并能够处理部分读取和写入(通过将部分数据存储到相关的本地内存缓冲区中)使用该消费者/生产者,直到您有足够的数据可以采取行动),否则您可能会调用 recv() 或 send() 块,这将阻止事件循环能够为任何其他消费者或生产者提供服务.理想情况下,您应该阻塞的唯一地方是 select()... 其他所有调用都应该是非阻塞的。但是,如果您想让事情一开始就简单,您可以暂时使用阻塞 I/O。

【讨论】:

  • 非常感谢您的回答。看到你几天的工作不得不被扔进垃圾箱,这有点令人沮丧,但知道真相是件好事。很遗憾,因为在执行服务器时,我似乎真的接近完成它。如果我使用 read()、write() 和阻塞套接字,你的小例子会发生很大变化吗?这是大学作业,我们从未使用过非阻塞套接字或非阻塞 I/O。这对我来说是全新的,而且截止日期也快到了。
  • 无论如何,在 Linux/Unix 上,recv()/read() 和 write()/send() 是等价的(至少当它们用于网络套接字时)。至于使用阻塞套接字/ IO,您可以尝试一下,但这很棘手,因为如果您尝试在没有准备读取数据的套接字上读取()/接收(),那么读取()/接收()在更多字节从网络到达之前不会返回......这可能很长时间,或者永远不会,同时,所有其他套接字 I/O 都被阻塞。 (如果远程客户端拒绝读取其传入数据,则 send()/write() 阻塞也存在类似问题,但问题不大)。
【解决方案2】:

您可以阅读介绍性tutorial 查看阻塞与非阻塞连接

【讨论】:

  • 我很抱歉格式不好。我尝试做的第一件事是使用 send() 和 recv() 而不是 read() 和 write(),但它仍然不能正常工作。无论如何感谢您的回复。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2010-11-05
  • 1970-01-01
  • 2016-12-14
  • 1970-01-01
  • 1970-01-01
  • 2014-09-24
相关资源
最近更新 更多