8

I know you will mask this as duplicate (question1, question2, question3) but the answers are not what I'm looking for (and I think also other people).
So, I'm refering to socket masters (I love you guys): how can I get bind error(address already in use) if I close the socket?
I will describe my problem.

I've got a client that communicate with a server
In the server, I have two sockets: sockS (the main socket, that listens) and sockTX (the client one)
If I call my programs once, the communication is fine, then I close both sockets
If I recall server and client, I get the error and I've to wait the TIME_WAIT (~3 minutes seconds on Ubuntu 32bit)

Why, after closing both sockets I still get bind error?
There's a way to make it works without any magic (SO_REUSEADDR)?
I know something in my code is wrong...

Thank you everyone.

That's the code:
Client

#include <stdio.h>
#include <stdlib.h>
#include <ctype.h>
#include <netinet/in.h>
#include <sys/socket.h>
#include <sys/types.h>
#include <string.h>
#include <netdb.h>
#include <unistd.h>
#include <arpa/inet.h>

#define PORT 5000
#define SERVER "127.0.0.1"
#define MAXLINE 128

int printMessage(char* str);

int main(){
char buff[MAXLINE+1];

struct sockaddr_in server, client;
struct hostent *host;
int sock, n;
//socklen_t len;

    if((sock = socket(AF_INET,SOCK_STREAM,0)) == -1){
        perror("\nErrore socket()");
        return -1;
    }

    client.sin_family = AF_INET;
    client.sin_port = htons(0); // la porta e' scelta dal sistema operativo
    client.sin_addr.s_addr = htonl(INADDR_ANY);

    if( bind(sock,(struct sockaddr*)&client, sizeof(client)) == -1){
        perror("\nErrore bind()");
        return -1;
    }

    server.sin_family = AF_INET;
    server.sin_port = htons(PORT);

    if((host = gethostbyname(SERVER)) == NULL ){
        perror("\nErrore gethostbyname()");
        return -1;
    }

    server.sin_addr = *((struct in_addr *)host->h_addr);

    if(connect(sock, (struct sockaddr*)&server, sizeof(server)) < 0){
        perror("\nErrore connect");
        return -1;
    }

    // MESSAGGIO

    sprintf(buff, "CTX\nclientTX\nserver\nCiao da client\n");
    if(send(sock, buff, strlen(buff), 0) < 0) {
        perror("\nErrore sendto");
        return -1;
    }
    else {
        printf("\nMessaggio inviato");
    }

    if((n = recv(sock, buff, MAXLINE, 0)) < 0) {
        perror("\nErrore ricezione risposta");
        return -1;
    } else {
        buff[n] = '\0';
        int test = printMessage(buff);
        printf("\nEsito: %s\n", (test == 1 ? "OK" : "FAIL"));
    }

    shutdown(sock, 2); // 2 = RD_WR
    close(sock);
    return 0;
}

int printMessage(char* str){
int i;
char* temp;

    printf("Mittente: ");
    for(i = 0; str[i] != '\n'; i++)
        printf("%c", str[i]);
    printf(" ");
    for(i = i+1; str[i] != '\n'; i++)
        printf("%c", str[i]);
    printf("\nDestinatario: ");
    for(i = i+1; str[i] != '\n'; i++)
        printf("%c", str[i]);

    temp = (char*)malloc(30 * sizeof(char));
    strncpy(temp, str+i+1, 30);
    printf("Messaggio: %s\n", temp);

    if(strcmp(temp, "OK") == 0)
        return 1;
    else
        return 0;
}


Server:

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <netinet/in.h>
#include <sys/socket.h>
#include <sys/types.h>
#include <netdb.h>
#include <unistd.h>
#include <arpa/inet.h>

#define PORT 5000
#define SERVER "127.0.0.1"
#define MAXLINE 128

int printMessage(char* str);

