【问题标题】:Listen() and select ()听()和选择()
【发布时间】:2017-10-06 10:49:24
【问题描述】:

我实际上遇到了客户端-客户端应用程序的问题。在这个问题中,一切都与 Unix 网络编程环境有关。

这是我的情况:

  • 我有一个客户端(从现在起称为C1)在套接字上调用listen()
  • C1 将与先前的listen() 调用关联的listenfd 套接字放入适当的fd_set 变量中,并在其上调用select()
  • 一旦它收到来自另一个客户端(从现在起称为C2)的新传入连接,即select() procs,则使用accept() 成功创建连接,并且客户端C1-C2 开始通信。
  • 让我们调用 accept() 返回的 int accfdconnect()returned int connfd
  • 完成后,C1-C2 都使用 close(connfd),close (accfd) 关闭相关套接字。

完成后,两个客户端都有机会是否再次发送/接收数据。如果C1 选择重新启动其发送/接收例程,则fd_set 被归零(使用FD_ZERO() 宏)并且listenfd 再次放入与先前调用的select() 关联的fd_set 变量中。问题是,如果C2 尝试与C1 建立新连接,则第二个connect() 不会在C1 中生成select() proc,即使connect() 调用由C1 进行成功。如果第三个客户端 (C3) 尝试将 connect() 发送到 C1,则不会发生这种情况。

我想了解的是如何关闭与客户端的连接并在不同时间打开与同一客户端的新连接。 请注意,我不希望客户端在完成发送/接收例程后保留第一次创建的连接。我想与两个客户端创建一个新连接。

这是客户端代码,注意我省略了代码中明显或无用的部分:

int nwrite,nread,servsock,listenfd,clsock[10],mastfd,maxfd,s=0,t=0,i=0,j=0;
int act,count=0;
for (i=0;i<10;i++)
    clsock[i]=socket(PF_INET,SOCK_STREAM,0); //clsock is an array of sockets. Each time C2 tries to connect to a generic C1 client, it uses the clsock[count] int. count is incremented everytime a connection is closed.
for (i=0;i<10;i++)
    if(setsockopt(clsock[i],SOL_SOCKET,SO_REUSEADDR,(char *)&opt2,sizeof(opt2))<0)
    {
        perror("setsockopt");
        exit(-1);
    }
listenfd=socket(PF_INET,SOCK_STREAM,0); //this is the listening socket 
if(setsockopt(listenfd,SOL_SOCKET,SO_REUSEADDR,(char *)&opt,sizeof(opt))<0)
{
    perror("setsockopt");
    exit(-1);
}
if (listenfd<0)
{
    perror("Listenfd: ");
    exit(-1);
}

if (bind(listenfd,(struct sockaddr*)&cl2addr,sizeof(cl2addr))<0)
{
    perror("Binding: ");
    exit(-1);
 }
if (listen(listenfd,100)<0)
{
    perror("Listening: ");
    exit(-1);
}
do
{
    do
    {
        FD_ZERO(&readfd);
        FD_SET(STDIN_FILENO,&readfd);
        FD_SET(listenfd,&readfd); //the listenfd socket is added
        FD_SET(servsock,&readfd);
[... Maxfd and the elaps structure are set....] 
        act=select(maxfd,&readfd,NULL,NULL,&elaps); //maxfd is calculated in another piece of code. I'm sure it is right.
        system("reset");
            if (FD_ISSET(listenfd,&readfd)) //when the listen procs, the loop ends for C1.
            {
            [...exits from the first  do-while loop...]
        }
        if (FD_ISSET(STDIN_FILENO,&readfd)) //this is where c2 exits from the loop
        {
          s=1;
          do some things here.
        }
    [.....some useless code here .....]
    }
    while (s!=1); //this is the condition used by c1/c2 to exit the loop
if (t==1) //this is what C1 runs, having t=1.
    {
        if ((mastfd=accept(listenfd,(struct sockaddr *) &cl2addr,&cllen))<0) //C1 accepts a generic connection
        {
            perror("Accept: ");
            exit(-1);
        }
[....do some things...]
        if (close(mastfd)<0) //Once done, it closes the actually connected socket
            {
                perror("Error closing mastfd");
                _exit(-1);

            }
    }
else //this is what C2 runs
    {   
        claddr.sin_addr.s_addr=inet_addr(ipbuff); // ipbuff is C1 port
        claddr.sin_port=htons(sprt); //sprt is C1 port
        if (connect(clsock[count],(struct sockaddr *) &claddr,sizeof(claddr))<0) //create a connection between C1 and C2
        {
            perror("Connect: ");
            printf("ERROR: %s",strerror(errno));
            exit(-1);   
        }
[....do some things...]
        if (close(clsock[count])<0)
        {
            perror("Error closing socket!");
            _exit(-1);
        }
count++; //increment count to be able to create a new connection and not to re-use the same socket in the clsock[count] array.
    }
if (menu==1)
    {           
        memset(&claddr,0,sizeof(claddr)); //this was when my brain was about to pop off
        fflush(stdin);
        fflush(stdout); 
        t=0;
        s=0;
        num_usr=0;
        system("reset");
        FD_ZERO(&readfd); //this is when my brain totally popped off
        FD_CLR(listenfd,&readfd);
        FD_CLR(servsock,&readfd);
        FD_CLR(STDIN_FILENO,&readfd);
        FD_SET(listenfd,&readfd);
        FD_SET(servsock,&readfd);
        FD_SET(STDIN_FILENO,&readfd);
    }
} while (menu==1)

