I wrote a code about sockets and I cannot prevent the server to close when the client sends a message or when the client do a CTRL + C
The server goes off.
Server.c:
#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>
#define PORT 8888
#define BACKLOG 10
#define LOOP(i, n) for((i) = 0; (i) < (n); (i)++)
#define BUFFSIZE 6500
int create_fd( struct sockaddr_in *server );
int bind_fd ( struct sockaddr_in *server, int servSocket );
int list_fd( int servSocket );
int accept_fd( struct sockaddr_in *server, int servSocket );
int select_fd ( fd_set *readfds, int max_sd );
ssize_t send_fd( int new_fd, const char *const msg );
int main( void ){
const char msg[BUFFSIZE] = "Server is On\n";
char recive[BUFFSIZE];
int servSocket ,clientSocket ,i ,sd;
int client_socket_max[30] = { 0 } , max_clients = 30;
int max_sd;
ssize_t valread;
struct sockaddr_in server;
for (i = 0; i < max_clients; i++)
{
client_socket_max[i] = 0;
}
//set of socket descriptors
fd_set readfds;
size_t addr_size = sizeof(server);
//create
servSocket = create_fd( &server );
//bind the socket to localhost port 8888
bind_fd(&server, servSocket );
//listen the socket to localhost port 8888
printf("\tServer is On\nListener on port %d \n", PORT);
list_fd( servSocket );
//accept the incoming connection
printf( "\n\tWaiting for connections ...\n" );
while(1){
//clear the socket set
FD_ZERO(&readfds);
//add master socket to set
FD_SET(servSocket, &readfds);
max_sd = servSocket;
//add child sockets to set
LOOP( i, max_clients ){
//socket descriptor
sd = client_socket_max[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
select_fd( &readfds, max_sd );
//select( max_sd + 1 , &readfds , NULL , NULL , NULL);
//If something happened on the master socket , then its an incoming connection
if ( FD_ISSET( servSocket, &readfds ) ){
clientSocket = accept_fd( &server, servSocket );
//inform user of socket number - used in send and receive commands
printf("New connection , socket fd is %d , ip is : %s , port : %d \n" , clientSocket , inet_ntoa(server.sin_addr) , ntohs(server.sin_port));
//send new connection greeting msg
send_fd( clientSocket, msg );
/*if( send(clientSocket, msg, strlen(msg), 0) != (long int)strlen(msg) ){
perror("send");
}*/
puts("Welcome msg sent successfully");
//add new socket to array of sockets
LOOP( i, max_clients ){
//if position is empty
if( client_socket_max[i] == 0 ){
client_socket_max[i] = clientSocket;
printf("Adding to list of sockets as %d\n" , i);
break;
}
}
}
//else its some IO operation on some other socket :)
LOOP( i, max_clients ){
sd = client_socket_max[i];
if (FD_ISSET( sd , &readfds)){
//Check if it was for closing , and also read the incoming msg
if (( valread = read( sd , recive, 1024)) == 0){
//Somebody disconnected , get his details and print
getpeername(sd , (struct sockaddr*)&server , (socklen_t*)&addr_size);
printf("Host disconnected , ip %s , port %d \n" , inet_ntoa(server.sin_addr) , ntohs(server.sin_port));
//Close the socket and mark as 0 in list for reuse
close( sd );
client_socket_max[i] = 0;
}else{//Echo back the msg that came in
//set the string terminating NULL byte on the end of the data read
recive[valread] = '\0';
send(sd , recive , strlen(recive) , 0 );
}
}
}
}
close( servSocket );
}
int create_fd( struct sockaddr_in *server ){
int opt = 1;
int servSocket = socket(AF_INET , SOCK_STREAM , 0);
if (servSocket == -1 ){
printf("Error, socket()\n");
fprintf(stderr, "socket: %s (%d)\n", strerror(errno), errno);
exit ( EXIT_FAILURE );
}
//set master socket to allow multiple connections , this is just a good habit, it will work without this
if( setsockopt(servSocket, SOL_SOCKET, SO_REUSEADDR, (char *)&opt, sizeof(opt)) < 0 ){
perror("setsockopt");
exit(EXIT_FAILURE);
}
//type of socket created
server->sin_family = AF_INET;
server->sin_addr.s_addr = INADDR_ANY;
server->sin_port = htons( PORT );
//printf("socket() \tOK\n");
return servSocket;
}
int bind_fd ( struct sockaddr_in *server, int servSocket ){
int bindfd = bind( servSocket, (struct sockaddr *)server, sizeof(*server) );
if (bindfd == -1 ){
printf("Error, bind(), check line 34\n");
fprintf(stderr, "bind: %s (%d)\n", strerror(errno), errno);
exit ( EXIT_FAILURE );
}else{
//printf("bind() \t\tOK\n");
return bindfd;
}
}
int list_fd( int servSocket ){
int listfd = listen(servSocket, BACKLOG);
if (listfd == -1 ){
printf("Error, listen()\n");
fprintf(stderr, "listen: %s (%d)\n", strerror(errno), errno);
exit ( EXIT_FAILURE );
}else{
//printf("listen() \tOK\n");
return listfd;
}
}
int accept_fd( struct sockaddr_in *server, int servSocket ){
int new_fd;
socklen_t addr_size = sizeof( server );
new_fd = accept(servSocket, (struct sockaddr *)server, (socklen_t*)&addr_size);
if (new_fd == -1 ){
printf("Error, accept()\n");
fprintf(stderr, "accept: %s (%d)\n", strerror(errno), errno);
exit ( EXIT_FAILURE );
}else{
//printf("accept() \tOK\n");
return new_fd;
}
}
int select_fd ( fd_set *readfds, int max_sd ){
int activity = select( max_sd + 1 , readfds , NULL , NULL , NULL);
if ((activity < 0) && (errno!=EINTR)){
printf("select error");
exit ( EXIT_FAILURE );
}else{
return activity;
}
}
ssize_t send_fd( int new_fd, const char *const msg ){
size_t len = strlen(msg);
ssize_t sendfd;
sendfd = send( new_fd, msg, len, 0 );
if (sendfd == -1 ){
printf("Error, write()\n");
fprintf(stderr, "recv: %s (%d)\n", strerror(errno), errno);
exit ( EXIT_FAILURE );
}else{
printf("write() \tOK\n");
printf("Client sent:\t%s", msg );
return sendfd;
}
}
Client.c:
#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 PORT 8888
#define BUFFSIZE 6500
int create_fd( struct sockaddr_in server );
int connect_fd( const int servSocket, struct sockaddr_in *server );
int opt = 1;
int main ( void ){
struct sockaddr_in server;
char recivemsg[BUFFSIZE];
char sendmsg[BUFFSIZE];
memset( recivemsg, 0, sizeof(*recivemsg) );
memset( sendmsg, 0, sizeof(*sendmsg) );
int servSocket = socket(AF_INET , SOCK_STREAM , 0);
if (servSocket == -1 ){
printf("Error, socket()\n");
fprintf(stderr, "socket: %s (%d)\n", strerror(errno), errno);
exit ( EXIT_FAILURE );
}
//set master socket to allow multiple connections , this is just a good habit, it will work without this
if( setsockopt(servSocket, SOL_SOCKET, SO_REUSEADDR, (char *)&opt, sizeof(opt)) < 0 ){
perror("setsockopt");
exit(EXIT_FAILURE);
}
//type of socket created
server.sin_family = AF_INET;
server.sin_addr.s_addr = INADDR_ANY;
server.sin_port = htons( PORT );
if ( ( connect( servSocket, ( struct sockaddr* )&server, sizeof( server ) ) ) < 0 ){
printf("Error, connect()\n");
fprintf(stderr, "connect: %s (%d)\n", strerror(errno), errno);
exit( EXIT_FAILURE );
}
if ( recv( servSocket, recivemsg, sizeof ( recivemsg ), 0 ) < 0 ){
printf("Error, recv()\n");
fprintf(stderr, "recv: %s (%d)\n", strerror(errno), errno);
exit( EXIT_FAILURE );
}
printf("Send a msg to the Server:> ");
fgets(sendmsg, BUFFSIZE, stdin );
if ( send( servSocket, sendmsg, sizeof ( sendmsg ), 0 ) < 0 ){
printf("Error, send()\n");
fprintf(stderr, "send: %s (%d)\n", strerror(errno), errno);
exit( EXIT_FAILURE );
}
close( servSocket );
}
There are a few problems with the code that together could lead to premature exits from your server program.
The most important is that you don't check for errors from either your
read
orsend
calls.If the
read
call fails it will return-1
which you don't check for (and which will lead to you using-1
as index into your arrayreceive
, which is out of bounds and lead to undefined behavior). That will in turn lead to you callingsend
with a broken connection, which will cause the operating system to send aSIGPIPE
signal to your process.The default behavior of
SIGPIPE
is to terminate the process.The usual way to handle this is to ignore the
SIGPIPE
signal, as thensend
would return with an error (it returns-1
witherrno
set toEPIPE
) that you should handle.The common way to handle errors (from either
read
orsend
) is to simplyclose
your end of the connection. That's because most errors are simply not recoverable.