int main() {
char buff[MAXLINE+1];
struct sockaddr_in server; //, client;
int sockS, sockTX, n;

    if((sockS = socket(AF_INET,SOCK_STREAM,0)) == -1)
    {
        perror("\nErrore socket()");
        return -1;
    }

    server.sin_family = AF_INET;
    server.sin_port = htons(PORT);
    server.sin_addr.s_addr = htonl(INADDR_ANY);

    if(bind(sockS, (struct sockaddr *)&server, sizeof(server)) == -1)
    {
        perror("\nErrore bind()");
        return -1;
    }

    if(listen(sockS, 10) == -1)
    {
        perror("\nErrore listen()");
        return -1;
    }
    printf("SERVER\nInizializzazione completata!\n");

    if((sockTX = accept(sockS, (struct sockaddr *)NULL, NULL)) == -1)
    {
        perror("\nErrore accept()");
        return -1; 
    }
    printf("Socket connesso\n");

    // INVIO
    if((n = recv(sockTX, buff, MAXLINE, 0)) < 0) {
        perror("\nErrore recv()");
        return -1; // BREAK??

    } else {
        buff[n] = '\0';
        printMessage(buff);

    }

    sprintf(buff, "S\nserver\nclientTX\nOK\n");
    if(send(sockTX, buff, strlen(buff), 0) < 0){
        perror("\nErrore send()");
        return -1; // BREAK??

    } else {
        printf("Risposta inviata\n");

    }

    shutdown(sockTX, 2);
    close(sockTX);

    shutdown(sockS, 2);
    close(sockS);
    return 0;   
}

int printMessage(char* str){
int i;
char* temp;

    printf("Mittente: ");
    for(i = 0; str[i] != '\n'; i++)
        printf("%c", str[i]);
    printf(" ");
    for(i = i+1; str[i] != '\n'; i++)
        printf("%c", str[i]);
    printf("\nDestinatario: ");
    for(i = i+1; str[i] != '\n'; i++)
        printf("%c", str[i]);

    temp = (char*)malloc(30 * sizeof(char));
    strncpy(temp, str+i+1, 30);
    printf("Messaggio: %s\n", temp);

    if(strcmp(temp, "OK\n") == 0)
        return 1;
    else
        return 0;
}

Thank you everyone

EDIT 1: Possible solution (not very beautiful, but just a bit more than SOCK_REUSEADDR)
Try to add, before shutdown and close of the two server's socket, a sleep(1).
The client will close before the server and it will work
Not very beautiful, I know.

Or, better, before closing the first socket inside the server, you can check if the client closed the connection (like here)

Community
  • 1
  • 1