谢谢大家,如果问题提出或写得不好,请告诉我。对于我的经验不足和我的英语,我很抱歉,我才刚刚开始使用网络编程。非常感谢您的帮助。

【问题讨论】:

  • 最有可能看到代码的正确部分将有助于理解问题,甚至是minimal reproducible example
  • 我总是犯同样的错误,我尝试以正确的方式格式化代码,但最终失败了。我试图描述我的问题,我最终失败了。我尝试在其中放入一些代码,但最终失败了。实际上,我对我的生活和我在这里所做的事情有很好的想法。开个玩笑,我会提供一些代码。谢谢。
  • 警惕using fflush(stdin)——如果你在Unix上,那基本上是错误的。嵌套的do … while 循环令人担忧。单个函数中的巨大代码块非常令人担忧。很难看出哪里发生了什么——这使得很难弄清楚可能出了什么问题。 system("reset") 在你的机器上做了什么?它将终端切换到 macOS 上的已知状态。你需要减少你的代码,使它更简单、更可编译——MCVE 已经请求了。
  • 我在尽我所能,我发誓。我将尽可能缩小代码。对不起。
  • 一开始为什么要从 fdset 中删除 listenfd?把它留在那里。您应该始终对新联系感兴趣,而不仅仅是在旧联系消失时。

标签: c sockets unix network-programming


【解决方案1】:

在为select() 准备readfd 时,我没有看到任何代码计算新的maxfd 值。调用select()时,maxfd必须是所有提供的fd_sets中最高描述符的值+1。没有显示每次重置readfd 时计算maxfd 的代码。你需要做这样的事情:

FD_ZERO(&readfd);
FD_SET(STDIN_FILENO, &readfd);
maxfd = STDIN_FILENO;
FD_SET(listenfd, &readfd);
maxfd = max(listenfd, maxfd);
FD_SET(servsock, &readfd);
maxfd = max(servsock, maxfd);

act = select(maxfd+1, &readfd, NULL, NULL, &elaps);

另外请记住,在某些系统上,select() 会修改timeval 结构以报告select() 退出后剩余的时间量,因此您应该在每次调用elaps 时重置elaps

【讨论】:

  • 感谢您的回答。正如我的代码中的 cmets 所述,maxfd 是在 do-while 循环的每次迭代中计算的。 timeval 结构也在每次迭代开始时重置。 (忘记写了。)
【解决方案2】:

我解决了删除代码末尾的行的问题 memset(&amp;claddr,0,sizeof(claddr)); 尽管它解决了我的问题,但我真的不知道为什么这不能使代码正常工作。如果有人能解释一下,那就太好了。

【讨论】:

    猜你喜欢
    • 2013-04-01
    • 1970-01-01
    • 1970-01-01
    • 2012-03-16
    • 2014-06-06
    • 1970-01-01
    • 2015-12-24
    • 2012-04-13
    • 2010-09-08
    相关资源
    最近更新 更多