2012-10-24 183 views
1

我的代碼是:收盤插座

int main(int argc, char *argv[]) 
{ 
    int sockfd, new_fd; /* listen on sock_fd, new connection on new_fd */ 
    struct sockaddr_in my_addr; /* my address information */ 
    struct sockaddr_in their_addr; /* connector's address information */ 
    socklen_t sin_size; 

    /* generate the socket */ 
    if ((sockfd = socket(AF_INET, SOCK_STREAM, 0)) == -1) { 
     perror("socket"); 
     exit(1); 
    } 

    /* generate the end point */ 
    my_addr.sin_family = AF_INET;   /* host byte order */ 
    my_addr.sin_port = htons(MYPORT);  /* short, network byte order */ 
    my_addr.sin_addr.s_addr = INADDR_ANY; /* auto-fill with my IP */ 
     /* bzero(&(my_addr.sin_zero), 8); ZJL*/  /* zero the rest of the struct */ 

    /* bind the socket to the end point */ 
    if (bind(sockfd, (struct sockaddr *)&my_addr, sizeof(struct sockaddr)) \ 
    == -1) { 
     perror("bind"); 
     exit(1); 
    } 

    /* start listnening */ 
    if (listen(sockfd, BACKLOG) == -1) { 
     perror("listen"); 
     exit(1); 
    } 

    printf("server starts listnening %d...\n",sockfd); 

    /* repeat: accept, send, close the connection */ 
    /* for every accepted connection, use a sepetate process or thread to serve it */ 
while(1) { /* main accept() loop */ 
    sin_size = sizeof(struct sockaddr_in); 
    if ((new_fd = accept(sockfd, (struct sockaddr *)&their_addr, \ 
    &sin_size)) == -1) { 
     perror("accept"); 
     continue; 
    } 
    printf("server: got connection from %s\n", \ 
     inet_ntoa(their_addr.sin_addr)); 

    if ((numbytes=recv(new_fd, buf, MAXDATASIZE, 0)) == -1) { 
    perror("recv"); 
    exit(1); 
    } 

    buf[numbytes] = '\0'; 

    printf("Received: %s",buf); 

    if (send(new_fd, "Hello, world!\n", MAXDATASIZE, 0) == -1) 
     perror("send"); 
     close(new_fd); 
     exit(0); 

    close(new_fd); /* parent doesn't need this */ 

    while(waitpid(-1,NULL,WNOHANG) > 0); /* clean up child processes */ 
} 
return 0; 
} 

所以每當我執行該服務器後,一個客戶端使用它終止。但是如果我想再次執行它可以說在60秒內,然後它給出了一個錯誤bind: Address already in use我認爲close()函數實際釋放套接字,以便它可以立即再次使用它。那麼我在這裏錯過了什麼?

+0

此代碼在發出「Hello,world!」後始終終止服務器。 – alk

+0

在調用套接字上的close()之前調用'shutdown()'不是一個壞主意。 – alk

+0

你還沒有調用fork()來清理子進程,還有一件事你沒有在return 0之前調用'exit(code)',我不確定是否使用'return 0'會讓內核關閉所有你打開的fds,而'exit(code)'做的,如果你不想在返回0之前使用exit(code)'close(sockfd)', if(send(new_fd,「Hello,world!\ n」,MAXDATASIZE,0)== -1)'。 –

回答

7

在致電bind,你可以標記要使用SO_REUSEADDR套接字選項可能重用一個地址/端口:

int reuseaddr = 1; 
int err = setsockopt(sockfd, SOL_SOCKET, SO_REUSEADDR, 
        &reuseaddr, sizeof(reuseaddr)); 
+0

so close()不釋放套接字? –

+0

不立即。 close()'套接字會進入[TIME_WAIT](http://www.unixguide.net/network/socketfaq/2.7.shtml)狀態。 SO_REUSEADDR告訴網絡堆棧忽略這個並重復使用地址 – simonc

+0

一個或許應該提到'close()'用於關閉文件描述符,並且確實會立即「釋放」。 – alk

0

另外,我看不出積壓的定義,您在使用listen()調用。如果偶然設置爲1,則可能需要增加它。然後,當最後一個套接字關閉時,您可以處理下一個呼叫。

0

首先這個代碼的原始形式來自Beej's guide

您所提供的代碼,或者是非常錯誤或編輯爲簡潔。發送「Hello World」響應後,您可以調用exit(0);請添加花括號。

if (send(new_fd, "Hello, world!\n", MAXDATASIZE, 0) == -1) 
    perror("send"); 
    close(new_fd); 
    exit(0); 

Beej的代碼爲:

if (!fork()) { // this is the child process 
     close(sockfd); // child doesn't need the listener 
     if (send(new_fd, "Hello, world!", 13, 0) == -1) 
      perror("send"); 
     close(new_fd); 
     exit(0); 
    } 
    close(new_fd); // parent doesn't need this` 

我還要指出的是,其中「recv的」在連接丟失或中斷的情況下返回0 Beej的代碼和你不處理該事件客戶端。在旁註中記住,對recv的呼叫將被阻止。

if ((numbytes=recv(new_fd, buf, MAXDATASIZE, 0)) == -1) { 
perror("recv"); 
exit(1); 
} 

雖然這看起來它可能不會影響到崩潰時,客戶端意外關閉這一具體問題可能會導致意外崩潰以後。

0

延誤是由於TIME_WAIT

在終止連接的過程中,要記住最重要的事情是,在連接兩側的應用過程中必須單獨關閉連接它的一半。

方法1:由於TCP連接的三次握手政策,內核等待承認,在另一側的連接也被關閉

然而,您可以通過以下方法來覆蓋此功能

/etc/sysctl.conf文件,添加以下行重啓後堅持它:

net.ipv4.tcp_tw_recycle = 1 
net.ipv4.tcp_tw_reuse = 1 

方法2

echo 1 > /proc/sys/net/ipv4/tcp_tw_recycle 
echo 1 > /proc/sys/net/ipv4/tcp_tw_reuse