1

我编写了以下代码以创建一个回显服务器(我写入标准输出的数据从我的 PC 移动到服务器并返回到我的 PC)。在代码中,我在套接字中设置了 SO_LINGER 选项。因此,当我按下Ctrl+C导致 client.cpp 向 server.cpp 发送 FIN 时,client.cpp 的 close() 应该等待 10 秒,以便 server.cpp 的套接字发回 FIN

但是我发现client.cpp在按下Ctrl+后立即完成执行并退出,即使close()函数也没有返回-1,如果对方在l_linger中提到的时间之前没有发送FIN,它应该有expires (我确定服务器没有发送 FIN 或者它必须已在 tcpdump 中列出。)除非我在其终端上按+ ,否则服务器端没有发送 FIN 。屏幕截图中显示了在我按下服务器终端上的+之前的 tcpdump :CCtrlCCtrlC

上面突出显示的行是客户的 FIN。

客户端.cpp:

int main()
{
    int clifd;
    clifd=socket(AF_INET,SOCK_STREAM, IPPROTO_TCP);
    sockaddr_in serv;
    bzero(&serv, sizeof(serv));
    serv.sin_family=AF_INET;
    serv.sin_port=htons(3345);
    inet_aton("127.0.0.1", &(serv.sin_addr));

    linger lin;
    unsigned int y=sizeof(lin);
    lin.l_onoff=1;
    lin.l_linger=10;
    setsockopt(clifd,SOL_SOCKET, SO_LINGER,(void*)(&lin), y);  

    connect(clifd, (sockaddr*)(&serv), sizeof(serv));
    int n,m;
    char data[100];
    char recvd[100];
    for(;;)
    {
        fgets(data, 100,stdin );
        n=strlen(data);
        cout<<"You have written "<<n<<endl;

        if(n>0)
        {
            while(n>0)
            {  
                m=write(clifd,data,n);
                n=n-m;
            }
        }

        n=read(clifd, recvd, 100);
        cout<<"Server echoed back "<<n<<endl; 

        if(n>0)
        {
            while(n>0)
            {
                m=fputs(data,stdout);  
                cout<<"m is"<<m<<endl; 
                fflush(stdout);
                n=n-m;
            }
            //cout<<data<<endl;
        }
    }
    int z=close(clifd);
    if(z==-1)
        cout<<"close returned -1 "<<endl; ***//this doesn't get printed***
}

服务器.cpp:

void reflect(int x)
{
    int n;
    int m;
    char data[100];
    cout<<"Entered reflect function"<<endl; 

    for(;;)
    {
        n=read(x,data, 100); 
        cout<<"Client sent "<<n<<endl; 

        if(n>0)
        {
            while(n>0)
            {
                m=write(x,data,n);
                n=n-m;
            }
            cout<<"Successfully echoed back to client"<<endl;  
        }
    }//end of for loop
}

int main()
{
    sockaddr_in serv;
    bzero(&serv, sizeof(serv));
    serv.sin_family=AF_INET;
    serv.sin_port=htons(3345);
    inet_aton("127.0.0.1", &(serv.sin_addr));

    int servfd=socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
    int x;
    x=bind(servfd, (sockaddr*)(&serv), sizeof(serv));

    cout<<"Bind returned"<<x<<endl; //this displays x as 0

    listen(servfd, 5);
    sockaddr cli;
    int connfd;
    pid_t id=-1;
    socklen_t siz=sizeof(cli);
    for(;;)
    {
        if((connfd=accept(servfd, &cli, &siz))>=0)
            id=fork();

        if(id==0)
            reflect(connfd);
        else 
            continue;
    }
}

为什么客户端的 close() 不等待?

4

4 回答 4

5

为什么客户端的 close() 不等待?

你没有打电话给close()你的客户。for紧接在您调用之前的循环close()永远不会退出,因此close()永远不会发生。

在您的控制台中键入CTRL-C会导致程序立即退出,而不执行任何剩余操作。

由于close()从未被调用,因此您的其余问题(应该close()等待?我应该使用closesocket()吗?等)没有实际意义。

如果您确实希望能够退出for(;;)循环,请尝试以下操作:

for(;;)
{
  if(fgets(data, 100,stdin ) == NULL)
    break;
  ... rest of loop goes here ...

然后,当您运行客户端程序时,不要使用CTRL-终止它C,而是键入CTRL-D作为该行中唯一的字符。

于 2012-07-11T19:36:55.840 回答
3

要扩展上面的答案,如果您想close()调用您的函数,请对您的客户端进行这些相对简单的更改:

#include <signal.h>

volatile bool stop_now = false;

// This function will be called when SIGINT (Ctrl+C)
// is received, and will set this global flag that
// will terminate your loop below.
void app_stopped(int sig)
{
    stop_now = true;
}


int main()
{
   // setup to catch Ctrl+C
   signal(SIGINT, app_stopped);

   ...

   // for(;;)
   // becomes:
   while(!stop_now)
   {
      ...
   }
   ...
}

现在,当您发送 SIGINT(通过Ctrl+C或通过kill -s INT)时,您的程序将正常停止并close()调用您的函数。您可以从那里开始进行故障排除。

于 2012-07-11T19:44:50.000 回答
2

lingerMSDN 上的参考资料(如果您不信任 msdn,您可以在此处找到相同的信息)

linger 结构维护有关特定套接字的信息,该信息指定当数据排队等待发送并且在套接字上调用 closesocket 函数时该套接字应如何表现。

所以它会等到所有数据都发送完毕,而不是等到收到 FIN。

于 2012-07-11T19:38:45.073 回答
1

因此,client.cpp 的 close() 应该等待 10 秒,以便 server.cpp 的套接字发回一个 FIN。

这是不正确的。它应该等待所有传出数据和传出 FIN 发送完毕,或等待计时器到期。任何地方都没有声明可以让您期望它等待传入的 FIN。

于 2012-07-11T23:58:09.010 回答