Reputation:
I made a small client/server program where the server can handle multiple clients at the same time using the select()
function. When I run my program everything works fine until I kill a client program, when I kill a client program the server doesn't block at select()
but it just iterates in the while loop.
When I change the FD_SETSIZE
in select(FD_SETSIZE, ©Fds, NULL, NULL, NULL);
to master_socket + 1
it doesn't block so it works fine.
The GNU library says: The select function checks only the first nfds file descriptors. The usual thing is to pass FD_SETSIZE as the value of this argument. https://www.gnu.org/software/libc/manual/html_node/Waiting-for-I_002fO.html
Why do I get this bug? How can I pass in FD_SETSIZE
and still let the program to block at select()
?
Server:
#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>
int main()
{
system("clear");
printf("***SERVER STARTED***\n");
char message[125] = "Welcome to my server";
int master_socket = socket(AF_INET, SOCK_STREAM, 0);
fd_set masterFds, copyFds;
struct sockaddr_in address;
address.sin_family = AF_INET;
address.sin_addr.s_addr = INADDR_ANY;
address.sin_port = htons(8888);
socklen_t addrlen = sizeof address;
bind(master_socket, (struct sockaddr *)&address, sizeof(address));
listen(master_socket, 1);
FD_ZERO(&masterFds);
FD_SET(master_socket, &masterFds);
while (1)
{
copyFds = masterFds;
printf("Monitoring for connections...\n");
select(FD_SETSIZE, ©Fds, NULL, NULL, NULL); // When a socket wants to connect it's file descriptor gets stored in copyFds
for (int i = 0; i < FD_SETSIZE; i++)
{
if (FD_ISSET(i, ©Fds))
{
if (i == master_socket) // New connection
{
int accepted_socket = accept(master_socket, (struct sockaddr *)&address, &addrlen);
FD_SET(accepted_socket, &masterFds);
printf("[+] New connection accepted\n");
write(accepted_socket, message, sizeof(message)); // Send message to client
}
else
{
// Not finished yet
}
}
}
printf("End of while loop\n");
}
}
Client (same headers):
int main()
{
system("clear");
int clientSocket = socket(AF_INET, SOCK_STREAM, 0), x;
struct sockaddr_in serverAddress;
serverAddress.sin_family = AF_INET;
serverAddress.sin_port = htons(8888);
serverAddress.sin_addr.s_addr = INADDR_ANY;
connect(clientSocket, (struct sockaddr *)&serverAddress, sizeof(serverAddress));
char serverResponse[125];
read(clientSocket, serverResponse, sizeof(serverResponse));
printf("test: %s\n", serverResponse);
scanf("%d", &x);
close(clientSocket);
}
Upvotes: 3
Views: 141
Reputation: 212168
If a client closes its connection, you need to remove that fd from the set of descriptors that select
is watching. Otherwise, it will return immediately, expecting you to take some action on that fd
. In other words, add something like:
char buf[1024];
ssize_t nr = read(i, buf, sizeof buf);
switch( nr ){
case -1:
perror("read");
/* fallthru */
case 0:
if( close(i) ){
perror("close");
}
puts("Client connection closed");
FD_CLR(i, masterFds);
break;
default:
fwrite(buf, 1, nr, stdout);
fflush(stdout);
}
in the else
clause. (The "Not finished yet" section.)
Upvotes: 2