Chris Grimm
Chris Grimm

Reputation: 771

Address Already in Use.

Recently I have been working on some client-side code for sending and receiving messages from a server using threading. The below code behaves strangely when run. Upon inputting a message to send to the server, the code completes the task, albeit with a "socket already in use" error, the server gets it. But every subsequent message I attempt to send to the server is not received immediately, yet it is seemingly all received at once when the client program terminates.

(Additionally, I am certain the error is client-side, the strange behavior isn't exhibited if one comments the output function.)

How can I fix this error?

Client

#include <stdio.h>
#include <cstdlib>
#include <sys/types.h>
#include <sys/socket.h>
#include <sys/time.h>
#include <unistd.h>
#include <netdb.h>
#include <arpa/inet.h>
#include <string>
#include <iostream>
#include <errno.h>
#include <pthread.h>    
void* input(void* ptr)
{
    int on = 1;
    bool *input_done = ((struct thread_args*)ptr)->process_done;
    struct addrinfo *res = ((struct thread_args*)ptr)->result;
    char msg[256];
    int sock = socket(res->ai_family,res->ai_socktype,res->ai_protocol);
    setsockopt(sock,SOL_SOCKET,SO_REUSEADDR,(char *)&on,sizeof(on));
    bind(sock,res->ai_addr,res->ai_addrlen);
    connect(sock,res->ai_addr,res->ai_addrlen);
    cin.getline(msg,256);
    if (msg[0] == '/') {exit(1);}
    send(sock,msg,sizeof msg,0);
    cout << "You:" << msg << endl;
    *input_done = 1;
    close(sock);
    pthread_exit(NULL);
}
void* output(void* ptr)
{
        int on = 1;
        bool *output_done = ((struct thread_args*)ptr)->process_done;
    struct addrinfo *res = ((struct thread_args*)ptr)->result;
    char msg[256];
    int sock = socket(res->ai_family,res->ai_socktype,res->ai_protocol);
    bind(sock,res->ai_addr,res->ai_addrlen);
    connect(sock,res->ai_addr,res->ai_addrlen);
    recv(sock,msg,sizeof msg,0);
    cout << "Recieved:" << msg;
    *output_done = 1;
    close(sock);
    pthread_exit(NULL);
}

void io_client()
{
    //thread function variables
    pthread_t t1,t2;
    bool input_done = 1, output_done = 1;
    //socket setup variables
    struct addrinfo hints, *res;
    memset(&hints,0,sizeof hints);
    hints.ai_family = AF_INET;
    hints.ai_socktype = SOCK_STREAM;
    getaddrinfo("localhost","8080",&hints,&res);
    //setting up structures to pass data to threaded functions
    struct thread_args i_args, o_args;
    i_args.result = res; i_args.process_done = &input_done;
    o_args.result = res; o_args.process_done = &output_done;
    while(1)
    {
        if (output_done)
        {
            pthread_create(&t2,NULL,output,&o_args);
            output_done = 0;
        }
        if (input_done)
        {
            pthread_create(&t1,NULL,input,&i_args);
            input_done = 0;
        }
    }
}
int main()
{
    io_client();
}

Server

void server()
{
    struct addrinfo hints, *res;
    int sock=-1, newsock=-1;
    int length, on=1;
    char **address_list; int entries = 0;
    //fd_set read_fd;
    //struct timeval timeout;
    char buffer[100];
    memset(&hints,0,sizeof hints);
    res = NULL;
    memset(&res,0,sizeof res);
    hints.ai_family = AF_INET;
    hints.ai_socktype = SOCK_STREAM;
    getaddrinfo("localhost","8080",&hints,&res);
    sock = socket(res->ai_family,res->ai_socktype,res->ai_protocol);
    setsockopt(sock,SOL_SOCKET,SO_REUSEADDR,(char *)&on,sizeof(on));
    bind(sock,res->ai_addr,res->ai_addrlen);
    listen(sock,10);
    while(1)
    {
        struct sockaddr_storage addr;
        char ipstr[INET6_ADDRSTRLEN];
        socklen_t len;
        len = sizeof addr;
        newsock = accept(sock,NULL,NULL);
        getpeername(newsock,(struct sockaddr*)&addr,&len);
        struct sockaddr_in *s = (struct sockaddr_in*)&addr;
        inet_ntop(AF_INET,&s->sin_addr,ipstr,sizeof ipstr);
        length = 100;
        setsockopt(newsock,SOL_SOCKET,SO_RCVLOWAT, (char*)&length,sizeof length);
        recv(newsock,buffer,sizeof buffer,0);
        cout << buffer << endl;
    }
    if (newsock != -1)
    {
        close(newsock);
    }
    if (sock != -1)
    {
        close(sock);
    }
}
int main()
{
    server();
}

Upvotes: 1

Views: 3476

Answers (2)

selbie
selbie

Reputation: 104514

It looks like you are trying to have your client bind() to the same port as the server. That's not necessary. And worse, you are trying to bind to to the IP address of the server - which is also a bigger problem. In general, for client sockets that are to call the connect() function, you should just have your socket bind to port 0 and IP 0, thus letting the OS pick a randomly available port for you and enabling use the right local IP address and adapter for the connection. You can call getsockname() to discover what port the OS picked for you after you call connect.

And if you let the OS pick the client port for you, you won't need that SO_REUSESADDR call. Although, your server code could call it for cases where it needs to restart after shutting down with connections still pending to close.

Also. you aren't checking the return value of any of your socket calls. That's probably why you are getting some mysterious results. The call to bind() is more likely failing because you are specifying the server IP, but connect() is succeeding because it will auto-bind the socket if it hasn't already.

Here's a cleaned up version of you input() function. Converting your output() function is an exercise left up to the reader. If you follow my example, you'll be in good shape.

void* input(void* ptr)
{
    int on = 1;
    bool *input_done = ((struct thread_args*)ptr)->process_done;
    int ret;
    int success = true;

    struct sockaddr_in addrLocal = {};

    struct addrinfo *res = ((struct thread_args*)ptr)->result;
    char msg[256];

    int sock = socket(AF_INET, SOCK_STREAM, 0);
    success = (sock != -1);

    if (success)
    {
        addrLocal.sin_family = AF_INET;
        addrLocal.sin_port = INADDR_ANY;        // INADDR_ANY == 0 --> pick a random port for me
        addrLocal.sin_addr.s_addr = INADDR_ANY; // INADDR_ANY == 0 --> use all appropriate network 
        ret = bind(sock,(sockaddr*)&addrLocal,sizeof(addrLocal));
        if (ret == -1) perror("bind: ");
        success = (ret != -1);
    }

    if (success)
    {
        ret = connect(sock,res->ai_addr,res->ai_addrlen);
        if (ret == -1) perror("connect: ");
        success = (ret != -1);
    }

    if (success)
    {
        cin.getline(msg,256);
        if (msg[0] == '/') {exit(1);}
        ret = send(sock,msg,sizeof msg,0);
        if (ret == -1) perror("send: ");
        success = (ret != -1);
    }

    if (success)
    {
        cout << "You:" << msg << endl;
        *input_done = 1;
    }

    if (sock != -1)
    {
        close(sock);
        sock = -1;
    }

    return NULL;
}

Upvotes: 3

Arunmu
Arunmu

Reputation: 6901

I guess that "SO_REUSEADDR" socket option that you are giving is the problem.

Are you calling that function again and again without closing the client socket ? In that case it will not work.

The purpose of this socket option is to "reuse the address when the already opened socket for the same address is in TIME_WAIT state else you will get the mentioned error".

If you client is opening a new connection each and every time, then I must say that you will have to structure your code more efficiently and handle the socket closing scenarios as well.

Upvotes: 0

Related Questions