Home > front end >  Socket programming Issue - C
Socket programming Issue - C

Time:10-04

I am trying to make a simple echo program which includes a client and a server utilizing TCP protocol under Socket Programming. For this purpose I am using C to write both server.c and client.c.

The issue I am getting is in server.c program where I am trying to reveive a message from the client, after the connection has been made but keeps on failing. This happens in the recv() API that is in the server program. Here are the two programs:

1.) Server.c:

#include<stdio.h>
#include<stdlib.h>
#include<sys/socket.h>
#include<sys/types.h>
#include<netinet/ip.h>

int main(){

    //Let's create a socket here.
    int serverSoc = socket(PF_INET, SOCK_STREAM,0);


    struct sockaddr_in serverInfo;
    serverInfo.sin_family = PF_INET;
    serverInfo.sin_port = htons(8000);
    serverInfo.sin_addr.s_addr = INADDR_ANY; 
    socklen_t addrLen = (socklen_t)sizeof(serverInfo);

      //We need to bind the socket to the port number. So, here we make use of the in-built struct
    if(bind(serverSoc,(struct sockaddr*)&serverInfo,addrLen)<0){
        printf("The binding of the server socket with the given port has been unsuccessful\n.");
        return -1;
    }

    //After binding the socket, here we listen for connections from the clients
    if(listen(serverSoc, 1)<0){ //We used 1 here in the int backlog because that specifies the number of pending connections that the queue will hold. This will help in the case when multiple clients try to connect to the server.
        printf("We have an error in connecting to the client(s)\n");
        return -1; 
    }

    if(accept(serverSoc,(struct sockaddr*)&serverInfo,&addrLen)<0){
        printf("We have an error accepting the connections from the client(s).\n");
        return -1;
    }

    char recvBuff[50];
    if(recv(serverSoc,&recvBuff,sizeof(recvBuff),0)<0){
     printf("We have an error receiveing messages from the client.\n");
     printf("%zd\n",recv(serverSoc,recvBuff,sizeof(recvBuff),0));
     return -1;
    }else{
        printf("The size of recvBuff is:%d\n",(int)sizeof(recvBuff));
        printf("%s\n",recvBuff); 
    }
     
     char sendBuff[50] = "hello";
    if(send(serverSoc,&sendBuff,sizeof(sendBuff),0)<0){
        printf("We have an error sending message to the client.\n");
        return -1;
    }else{
         printf("%zd\n",send(serverSoc,&sendBuff,sizeof(sendBuff),0));
    }

return 0;
}

2.) Client.c:

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

int main(){
    char clientRecv[50];
    char clientSend[50] = "Hello Server\n";
    //Here, we create a socket for the client with the in-built function socket that we get from sys/socket.h, socket(int domain, int type , int protocol)
    int clientSoc = socket(PF_INET,SOCK_STREAM,0);
    printf("The client socket has been assigned the value of: %d\n",clientSoc);
    if(clientSoc < 0){
        printf("We have an error");
        return (-1);
    }

    //all the struct(s) that are needed for the program
    struct sockaddr_in serverInfo;
    serverInfo.sin_family = PF_INET;
    serverInfo.sin_port = htons(8000);
    serverInfo.sin_addr.s_addr = INADDR_ANY;

    //Connecting the client to the server
    if((connect(clientSoc,(struct sockaddr*)&serverInfo,(socklen_t)sizeof(serverInfo)))< 0){
        printf("Not connecting");
return(-1);
    }

if(send(clientSoc,&clientSend,sizeof(clientSend),0)<0){
    printf("We are having an error in sending the message.\n");
    return -1;
}
int clientRecvmsg = (int)recv(clientSoc, &clientRecv,sizeof(clientRecv),0);
if(clientRecvmsg<0){
    printf("We have error receiving messages from the server.\n");
}else{
printf("clientRecvmsg: %s\n", clientRecv);
return -1;
}
return 0;
}

CodePudding user response:

Do not call recv() on the listening server socket (serverSoc). That socket is made only to accept new connections. Communication needs to occur on the connected socket that accept() returns.

So, do not use this:

accept(serverSoc,...);
recv(serverSoc,...);

but do use this:

int conSoc = accept(serverSoc,...);
recv(conSoc,...);

Some explanation, if needed.

Note that if you accept only one connection simultaneously (which you do, currently), this may seem as an overcomplication (why would the API force me to use 2 different sockets? one may ask).

But if you don't, well, consider that two different things may happen anytime, concurrently, to the server:

  • An already connected client may send or read some data. Or disconnect
  • A new, not already connected client may connect.

Imagine, a web server: while it is serving some big files to, say, three different clients, you don't want the server to refuse any connection from a 4th client (at least not if you are Google. My server probably can). So, while that server is serving 3 clients (reading what they write, writing what they read), it needs 4 sockets. 3 (conSoc-like) to send/receive data to each of the 3 clients, and a 4th (serverSoc-like) to get incoming new connections.

