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 recv
ing/send
ing from/to each client, on conSoc
-like sockets, and another accepting
new client on serverSoc). Or pass 4 sockets to
listen/
poll(3
conSoc-like to watch when a connected client is ready to recv or send. A 1
serverSocto watch when a new connection is ready to be
accept``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:
your server is using the wrong socket with
recv()
andsend()
. It needs to use the connected socket thataccept()
returns, not the listening socket thataccept()
is being called on. That is whyrecv()
is failing.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 toINADDR_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 usingrecvBuff
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 ofrecv()
, which tells you how many bytes were actually written intorecvBuff
.you are calling
recv()
andsend()
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;
}