I am implementing a Linux C program for client/server. I am using select() to connect with multiple clients. The 1st client connected successfully, but 2nd, 3rd and so on, have not give response any response in terminal(server), although client program is executed. The server is connected with some custom shell that run on client side.My server code is:
/**Handle multiple socket connections with select and fd_set on Linux*/
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <errno.h>
#include <unistd.h>
#include <arpa/inet.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <sys/time.h>
#include <pthread.h>
#define TRUE 1
#define FALSE 0
#define PORT 80
#define BUFSIZE 1025
void *sending( void *fdc)
//void sending(int fd_client)
{
int fd_client= *((int *)fdc);
char buffer[BUFSIZE];
while(1)
{
memset(buffer,0,sizeof(buffer));
fgets(buffer,512,stdin);
if((write(fd_client,buffer,strlen(buffer)))==0)
{
printf("HOST [%d] DISCONNECTED \n",fd_client);
close(fd_client);
}
if((!strcmp(buffer,"quit")) || (!strcmp(buffer,"q")) || (!strcmp(buffer,"QUIT")) || (!strcmp(buffer,"Q")))
{
exit(1);
}
}
}
void *receiving( void *arg)
//void receiving(int fd_client)
{
int fd_client= *((int *)arg);
char buffer[BUFSIZE];
ssize_t size;
while(1)
{
do{
if((size=read(fd_client,buffer,BUFSIZE))==0)
{
printf("HOST [%d] DISCONNECTED \n",fd_client);
close(fd_client);
}
if((write(1,buffer,size))==-1)
{
printf("Eror in Writting to client\n");
perror("");
exit(0);
}
}while(size==BUFSIZE);
}
}
//void *socket_thread(void *arg)
void socket_thread(int fd_client)
{
pthread_t th1,th2;
pthread_create(&th1,NULL,&sending,&fd_client);
pthread_create(&th2,NULL,&receiving,&fd_client);
pthread_join(th1,NULL);
pthread_cancel(th2);
}
int main(int argc , char *argv[])
{
int opt = TRUE;
int master_socket , addrlen , new_socket , client_socket[30] , max_clients = 30 , activity, i , valread , sd;
int max_sd;
struct sockaddr_in address;
char buffer[1025]; //data buffer of 1K
//set of socket descriptors
fd_set readfds;
//a message
char *message = "ECHO Daemon v1.0 \r\n";
//initialise all client_socket[] to 0 so not checked
for (i = 0; i < max_clients; i++)
{
client_socket[i] = 0;
}
//create a master socket
if( (master_socket = socket(AF_INET , SOCK_STREAM , 0)) == 0)
{
perror("socket failed");
exit(EXIT_FAILURE);
}
//set master socket to allow multiple connections , this is just a good habit, it will work without this
if( setsockopt(master_socket, SOL_SOCKET, SO_REUSEADDR, (char *)&opt, sizeof(opt)) < 0 )
{
perror("setsockopt");
exit(EXIT_FAILURE);
}
//type of socket created
address.sin_family = AF_INET;
address.sin_addr.s_addr = INADDR_ANY;
address.sin_port = htons( PORT );
//bind the socket to localhost port 8888
if (bind(master_socket, (struct sockaddr *)&address, sizeof(address))<0)
{
perror("bind failed");
exit(EXIT_FAILURE);
}
printf("Listener on port %d \n", PORT);
//try to specify maximum of 3 pending connections for the master socket
if (listen(master_socket, 3) < 0)
{
perror("listen");
exit(EXIT_FAILURE);
}
//accept the incoming connection
addrlen = sizeof(address);
puts("Waiting for connections ...");
pthread_t tid[30];
while(TRUE)
{
//clear the socket set
FD_ZERO(&readfds);
//add master socket to set
FD_SET(master_socket, &readfds);
max_sd = master_socket;
//add child sockets to set
for ( i = 0 ; i < max_clients ; i++)
{
//socket descriptor
sd = client_socket[i];
//if valid socket descriptor then add to read list
if(sd > 0)
FD_SET( sd , &readfds);
//highest file descriptor number, need it for the select function
if(sd > max_sd)
max_sd = sd;
}
//wait for an activity on one of the sockets , timeout is NULL , so wait indefinitely
activity = select( max_sd + 1 , &readfds , NULL , NULL , NULL);
if ((activity < 0) && (errno!=EINTR))
{
printf("select error");
}
//If something happened on the master socket , then its an incoming connection
if (FD_ISSET(master_socket, &readfds))
{
if ((new_socket = accept(master_socket, (struct sockaddr *)&address, (socklen_t*)&addrlen))<0)
{
perror("accept");
exit(EXIT_FAILURE);
}
//inform user of socket number - used in send and receive commands
printf("New connection , socket fd is %d , ip is : %s , port : %d \n" , new_socket , inet_ntoa(address.sin_addr) , ntohs(address.sin_port));
puts("Welcome message sent successfully");
for (i = 0; i < max_clients; i++)
{
//if position is empty
if( client_socket[i] == 0 )
{
client_socket[i] = new_socket;
printf("Adding to list of sockets as %d\n" , i);
break;
}
}
}
//else its some IO operation on some other socket :)
for (i = 0; i < max_clients; i++)
{
sd = client_socket[i];
if (FD_ISSET( sd , &readfds))
{
socket_thread(sd);
}
}
}
return 0;
}
The program work perfectly for one client but not for multiple. I also tries multi-threads for each file descriptor instead of using select(), but in this case the 2nd client connection overtake the first one in terminal. I want the following outcomes:
Switch between multiple connected clients from Linux terminal(i,e by pressing some keys from keyboard).
When new client is connected in-between, , it prompt a signal , and also can be switch to further read and write operations.
Any help regrading this will be appreciated.
Related
I have wrote simple socket programming in c in for Handle multiple socket connections client and sever.
But when I run it, client showed that connection refused error.
Server side
/**
Handle multiple socket connections with select and fd_set on Linux
*/
#include <stdio.h>
#include <string.h> //strlen
#include <stdlib.h>
#include <errno.h>
#include <unistd.h> //close
#include <arpa/inet.h> //close
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <sys/time.h> //FD_SET, FD_ISSET, FD_ZERO macros
#define TRUE 1
#define FALSE 0
#define PORT 8889
int main(int argc , char *argv[])
{
int opt = TRUE;
int master_socket , addrlen , new_socket , client_socket[30] , max_clients = 30 , activity, i , valread , sd;
int max_sd;
struct sockaddr_in address;
char buffer[1025]; //data buffer of 1K
//set of socket descriptors
fd_set readfds;
//a message
char *message = "ECHO Daemon v1.0 \r\n";
//initialise all client_socket[] to 0 so not checked
for (i = 0; i < max_clients; i++)
{
client_socket[i] = 0;
}
//create a master socket
if( (master_socket = socket(AF_INET , SOCK_STREAM , 0)) == 0)
{
perror("socket failed");
exit(EXIT_FAILURE);
}
//set master socket to allow multiple connections , this is just a good habit, it will work without this
if( setsockopt(master_socket, SOL_SOCKET, SO_REUSEADDR, (char *)&opt, sizeof(opt)) < 0 )
{
perror("setsockopt");
exit(EXIT_FAILURE);
}
//type of socket created
address.sin_family = AF_INET;
address.sin_addr.s_addr = INADDR_ANY;
address.sin_port = htons( PORT );
//bind the socket to localhost port 8888
if (bind(master_socket, (struct sockaddr *)&address, sizeof(address))<0)
{
perror("bind failed");
exit(EXIT_FAILURE);
}
printf("Listener on port %d \n", PORT);
//try to specify maximum of 3 pending connections for the master socket
if (listen(master_socket, 3) < 0)
{
perror("listen");
exit(EXIT_FAILURE);
}
//accept the incoming connection
addrlen = sizeof(address);
puts("Waiting for connections ...");
while(TRUE)
{
//clear the socket set
FD_ZERO(&readfds);
//add master socket to set
FD_SET(master_socket, &readfds);
max_sd = master_socket;
//add child sockets to set
for ( i = 0 ; i < max_clients ; i++)
{
//socket descriptor
sd = client_socket[i];
//if valid socket descriptor then add to read list
if(sd > 0)
FD_SET( sd , &readfds);
//highest file descriptor number, need it for the select function
if(sd > max_sd)
max_sd = sd;
}
//wait for an activity on one of the sockets , timeout is NULL , so wait indefinitely
activity = select( max_sd + 1 , &readfds , NULL , NULL , NULL);
if ((activity < 0) && (errno!=EINTR))
{
printf("select error");
}
//If something happened on the master socket , then its an incoming connection
if (FD_ISSET(master_socket, &readfds))
{
if ((new_socket = accept(master_socket, (struct sockaddr *)&address, (socklen_t*)&addrlen))<0)
{
perror("accept");
exit(EXIT_FAILURE);
}
//inform user of socket number - used in send and receive commands
printf("New connection , socket fd is %d , ip is : %s , port : %d \n" , new_socket , inet_ntoa(address.sin_addr) , ntohs(address.sin_port));
//send new connection greeting message
if( send(new_socket, message, strlen(message), 0) != strlen(message) )
{
perror("send");
}
puts("Welcome message sent successfully");
//add new socket to array of sockets
for (i = 0; i < max_clients; i++)
{
//if position is empty
if( client_socket[i] == 0 )
{
client_socket[i] = new_socket;
printf("Adding to list of sockets as %d\n" , i);
break;
}
}
}
//else its some IO operation on some other socket :)
for (i = 0; i < max_clients; i++)
{
sd = client_socket[i];
if (FD_ISSET( sd , &readfds))
{
//Check if it was for closing , and also read the incoming message
if ((valread = read( sd , buffer, 1024)) == 0)
{
//Somebody disconnected , get his details and print
getpeername(sd , (struct sockaddr*)&address , (socklen_t*)&addrlen);
printf("Host disconnected , ip %s , port %d \n" , inet_ntoa(address.sin_addr) , ntohs(address.sin_port));
//Close the socket and mark as 0 in list for reuse
close( sd );
client_socket[i] = 0;
}
//Echo back the message that came in
else
{
//set the string terminating NULL byte on the end of the data read
buffer[valread] = '\0';
send(sd , buffer , strlen(buffer) , 0 );
}
}
}
}
return 0;
}
Client side
// Client side C/C++ program to demonstrate Socket programming
#include <stdio.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <unistd.h>
#include <string.h>
#define PORT 8889
int main(int argc, char const *argv[])
{
int sock = 0, valread;
struct sockaddr_in serv_addr;
char *hello = "Hello from client";
char buffer[1024] = {0};
if ((sock = socket(AF_INET, SOCK_STREAM, 0)) < 0)
{
printf("\n Socket creation error \n");
return -1;
}
serv_addr.sin_family = AF_INET;
serv_addr.sin_port = htons(PORT);
// Convert IPv4 and IPv6 addresses from text to binary form
if(inet_pton(AF_INET, "127.0.0.1", &serv_addr.sin_addr)<=0)
{
printf("\nInvalid address/ Address not supported \n");
return -1;
}
if (connect(sock, (struct sockaddr *)&serv_addr, sizeof(serv_addr)) < 0)
{
printf("\nConnection Failed \n");
return -1;
}
send(sock , hello , strlen(hello) , 0 );
printf("Hello message sent\n");
valread = read( sock , buffer, 1024);
printf("%s\n",buffer );
return 0;
}
When I compile and run the code getting Connection refused
Compile
gcc server1.c -o server1
gcc client2.c -o client2
Run
./client2 127.0.0.1
Client Error: Connection Failed.: Connection refused
binded successfully
I am currently trying to create a multiple client-server which enables server to perform both read and write functions in C language. I am able to read the data from the client using readfds and putting it as parameter in SELECT. When I added in writefds, the client fails to connect to the server. I am not sure what is the issue behind it, whether this is the correct method to transmit and receive data
This is the code for my server
#include <stdio.h>
#include <string.h> //strlen
#include <stdlib.h>
#include <errno.h>
#include <unistd.h> //close
#include <arpa/inet.h> //close
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <sys/time.h> //FD_SET, FD_ISSET, FD_ZERO macros
#define TRUE 1
#define FALSE 0
#define PORT 8080
int main(int argc , char *argv[])
{
int opt = TRUE;
int master_socket , addrlen , new_socket , client_socket[30] ,
max_clients = 2 , activity, i , valread , sd;
int max_sd;
struct sockaddr_in address;
char buffer[1025]; //data buffer of 1K
fd_set readfds;
fd_set writefds;
char *message = "Welcome to Server\r\n";
//initialise all client_socket[] to 0 so not checked
for (i = 0; i < max_clients; i++)
{
//client_socket[i] = 0;
}
//create a master socket
if( (master_socket = socket(AF_INET , SOCK_STREAM , 0)) == 0)
{
perror("socket failed");
exit(EXIT_FAILURE);
}
if( setsockopt(master_socket, SOL_SOCKET, SO_REUSEADDR, (char *)&opt,
sizeof(opt)) < 0 )
{
perror("setsockopt");
exit(EXIT_FAILURE);
}
//type of socket created
address.sin_family = AF_INET;
address.sin_addr.s_addr = INADDR_ANY;
address.sin_port = htons( PORT );
//bind the socket to localhost port 8888
if (bind(master_socket, (struct sockaddr *)&address, sizeof(address))<0)
{
perror("bind failed");
exit(EXIT_FAILURE);
}
printf("Listener on port %d \n", PORT);
if (listen(master_socket, 3) < 0)
{
perror("listen");
exit(EXIT_FAILURE);
}
addrlen = sizeof(address);
puts("Waiting for connections ...");
for(;;)
{
//clear the socket set
FD_ZERO(&readfds);
FD_ZERO(&writefds);
//add master socket to set
FD_SET(master_socket, &readfds);
FD_SET(master_socket, &writefds);
max_sd = master_socket;
//add child sockets to set
for ( i = 0 ; i < max_clients ; i++)
{
//socket descriptor
sd = client_socket[i];
//if valid socket descriptor then add to read list
if(sd > 0)
FD_SET( sd , &readfds);
//highest file descriptor number, need it for the select function
if(sd > max_sd)
max_sd = sd;
}
//wait for an activity on one of the sockets , timeout is NULL ,
//so wait indefinitely
activity = select( max_sd + 1 , &readfds , &writefds, NULL , NULL);
if ((activity < 0) && (errno!=EINTR))
{
printf("select error");
}
if (FD_ISSET(master_socket, &readfds))
{
if ((new_socket = accept(master_socket,
(struct sockaddr *)&address, (socklen_t*)&addrlen))<0)
{
perror("accept");
exit(EXIT_FAILURE);
}
else {
printf("Connected to socket \n");
}
bzero(buffer, 1025);
int n=0;
read(new_socket, buffer, sizeof(buffer));
printf("From client: %s\t To client: ", buffer);
bzero(buffer, 1025);
//add new socket to array of sockets
for (i = 0; i < max_clients; i++)
{
//if position is empty
if( client_socket[i] == 0 )
{
client_socket[i] = new_socket;
printf("Adding to list of sockets as %d\n" , i+1);
}
}
}
else if(FD_ISSET(master_socket, &readfds)) {
printf("From client: %s\t To client : ", buffer);
bzero(buffer, 1025);
// copy server message in the buffer
while ((buffer[n++] = getchar()) != '\n')
;
write(new_socket, buffer, sizeof(buffer));
}
else{
//else its some IO operation on some other socket
for (i = 0; i < max_clients; i++)
{
sd = client_socket[i];
if (FD_ISSET( sd , &readfds))
{
//Check if it was for closing , and also read the
//incoming message
if ((valread = read( sd , buffer, 1024)) == 0)
{
close( sd );
client_socket[i] = 0;
}
//Echo back the message that came in
else
{
}
}
}
}//else
}
return 0;
}
This is the code for the client
#include <netdb.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/socket.h>
#define MAX 1000
#define PORT 8080
#define SA struct sockaddr
void func(int sockfd)
{
char buff[MAX];
int n;
int firstConnect = 1;
for (;;)
{
bzero(buff, sizeof(buff));
n = 0;
if(firstConnect==1) {
printf("Enter the string : ");
firstConnect = 0;
while ((buff[n++] = getchar()) != '\n')
;
write(sockfd, buff, sizeof(buff));
}
bzero(buff, sizeof(buff));
read(sockfd, buff, sizeof(buff));
printf("From Server: %s\t To Server : ", buff);
while ((buff[n++] = getchar()) != '\n');
write(sockfd, buff, sizeof(buff));
if ((strncmp(buff, "exit", 4)) == 0)
{
printf("Client Exit...\n");
break;
}
}
}
int main()
{
printf("CLIENT");
int sockfd, connfd;
struct sockaddr_in servaddr, cli;
// socket create and varification
sockfd = socket(AF_INET, SOCK_STREAM, 0);
if (sockfd == -1)
{
printf("socket creation failed...\n");
exit(0);
}
else
printf("Socket successfully created..\n");
bzero(&servaddr, sizeof(servaddr));
// assign IP, PORT
servaddr.sin_family = AF_INET;
// servaddr.sin_addr.s_addr = inet_addr("127.0.0.1");
servaddr.sin_addr.s_addr = inet_addr("127.8.1.0");
servaddr.sin_port = htons(PORT);
// connect the client socket to server socket
if (connect(sockfd, (SA*)&servaddr, sizeof(servaddr)) != 0)
{
printf("connection with the server failed...\n");
exit(0);
}
else
printf("connected to the server..\n");
// function for chat
func(sockfd);
// close the socket
close(sockfd);
}
As side node, it appears that you expect read to zero-terminate received data. That is not the case. The length of received data is in the return value of read, it must not be ignored.
It also appears that you expect write to find the end of the string you are sending. That doesn't happen. You must specify the exact length of the data you are sending, rather than the length of the buffer that contains the data.
send/write and recv/read functions work on binary data rather than zero-terminated strings and can send/receive less than the specified size or may return an error. Learn how to use these functions correctly by not ignoring the return value and handling errors and partial reads and writes.
#include <stdio.h>
#include <ctype.h>
#include <string.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
int port = 3008;
int listenfd;
extern void makelistener();
int main(int argc, char **argv)
{
makelistener();
int clientfd, nready;
socklen_t len;
struct sockaddr_in q;
int i;
// initialize allset and add listenfd to the
// set of file descriptors passed into select
fd_set allset;
fd_set rset;
int maxfd;
FD_ZERO(&allset);
FD_SET(listenfd, &allset); // set of file descriptors
maxfd = listenfd;
int ret;
while (1)
{
// make a copy of the set before we pass it into select
rset = allset;
/*select will wait until an exceptional event occurs when tv is NULL*/
nready = select(maxfd + 1, &rset, NULL, NULL, NULL);
if (nready == 0) {
continue;
}
if (nready == -1) {
perror("select");
continue;
}
//FD_ISSET returns 1 when a new connection is attempted
if(FD_ISSET(listenfd, &rset)){
//printf("a new client is connecting\n");
len = sizeof(q); //accept connection of listenfd stream socket
if ((clientfd = accept(listenfd, (struct sockaddr *)&q, &len)) < 0) {
perror("accept");
exit(1);
}
FD_SET(clientfd, &allset);
if (clientfd > maxfd) {
maxfd = clientfd;
}
static char msg[] = "What is your name?\r\n";
write(clientfd, msg, sizeof msg - 1);
printf("connection from %s\n", inet_ntoa(q.sin_addr));
char buf[256];
ret = read(clientfd, buf, sizeof(buf));
buf[ret] = '\0';
printf("%s", buf);
}
}
}
void makelistener()
{
struct sockaddr_in r;
if ((listenfd = socket(AF_INET, SOCK_STREAM, 0)) < 0) {
perror("socket");
exit(1);
}
memset(&r, '\0', sizeof r);
r.sin_family = AF_INET;
r.sin_addr.s_addr = INADDR_ANY;
r.sin_port = htons(port);
if (bind(listenfd, (struct sockaddr *)&r, sizeof r)) {
perror("bind");
exit(1);
};
if (listen(listenfd, 5)) {
perror("listen");
exit(1);
}
}
above code is for the server and it does this
$ ./above.c
(does nothing but runs forever)
How to connect as a client:
$ nc 127.0.0.1 3000
What is your name?
(waiting for my input) so if I put bob, it would output it to the server
It works as intended. But I want it too work concurrently with multiple clients.
for example:
server
$ ./above.c
(does nothing but runs forever)
client1
$ nc 127.0.0.1 3000
What is your name?
client 2
$ nc 127.0.0.1 3000
What is your name? (Currently client2 wont show up until client1 is answered which is what I'm trying to fix)
How do I make it so the clients can run concurrently without waiting for the first client to finish? To explain the code a little bit, listener just binds and listens to a connection. Inside the while(1) is where select and calls are.
How do I make it so the clients can run concurrently without waiting for the first client to finish?
By paying attention to which sockets select() reports to you. You are asking select() to monitor multiple sockets for readability, but you are only checking if the listening socket is readable, you are not checking the client sockets. You need to keep track of the connected clients so you can enumerate them when needed.
Try something like this:
#include <stdio.h>
#include <ctype.h>
#include <string.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
int port = 3008;
#define MAX_CLIENTS (FD_SETSIZE - 1)
int listenfd = -1;
extern void makelistener();
int main(int argc, char **argv)
{
int clientfd, nready;
socklen_t len;
struct sockaddr_in q;
int i, j, ret;
fd_set allset;
fd_set rset;
int clients[MAX_CLIENTS];
int num_clients = 0;
int maxfd;
char buf[256];
makelistener();
// initialize allset and add listenfd to the
// set of file descriptors passed into select
FD_ZERO(&allset);
FD_SET(listenfd, &allset);
maxfd = listenfd;
while (1)
{
// make a copy of the set before we pass it into select
FD_COPY(&allset, &rset);
// select will wait until an exceptional event occurs when tv is NULL
nready = select(maxfd + 1, &rset, NULL, NULL, NULL);
if (nready < 0) {
perror("select");
continue;
}
if (nready == 0) { // should never happen since no timeout was requested
continue;
}
//FD_ISSET returns 1 when a socket is readable
if (FD_ISSET(listenfd, &rset)) {
//printf("a new client is connecting\n");
len = sizeof(q); //accept connection of listenfd stream socket
if ((clientfd = accept(listenfd, (struct sockaddr *)&q, &len)) < 0) {
perror("accept");
exit(1);
}
printf("Client %d connected from %s\n", clientfd, inet_ntoa(q.sin_addr));
if (num_clients == MAX_CLIENTS) {
static char msg[] = "Max number of clients are already connected\r\n";
write(clientfd, msg, sizeof(msg)-1);
close(clientfd);
}
else {
static char msg[] = "What is your name?\r\n";
if (write(clientfd, msg, sizeof(msg)-1) < 0) {
close(clientfd);
}
else {
clients[num_clients++] = clientfd;
FD_SET(clientfd, &allset);
if (clientfd > maxfd) {
maxfd = clientfd;
}
}
}
}
for (i = 0; i < num_clients; ++i) {
clientfd = clients[i];
if (!FD_ISSET(clientfd, &rset)) {
continue;
}
ret = read(clientfd, buf, sizeof(buf));
if (ret <= 0) {
//printf("a client has disconnected\n");
close(clientfd);
FD_CLR(clientfd, &allset);
for (j = i + 1; j < num_clients; ++j) {
clients[j-1] = clients[j];
}
--num_clients;
if (clientfd == maxfd) {
maxfd = listenfd;
for (j = 0; j < num_clients; ++j) {
if (clients[j] > maxfd) {
maxfd = clients[j];
}
}
}
--i;
continue;
}
printf("Client %d: %.*s", clientfd, ret, buf);
}
}
return 0;
}
Note that poll() or epoll() would generally be a better choice to use than select().
How do I make it so the clients can run concurrently without waiting for the first client to finish?
every time that the call to accept() returns, start a thread pthread_create() to handle the actual communication with the client.
Note: creating/destroying a thread is very time consuming, so suggest learning about thread pools and how to use them.
When using threads, there is no call to select() (nor poll()) Rather the main function gets blocked on the call to accept() and when that function returns, pass the associated socket to a thread to handle
There are lots of example code for how a server should communicate with multiple clients on stackoverflow.com
I wrote up this udp receive C code to listen to data packets being streamed and it seems like the code is not entering the while loop: The output of the code is as following and then it stops there with no error although the port and ip are both defined correctly and send data works:
Initialising Winsock...Initialised.
This is the socket that was created: 128
Socket created.
connects to the socket ; it then stays here and doesnt go into the loop..
Code :
#include<stdio.h>
#include <stdlib.h>
#include <stdbool.h>
#include <winsock2.h>
#include <fcntl.h>
#pragma comment(lib, "Ws2_32.lib")
#pragma comment(lib, "wsock32.lib")
int main(int argc , char *argv[])
{
printf("started \n");
WSADATA wsa;
SOCKET s;
struct sockaddr_in server, client_addr;
int recv_size =8192;// 2000000;
char ser[recv_size] ;
int count = 0;
int addr_len;
int bytes;
char *message , server_reply[recv_size];
printf("\nInitialising Winsock...");
if (WSAStartup(MAKEWORD(2,2),&wsa) != 0)
{
printf("Failed. Error Code : %d",WSAGetLastError());
return 1;
}
printf("Initialised.\n");
if((s = socket(AF_INET , SOCK_DGRAM , 0 )) == INVALID_SOCKET)
{ printf("Could not create socket : %d" , WSAGetLastError());
}else{ printf("This is the socket that was created: %d \n", s); }
printf("Socket created.\n");
server.sin_addr.s_addr = inet_addr("192.168.10.103");
server.sin_family = AF_INET;
server.sin_port = htonl(25000);
addr_len = sizeof(struct sockaddr);
if (connect(s, (struct sockaddr *)&server, sizeof(server))<0)
{ puts("connect error");return 1 ;
}else {puts("connects to the socket"); }
while(1) {
// if ((recv_size = recv(s, server_reply, recv_size, 0)) == SOCKET_ERROR) {
// puts("failed at receive");
// }
puts("receiving from microzed");
bytes = recvfrom(s,ser,recv_size,0,(struct sockaddr *)&client_addr,&addr_len);
//ser[bytes] = '\0';
write(1,ser,bytes);
// printf("count: %d \n ",count = count+ 1);
// printf("data received: %d \n",ser);
}
puts("works \n");
// server_reply[recv_size] = '\0';
// printf("This is what the message reply is : %d, \n ",server_reply);
printf("finished \n");
return 0;
}
I'm going to assume that it's some kind of a 'race condition' between puts() and the recv(), since nothing else shows up, it's obvious that the recvfrom() is blocking, otherwise, you'd see the rest of the output (i.e "work \n").
a easy way to test that would be setting the mode of the socket to NON_BLOCKING.
I've tried running the code with MS Visual 2015 and it runs without issues.
Output:
started
Initialising Winsock...Initialised. This is the socket that was
created: 200 Socket created. connects to the socket receiving from
microzed
I have a client which is working fine, but whenever I run a new client, sometimes I don't receive the sent message on the other client already running, while using telnet it works flawlessly, the message "broadcasts" to all connected clients, and I want whenever a message is received to one of the clients to show even if I didn't already send a message.
Should I use select on clients ? and what should be changed ?
client.c:
#include <stdio.h> //printf
#include <string.h> //strlen
#include <sys/socket.h> //socket
#include <arpa/inet.h> //inet_addr
#include <unistd.h>
int main(int argc , char *argv[]){
int sock;
struct sockaddr_in server;
char message[256] , server_reply[256];
//Create socket
sock = socket(AF_INET , SOCK_STREAM , 0);
if (sock == -1)
{
printf("Could not create socket");
}
puts("Socket created");
server.sin_addr.s_addr = inet_addr("127.0.0.1");
server.sin_family = AF_INET;
server.sin_port = htons( 9034 );
//Connect to remote server
if (connect(sock , (struct sockaddr *)&server , sizeof(server)) < 0){
perror("connect failed. Error");
return 1;
}
puts("Connected\n");
//keep communicating with server
for(;;){
printf("Enter message: ");
memset(message, 0, 256);
fgets(message, 256,stdin);
// scanf("%s" , message);
//Send some data
if( send(sock , message , strlen(message) , 0) < 0)
{
puts("Send failed");
return 1;
}
//Receive a reply from the server
if( recv(sock , server_reply , 256 , 0) < 0)
{
puts("recv failed");
break;
}
printf("Server Reply: %s\n", server_reply);
server_reply[0]='\0';
}
close(sock);
return 0;
}
server.c:
/*
** selectserver.c -- a cheezy multiperson chat server
*/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <netdb.h>
#define PORT "9034" // port we're listening on
// get sockaddr, IPv4 or IPv6:
void *get_in_addr(struct sockaddr *sa)
{
if (sa->sa_family == AF_INET) {
return &(((struct sockaddr_in*)sa)->sin_addr);
}
return &(((struct sockaddr_in6*)sa)->sin6_addr);
}
int main(void){
fd_set master; // master file descriptor list
fd_set read_fds; // temp file descriptor list for select()
int fdmax; // maximum file descriptor number
int listener; // listening socket descriptor
int newfd; // newly accept()ed socket descriptor
struct sockaddr_storage remoteaddr; // client address
socklen_t addrlen;
char buf[256]; // buffer for client data
int nbytes;
char remoteIP[INET6_ADDRSTRLEN];
int yes=1; // for setsockopt() SO_REUSEADDR, below
int i, j, rv;
struct addrinfo hints, *ai, *p;
FD_ZERO(&master); // clear the master and temp sets
FD_ZERO(&read_fds);
// get us a socket and bind it
memset(&hints, 0, sizeof hints);
hints.ai_family = AF_UNSPEC;
hints.ai_socktype = SOCK_STREAM;
hints.ai_flags = AI_PASSIVE;
if ((rv = getaddrinfo(NULL, PORT, &hints, &ai)) != 0) {
fprintf(stderr, "selectserver: %s\n", gai_strerror(rv));
exit(1);
}
for(p = ai; p != NULL; p = p->ai_next) {
listener = socket(p->ai_family, p->ai_socktype, p->ai_protocol);
if (listener < 0) {
continue;
}
// lose the pesky "address already in use" error message
setsockopt(listener, SOL_SOCKET, SO_REUSEADDR, &yes, sizeof(int));
if (bind(listener, p->ai_addr, p->ai_addrlen) < 0) {
close(listener);
continue;
}
break;
}
// if we got here, it means we didn't get bound
if (p == NULL) {
fprintf(stderr, "selectserver: failed to bind\n");
exit(2);
}
freeaddrinfo(ai); // all done with this
// listen
if (listen(listener, 10) == -1) {
perror("listen");
exit(3);
}
// add the listener to the master set
FD_SET(listener, &master);
// keep track of the biggest file descriptor
fdmax = listener; // so far, it's this one
// main loop
for(;;) {
read_fds = master; // copy it
if (select(fdmax+1, &read_fds, NULL, NULL, NULL) == -1) {
perror("select");
exit(4);
}
// run through the existing connections looking for data to read
for(i = 0; i <= fdmax; i++) {
if (FD_ISSET(i, &read_fds)) { // we got one!!
if (i == listener) {
// handle new connections
addrlen = sizeof remoteaddr;
newfd = accept(listener,
(struct sockaddr *)&remoteaddr,
&addrlen);
if (newfd == -1) {
perror("accept");
} else {
FD_SET(newfd, &master); // add to master set
if (newfd > fdmax) { // keep track of the max
fdmax = newfd;
}
printf("selectserver: new connection from %s on "
"socket %d\n",
inet_ntop(remoteaddr.ss_family,
get_in_addr((struct sockaddr*)&remoteaddr),
remoteIP, INET6_ADDRSTRLEN),
newfd);
}
} else {
// handle data from a client
memset(buf, 0, 256);
if ((nbytes = recv(i, buf, sizeof buf, 0)) <= 0) {
// got error or connection closed by client
if (nbytes == 0) {
// connection closed
printf("selectserver: socket %d hung up\n", i);
} else {
perror("recv");
}
close(i); // bye!
FD_CLR(i, &master); // remove from master set
} else {
// we got some data from a client
for(j = 0; j <= fdmax; j++) {
// send to everyone!
if (FD_ISSET(j, &master)) {
// except the listener and ourselves
if (j != listener && j != i) {
if (send(j, buf, nbytes, 0) == -1) {
perror("send");
}
}
}
}
}
} // END handle data from client
} // END got new incoming connection
} // END looping through file descriptors
} // END for(;;)--and you thought it would never end!
return 0;
}
The reason a client can't receive a message until they send one is because.
fgets(message, 256,stdin);
Will keep "reading" (and will therefore block) until an EOF or a newline character has been read from the input stream
Also, note that
if( recv(sock , server_reply , 256 , 0) < 0)
blocks if there is nothing to read, which will prevent that user from sending more messages to the server until there is something new to read from the server. Assuming that you've played online games before, I hope that you can see that such a setup would be rather annoying!
So, we have to find someway of checking to see if we can read from STDIN and the server socket without incurring a block. Using select() will prevent us blocking on the sever socket, but it wouldn't work for STDIN whilst using fgets() to read input from the user. This is because, as mentioned above, fgets() blocks until an EOF or newline is detected.
The main solution I have in mind is to replace fgets with a method buffer_message() that will only read from STDIN when it won't block on read (we'll use select() to implement this). We'll then place what is read into a buffer. If there is a full message, this message will then be written to the server. Otherwise, we'll let the control keep going through the program until there is something to read or write.
This is code from a recent university assignment I did and so a small portion of the code isn't mine
Declarations:
//directives are above (e.g. #include ...)
//message buffer related delcartions/macros
int buffer_message(char * message);
int find_network_newline(char * message, int inbuf);
#define COMPLETE 0
#define BUF_SIZE 256
static int inbuf; // how many bytes are currently in the buffer?
static int room; // how much room left in buffer?
static char *after; // pointer to position after the received characters
//main starts below
Main:
//insert the code below into main, after you've connected to the server
puts("Connected\n");
//set up variables for select()
fd_set all_set, r_set;
int maxfd = sock + 1;
FD_ZERO(&all_set);
FD_SET(STDIN_FILENO, &all_set); FD_SET(sock, &all_set);
r_set = all_set;
struct timeval tv; tv.tv_sec = 2; tv.tv_usec = 0;
//set the initial position of after
after = message;
puts("Enter message: ");
//keep communicating with server
for(;;){
r_set = all_set;
//check to see if we can read from STDIN or sock
select(maxfd, &r_set, NULL, NULL, &tv);
if(FD_ISSET(STDIN_FILENO, &r_set)){
if(buffer_message(message) == COMPLETE){
//Send some data
if(send(sock, message, strlen(message) + 1, 0) < 0)//NOTE: we have to do strlen(message) + 1 because we MUST include '\0'
{
puts("Send failed");
return 1;
}
puts("Enter message:");
}
}
if(FD_ISSET(sock, &r_set)){
//Receive a reply from the server
if( recv(sock , server_reply , 256 , 0) < 0)
{
puts("recv failed");
break;
}
printf("\nServer Reply: %s\n", server_reply);
server_reply[0]='\0';
}
}
close(sock);
return 0;
//end of main
Buffer functions:
int buffer_message(char * message){
int bytes_read = read(STDIN_FILENO, after, 256 - inbuf);
short flag = -1; // indicates if returned_data has been set
inbuf += bytes_read;
int where; // location of network newline
// Step 1: call findeol, store result in where
where = find_network_newline(message, inbuf);
if (where >= 0) { // OK. we have a full line
// Step 2: place a null terminator at the end of the string
char * null_c = {'\0'};
memcpy(message + where, &null_c, 1);
// Step 3: update inbuf and remove the full line from the clients's buffer
memmove(message, message + where + 1, inbuf - (where + 1));
inbuf -= (where+1);
flag = 0;
}
// Step 4: update room and after, in preparation for the next read
room = sizeof(message) - inbuf;
after = message + inbuf;
return flag;
}
int find_network_newline(char * message, int bytes_inbuf){
int i;
for(i = 0; i<inbuf; i++){
if( *(message + i) == '\n')
return i;
}
return -1;
}
P.S.
if( send(sock , message , strlen(message) , 0) < 0)
The above can also block if there's no space to write to the server, but there's no need to worry about that here. Also, I'd like to point out a few things you should implement for your client and your server:
Whenever you send data over a network, the standard newline is \r\n, or carriage return / newline, or simply the network newline. All messages sent between the client and the server should have this appended at the end.
You should be buffering all data sent between the server and the client. Why? Because you're not guaranteed to receive all packets in a message in a single read of a socket. I don't have time to find a source, but when using TCP/IP, packets for a message/file don't have to arrive together, meaning that if you do read, you may not be reading all of the data you intend to read. I'm not well versed in this, so please investigate this more. Open to having this edited / corrected