In C you can implement that with threads or processes. Or with poll or select. In which case you would either have 4 threads/processes (1 recving/sending from/to each client, on conSoc-like sockets, and another accepting new client on serverSoc). Or pass 4 sockets to listen/poll(3conSoc-like to watch when a connected client is ready to recv or send. A 1 serverSocto watch when a new connection is ready to beaccept``ed.)

So, well, long story short, the serverSoc socket that you use to received new connections is not the same as the conSoc you use to talk into those connections. Even if, with 1 connection at a time, it is not obvious why.

CodePudding user response:

There are 2 main problems with your code:

  1. your server is using the wrong socket with recv() and send(). It needs to use the connected socket that accept() returns, not the listening socket that accept() is being called on. That is why recv() is failing.

  2. your client is connect()'ing to the wrong IP address. INADDR_ANY (0.0.0.0) is OK for a server to listen on (it will listening on all local interfaces), but is not OK for a client to connect to. You must connect to an actual IP address that the server is bound to and listening on. For instance, if your client and server are running on the same PC, you can connect to INADDR_LOOPBACK (127.0.0.1) instead.

On a lesser note, your code has some other problems, too:

  • you say you are trying to make an echo server, but your service is not trying to echo back what the client has sent.

  • after calling recv(), your server is using recvBuff incorrectly. It is not guaranted to be null-terminated (even if the client sends a null-terminator), but you are assuming it always will be. You are ignoring the return value of recv(), which tells you how many bytes were actually written into recvBuff.

  • you are calling recv() and send() inside of your print messages, which you should not be doing at all, especially in error messages.

Try something more like this instead:

Server.c:

#include<stdio.h>
#include<stdlib.h>
#include<sys/socket.h>
#include<sys/types.h>
#include<netinet/ip.h>

int main(){

    //Let's create a socket here.
    int serverSoc = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
    if (serverSoc < 0){
        printf("The creation of the server socket has been unsuccessful\n.");
        return -1;
    }

    struct sockaddr_in serverInfo;
    memset(&serverInfo, 0, sizeof(serverInfo));
    serverInfo.sin_family = AF_INET;
    serverInfo.sin_port = htons(8000);
    serverInfo.sin_addr.s_addr = INADDR_ANY; 

    //We need to bind the socket to the port number. So, here we make use of the in-built struct
    if (bind(serverSoc, (struct sockaddr*)&serverInfo, (socklen_t)sizeof(serverInfo)) < 0){
        printf("The binding of the server socket with the given port has been unsuccessful\n.");
        return -1;
    }

    //After binding the socket, here we listen for connections from the clients
    if (listen(serverSoc, 1) < 0){ //We used 1 here in the int backlog because that specifies the number of pending connections that the queue will hold. This will help in the case when multiple clients try to connect to the server.
        printf("We have an error in listening for clients\n");
        return -1; 
    }

    int clientSoc = accept(serverSoc, NULL, NULL);
    if (clientSock < 0){
        printf("We have an error accepting the connection from a client.\n");
        return -1;
    }

    char recvBuff[50];
    ssize_t numRecvd, numSent;

    while ((numRecvd = recv(clientSoc, recvBuff, sizeof(recvBuff), 0)) > 0){
        printf("The size of recvBuff is: %zd\n", numRecvd);
        printf("%.*s\n", (int)numRecvd, recvBuff); 

        if ((numSent = send(clientSoc, recvBuff, numRecvd, 0)) < 0){
            printf("We have an error sending a message to the client.\n");
            return -1;
        }

        printf("%zd\n", numSent);
    }
    
    if (numRecvd < 0){
        printf("We have an error receiving a message from the client.\n");
        return -1;
    } 

    printf("The client disconnected.\n");

    close(clientSoc);
    close(serverSoc);
    
    return 0;
}

Client.c:

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

int main(){
    char clientRecv[50];
    char clientSend[50] = "Hello Server\n";

    //Here, we create a socket for the client with the in-built function socket that we get from sys/socket.h, socket(int domain, int type , int protocol)
    int clientSoc = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
    printf("The client socket has been assigned the value of: %d\n", clientSoc);
    if (clientSoc < 0){
        printf("We have an error");
        return (-1);
    }

    //all the struct(s) that are needed for the program
    struct sockaddr_in serverInfo;
    memset(&serverInfo, 0, sizeof(serverInfo));
    serverInfo.sin_family = AF_INET;
    serverInfo.sin_port = htons(8000);
    serverInfo.sin_addr.s_addr = INADDR_LOOPBACK;

    //Connecting the client to the server
    if (connect(clientSoc, (struct sockaddr*)&serverInfo, (socklen_t)sizeof(serverInfo)) < 0){
        printf("Not connecting");
        return -1;
    }

    if (send(clientSoc, clientSend, strlen(clientSend), 0) < 0){
        printf("We are having an error in sending the message.\n");
        return -1;
    }

    ssize_t clientRecvmsg = recv(clientSoc, clientRecv, sizeof(clientRecv), 0);
    if (clientRecvmsg <= 0){
        printf("We have error receiving a message from the server.\n");
        return -1;
    }

    printf("clientRecvmsg: %.*s\n", (int)clientRecvmsg, clientRecv);

    close(clientSoc);
    return 0;
}
  • Related