C言語 - bind address already in use

 クラウディア
1. 概要
2. サーバプログラムサンプルコード
3. ポート解放の例

1. 概要

 socket の話なのです。  ソケットプログラミングの話ですが、ながらく悩んでいた問題が解決したので・・・(この程度のことでながらく悩んでいたということを白状する時点で技術力のなさを露呈)。  サーバソケットのプログラムを書いたとき(いまどきソケットのプログラムをべたで書くこともあまりないのでしょうが)、Ctrl-C で止めたり、ネットワークが切断されたために、プログラムを再起動して、bind すると return 値に -1 が返されることが多々あります。ソケットをクローズしていても出力されたりします。  errno は EADDRINUSE(98) で perrror や strerror で見てみると「Address already in use」とのメッセージ。  これは、ソケットを close した際に、FIN を送っているのですが、tcp/ip 接続しているものを close した場合、相手からの ACK が返ってくるのを待っているのです(RFC に書いてあるらしい)。  確かに netstat で見てみるとそのポートが TIME_WAIT の状態になっています。2 分待つのが規格らしく・・・。  しかし、実際、次の接続を行いたいので 2分待つのはもどかしいというか、お客さんは待ってくれない(笑)。  こういうときは、bind する前に setsockopt で SO_REUSEADDR オプションを設定してやれば、ポートを再利用できます。

2. サーバプログラムサンプルコード

 昔懐かしい、サーバプログラムのサンプルコード (ひろいものですが、こんな感じだったでしょうか?)

     int sockfd, newsockfd, portno, clilen;
     char buffer[256];
     struct sockaddr_in serv_addr, cli_addr;
     int  n;

     /* First call to socket() function */
    sockfd = socket(AF_INET, SOCK_STREAM, 0);

    if (sockfd < 0)
    {
        perror("ERROR opening socket");
        exit(1);
    }

    /* Initialize socket structure */
    bzero((char *) &serv_addr, sizeof(serv_addr));
    portno = 5001;

    serv_addr.sin_family = AF_INET;
    serv_addr.sin_addr.s_addr = INADDR_ANY;
    serv_addr.sin_port = htons(portno);

    /* Now bind the host address using bind() call.*/
    if (bind(sockfd, (struct sockaddr *) &serv_addr, sizeof(serv_addr)) < 0)
    {
        perror("ERROR on binding");  ← ここで「ERROR on binding Address already in use」となるわけです
        exit(1);
    }

    /* Now start listening for the clients, here process will
     * go in sleep mode and will wait for the incoming connection
     */

    listen(sockfd,5);
    clilen = sizeof(cli_addr);

    /* Accept actual connection from the client */
    newsockfd = accept(sockfd, (struct sockaddr *)&cli_addr, &clilen);
    if (newsockfd < 0)
    {
        perror("ERROR on accept");
        exit(1);
    }

    /* If connection is established then start communicating */
    bzero(buffer,256);
    n = read(newsockfd, buffer, 255);

    if (n < 0)
    {
        perror("ERROR reading from socket");
        exit(1);
    }

    printf("Here is the message: %s\n",buffer);

    /* Write a response to the client */
    n = write(newsockfd, "I got your message", 18);

    if (n < 0)
    {
        perror("ERROR writing to socket");
        exit(1);
    }

    return 0;
}

3. ポート解放の例

 前項のソースを下記のように書き加えてやれば (加えたのは 23~29 行)

     int sockfd, newsockfd, portno, clilen;
     char buffer[256];
     struct sockaddr_in serv_addr, cli_addr;
     int  n;

     /* First call to socket() function */
    sockfd = socket(AF_INET, SOCK_STREAM, 0);

    if (sockfd < 0)
    {
        perror("ERROR opening socket");
        exit(1);
    }

    /* Initialize socket structure */
    bzero((char *) &serv_addr, sizeof(serv_addr));
    portno = 5001;

    serv_addr.sin_family = AF_INET;
    serv_addr.sin_addr.s_addr = INADDR_ANY;
    serv_addr.sin_port = htons(portno);

    int yes = 1;

    if (setsockopt(sockfd, SOL_SOCKET, SO_REUSEADDR, (const char *)&yes, sizeof(yes)) < 0)
    {
        perror("ERROR on setsockopt");
        exit(1);
    }

    /* Now bind the host address using bind() call.*/
    if (bind(sockfd, (struct sockaddr *) &serv_addr, sizeof(serv_addr)) < 0)
    {
        perror("ERROR on binding");
        exit(1);
    }

    /* Now start listening for the clients, here process will
     * go in sleep mode and will wait for the incoming connection
     */

    listen(sockfd,5);
    clilen = sizeof(cli_addr);

    /* Accept actual connection from the client */
    newsockfd = accept(sockfd, (struct sockaddr *)&cli_addr, &clilen);
    if (newsockfd < 0)
    {
        perror("ERROR on accept");
        exit(1);
    }

    /* If connection is established then start communicating */
    bzero(buffer,256);
    n = read(newsockfd, buffer, 255);

    if (n < 0)
    {
        perror("ERROR reading from socket");
        exit(1);
    }

    printf("Here is the message: %s\n",buffer);

    /* Write a response to the client */
    n = write(newsockfd, "I got your message", 18);

    if (n < 0)
    {
        perror("ERROR writing to socket");
        exit(1);
    }

    return 0;
}
 他の理由がない限り、正常に接続できるようになります。
earthcar(アースカー)
JETBOY