incud
  • 541
  • 1
  • 9
  • 17
  • SO_REUSEADDR *is* the magic. – Duck May 28 '14 at 15:11
  • 1
    SO_REUSEADDR is not "magic"; it's the answer to your question. The fact that you don't like the correct answer is not a reason to repeat the question; the answer will not change. – rici May 28 '14 at 15:11
  • Also: It's really a bad idea to bind a client socket to a specific port. Let the OS choose a port for you, and you won't have any problem. Only servers need well-known (fixed) ports. – rici May 28 '14 at 15:14
  • @rici I wrote in on the first line (link no 1), and the reason why I decided to post the question, and I used a fixed PORT because I have a server.. – incud May 28 '14 at 15:20
  • @Duck I was looking for something a bit more 'elegant' :) – incud May 28 '14 at 15:21
  • @Ignus, then you don't need socket masters, you have to talk to the TCP gods. It's just how it works. :) – Duck May 28 '14 at 15:25
  • @Duck ahah fortunatly, one of my teacher is really a TCP god :D I have another example that works perfectly, without magic, and it's very similar to the one I posted.. I'll try to work a bit more. However, thanks a lot :) – incud May 28 '14 at 15:31
  • When you say "close both sockets", you mean `sockTX` and `sockS` in the server, right? You do not mean "the server socket and the client socket"? – jxh May 28 '14 at 15:31
  • So your question is basically: "Why is there TIME-WAIT for closing the accepting socket?" – jxh May 28 '14 at 15:34
  • @Ignus, then share your insight. Maybe you were misunderstood. – Duck May 28 '14 at 15:34
  • @jxh Not exactly. I was asking if there's a standard-correct method that can close my socket, tells the operative system my connection is closed and let me reuse the PORT immediatly :) – incud May 28 '14 at 15:40
  • @Ignus, those (your edit) are terrible solutions. Think of a server handling thousands of connections. Sleep the server a second for each one? What if the client doesn't close w/i a second? That is the point of time-wait to begin with. – Duck May 28 '14 at 16:04
  • @Duck oh :( you're right. The solution posted by jxh is better (very better). I just have problems with 'while(recv() < 0)' – incud May 28 '14 at 16:07
  • @rici: One reason to *not* use `SO_REUSEADDR` is that it can mask a bug when there really is another server using the port. The asker didn't ask the question very well, but the question is whether there is a way to close a socket that avoids the TIME-WAIT state. – jxh May 29 '14 at 02:21
  • @jxh: It's true that `SO_REUSEADDR` needs to be used with caution, and it's also true that if the server and client are on the same machine and you are confident that the client will send a `FIN`, it's ok to wait for it (or, in other words, that solution also needs to be used with caution :) ). I agree, though, that you have provided a good answer to the question you just raised. – rici May 29 '14 at 02:29
  • ARRRRRRRRRRRRRRRRRRRRRRRRRRRDUINO!!! TASTY – incud Jul 21 '14 at 08:50

1 Answers1

6

If you follow the TCP state machine diagram, you will see that it is mandatory for the socket to transition to the TIME-WAIT state if the socket initiated sending the FIN. Using shutdown(sockTX, 2) without waiting for the client's FIN does exactly that.

If you want the server to wait for the client's FIN, you block on recv() waiting for a 0 return value first. Then, you can close().

Note that unless you have duplicated the socket in some way (either with dup*() or with a fork() call), there is no need to call shutdown() if it is being immediately followed by a close(). You can just call close() (if the socket has been duplicated, the FIN will only be sent when the last copy is closed).

There is no need to shutdown() the accepting socket (sockS) at all.

So, I would change your server side to look something like the following to clean up the sockets:

while (recv(sockTX,...) > 0) {}
close(sockTX);

close(sockS);
jxh
  • 69,070
  • 8
  • 110
  • 193
  • Oh perfect :D I have just added a 'recv' before closing the first socket, and it works :D I tried just with the send but it gave me (obviously) some problems. (For now) it works. Thanks :D – incud May 28 '14 at 15:56
  • Just one note: if I write while(recv() < 0); it waits forever. If I test the condition once, it works. Uhm the second case (the one that seems to works) is correct or no? I think no, because it's just coincidence that when I test the condition, the client have already closed the socket. Or what? Why the 'while(recv() < 0)' doesn't work? – incud May 28 '14 at 16:05
  • `while (recv() < 0);` is nearly the opposite of my suggestion. What you want to do is wait for the client FIN, so you want to test if a 0 value is returned or an error. If you save the return value, you will see what value you received, and if it is less than 0, you should check what the error is. – jxh May 28 '14 at 16:08
  • yes sorry it was a misprint inside the comment. my code is while(1) { if(recv(sockTX, buff, MAXLINE, 0) < 0) break; } – incud May 28 '14 at 16:19
  • With that version, the code with call `recv()` again if a 0 is returned, which is wrong. 0 return means FIN has arrived. – jxh May 28 '14 at 16:23
  • OOK I'm such a bad programmer. Changing my code into "while((n = recv()) >= 0) printf("%d\n", n);" I saw that recv returned 0. It is less than 0 and not less equal than 0. And jxh wrote it right. I'm wrong. Ok sockets taught me a hard lesson. Thankyou again man – incud May 28 '14 at 16:28