2012-07-11 55 views
1

我編寫了下面的代碼,以便使echo服務器(我寫入stdout的數據從我的PC移動到服務器並返回到我的PC)。在代碼中,我在套接字中設置了SO_LINGER選項。所以,當我按下Ctrl鍵+ç其中造成client.cpp一個FIN發送到server.cpp的client.cpp的close()應該等待10秒的server.cpp的套接字發回FINSO_LINGER選項不會導致close()等待

但我發現的是,client.cpp立即執行完畢和後退出按Ctrl +ç按下甚至的close()funcion沒有返回-1它應該有如果對方在l_linger提到的時間到期之前沒有發送FIN(我確信服務器沒有發送FIN或者它必須已經在tcpdump中列出)。服務器端沒有發送FIN除非我按Ctrl + C在其終端人。直到我按下Ctrl鍵+服務器的終端基於C tcpdump的顯示在屏幕截圖:

突出顯示的上述線是客戶端的FIN。

client.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*** 
} 

server.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()不等待?

+1

爲什麼你認爲'Ctrl + C'不會終止你的應用程序(因此不會讓它有機會看到套接字被關閉)?您不會在您的客戶端應用程序中捕獲到SIGINT,並且您看到的「FIN」可能是由於套接字被應用程序「強行關閉」而「關閉」。 – Chad 2012-07-11 19:35:42

回答

1

因此,client.cpp的close()應該等待server.cpp套接字發回FIN。

這是不正確的。它應該等待所有的輸出數據和輸出的FIN被髮送,或者定時器到期。沒有任何聲明可以讓你期待它等待傳入的FIN。

5

爲什麼客戶端的close()沒有在等待?

您不打電話給您的客戶close()。緊接在您的close()呼叫之前的for循環從不退出,因此close()永遠不會出現。

鍵入CTRL - 在控制檯ç導致程序立即退出,而不執行任何餘下的行動。

由於close()永遠不會被調用,您的其餘問題(應該close()等待?我應該使用closesocket()?等)是沒有意義的。

如果你希望能夠退出for(;;)循環,嘗試這樣的事情:

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

然後,當你運行你的客戶端程序,不終止它CTRL - Ç ,而是鍵入CTRL - D作爲唯一的字符就行了。

+1

您可以使用'signal.h'和一些狀態標誌來顯示OP如何使這個關閉呼叫發生。 – Chad 2012-07-11 19:41:44

2

從MSDN上的linger引用(如果你不信任的MSDN,你可以找到相同的信息here

靈兒結構保持有關規定,插座應如何表現時,特定的信息插座數據排隊等待發送,並在套接字上調用closesocket函數。

所以它會一直等到所有的數據被髮送,直到收到一個FIN。

3

,就答案拓展上面,如果你想獲得所謂的close()功能,使這些相對簡單的客戶端的變化:

​​

現在,當你發送一個SIGINT(通過Ctrl+C或通過kill -s INT )你的程序會優雅地停下來,你的函數將被調用。您可以從那裏開始排除故障。

+0

+1,但您可能希望將「bool stop_now」設置爲「volatile bool stop_now」? – mpontillo 2012-07-11 20:00:40

+0

優秀點。 – Chad 2012-07-11 20:21:26