0

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;
}
4
  • 1
    accept returns the new socket you should use to communicate with the client, or -1 if accept was unsuccessful. Commented Oct 3, 2022 at 21:47
  • 1
    You should really use getaddrinfo rather than that outdated IPv4 stuff. Commented Oct 3, 2022 at 22:06
  • @KompjoeFriek, Should I set the 2nd and 3rd parameter in the accept function as NULL then? Commented Oct 3, 2022 at 22:34
  • @Cheatah, Thank you for your reply, I will study what "getaddrinfo" is and try to implement that. Commented Oct 3, 2022 at 22:35

2 Answers 2

2

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.

Sign up to request clarification or add additional context in comments.

2 Comments

And don't forget to close() the conSoc when you are done using it.
Thank you for your help, chrslg. I will try to implement the pointers you gave.
2

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 guaranteed 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.

  • there is no 1:1 relationship between sends and reads in TCP. Either operation can report fewer bytes than requested, so you have to be prepared to handle that condition. If send() reports fewer bytes than you asked to send, you have to call send() again to send the remaining unsent bytes. Likewise, if recv() reports fewer bytes than you asked to read, you have to call recv() again to read the remaining unread bytes.

  • 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>

ssize_t sendAll(int sockfd, const void *buf, size_t len) {
    const char *pbuf = (const char*) buf;
    while (len > 0) {
        ssize_t numSent = send(sockfd, pbuf, len, 0);
        if (numSent < 0) return -1;
        pbuf += numSent;
        len -= numSent;
    }
    return 0;
}

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 (sendAll(clientSoc, recvBuff, numRecvd) < 0){
            printf("We have an error sending a message to the client.\n");
            return -1;
        }
    }
    
    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>

ssize_t sendAll(int sockfd, const void *buf, size_t len) {
    const char *pbuf = (const char*) buf;
    while (len > 0) {
        ssize_t numSent = send(sockfd, pbuf, len, 0);
        if (numSent < 0) return -1;
        pbuf += numSent;
        len -= numSent;
    }
    return 0;
}

ssize_t recvAll(int sockfd, void *buf, size_t len) {
    char *pbuf = (char*) buf;
    while (len > 0) {
        ssize_t numRecvd = recv(sockfd, pbuf, len, 0);
        if (numRecvd <= 0) return numRecvd;
        pbuf += numRecvd;
        len -= numRecvd;
    }
    return 1;
}

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;
    }

    size_t len = strlen(clientSend);

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

    ssize_t clientRecvmsg = recvAll(clientSoc, clientRecv, min(sizeof(clientRecv), len));
    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;
}

6 Comments

Thank you for your help Remy Lebeau. I just had a question, why did you use while loop in the server after the accept instead of an 'if' and also why not send a string through the send() function?
"why did you use while loop in the server after the accept instead of an 'if'" - because an ECHO server should echo back whatever the client sends. Also, there is no 1:1 relationship between sends and reads in TCP, so you have to be prepared to handle the case where recv() returns fewer bytes than requested. The loop is so the server can handle all of the data that the client sends, until the client disconnects. "why not send a string through the send() function?" - again, because an ECHO server should echo the client's data exactly, not send its own data.
Also, I did make the changes you referred to Remy Lebeau. The code runs completely fine.
Hello Remy, I actually ran the two codes that you wrote for server and client. I had some doubts regarding them. 1.) In the client program, line 64 which is ssize_t clientRecvmsg = recvAll() you have given 4 parameters but the function just has three. 2.) Why did you make two different functions by the name sendAll and recvAll? 3.) When I ran the code and check for the processes in terminal, it showed that client and server both had processes with different PID is that normal result for the code?
Also Remy, The code ran when I commented on this line "serverInfo.sin_addr.s_addr = INADDR_LOOPBACK;"
|

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.