Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to avoid TIME_WAIT for server sockets? [duplicate]

Tags:

c

linux

sockets

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)

like image 840
incud Avatar asked May 28 '14 15:05

incud


People also ask

What will happen if you have too many socket connections in Time_wait state on server?

If too many sockets are in TIME_WAIT you will find it difficult to establish new outbound connections due to there being a lack of local ports that can be used for the new connections.

What causes Time_wait?

From Network perspective, TCP TIME_WAIT status is just a normal behavior that after closing the session, TCP stack will hold the high port for little more time to ensure the other side receive the last FIN-ACK packet and no more data will be received in this conversation.

How long does socket stay in Time_wait?

The TIME-WAIT state is a mechanism in TCP/IP stacks that keeps sockets open after an application shuts down the sockets. By default, this state lasts for 60 seconds to ensure complete data transmission between the server and the client.

What means time wait?

TIME_WAIT means it's waiting for a reply or connection. this often happens when a port is activated and the connection has not yet. been established.


1 Answers

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);
like image 199
jxh Avatar answered Oct 25 '22 18:10

jxh