I'm busy with this for 2 days now and still don't understand it. What does select()
do in this code?
I know that if there is an incoming connection that can be accepted, the copy.fd_array[]
will contain ListenSocket
but when the while loop repeats it's still there. So how do we know if a client is disconnected? What does fd_set copy
contain after the select()
call?
fd_set master;
FD_ZERO(&master);
FD_SET(ListenSocket, &master);
while (1)
{
fd_set copy = master;
select(FD_SETSIZE, ©, NULL, NULL, NULL);
for (int i = 0; i < FD_SETSIZE; i )
{
// If new connection
if (FD_ISSET(ListenSocket, ©))
{
printf("[ ] New connection\n");
// Accept connection
SOCKET AcceptedClient = accept(ListenSocket, NULL, NULL);
FD_SET(AcceptedClient, &master);
// Send welcome message to client
char buff[128] = "Hello Client!";
send(AcceptedClient, buff, sizeof(buff), 0);
}
}
}
CodePudding user response:
I'm busy with this for 2 days now and still don't understand it.
It's no wonder that you don't understand the code: The code in the example is nonsense.
Checking the ListenSocket
should be done outside the for
loop. And FD_ISSET
must also be checked for the connections accepted using accept
.
The correct code inside the while
loop would look like this:
fd_set copy = master;
select(FD_SETSIZE, ©, NULL, NULL, NULL);
// If new connection
if (FD_ISSET(ListenSocket, ©))
{
...
}
for (int i = 0; i < FD_SETSIZE; i )
{
// If an existing connection has data
// or the connection has been closed
if ((i != ListenSocket) && FD_ISSET(i, ©))
{
nBytes = recv(i, buffer, maxBytes, 0);
// Connection dropped
if(nBytes < 1)
{
close(i); // other OSs (Linux, MacOS ...)
// closesocket(i); // Windows
FD_CLR(i, &master);
}
// Data received
else
{
...
}
}
}
I know that if there is an incoming connection that can be accepted, the
copy.fd_array[]
will containListenSocket
but when thewhile
loop repeats it's still there.
What does fd_set copy contain after the select() call?
First of all: Before calling select()
, copy.fd_array[]
must contain all socket handles that you are interested in. This means it must contain ListenSocket
and all handles returned by accept()
.
master.fd_array[]
contains all these handles, so fd_set copy = master;
will ensure that copy.fd_array[]
also contains all these handles.
select()
(with NULL
as last argument) will wait until at least one socket becomes "available". This means that it will wait until at least one of the following conditions is true:
- A connection accepted using
accept()
is closed by the other side - a connection accepted using
accept()
has data that can be received - there is a new connection that can be accepted using
accept(ListenSocket...)
As soon as one condition is fulfilled, select()
removes all other handles from copy.fd_array[]
:
ListenSocket
is removed fromcopy.fd_array[]
if there is no incoming connection- A handle returned by
accept()
is removed from that array if the connection has neither been closed nor new data is available
If two events happen the same time, copy.fd_array[]
will contain more than one handle.
You use FD_ISSET()
to check if some handle is still in the array.
So how do we know if a client is disconnected?
When you detect FD_ISSET(i, ©)
for a value i
that has been returned by accept()
, you must call recv()
(under Linux read()
would also work):
If recv()
returns 0 (or negative in the case of errors), the other computer has dropped the connection. You must call close()
(closesocket()
on Windows) and remove the handle from copy.fd_array[]
(this means: you must remove it from master.fd_array[]
because of the line fd_set copy = master;
).
If recv()
returns a positive value, this is the number of bytes that have been received.