I'm studying Linux/UNIX sockets, so I wrote a very simple "game" based on it, called "21 matches". There's a heap that consists of 21 matches, and each player takes one, two or three matches from it. The person who takes the last match loses the game.
Obviously, the key to winning is to complement your opponent's turn up to 4 matches, so he has to take the very last match (works only if you do the first turn). So, client connects to the server and "plays" against the server till he loses. There can be more than one client connected, so I limit the number of connection, making my host reject any further ones.
The only thing I can't fix or explain is that when someone's getting rejected, the very first client loses the game immediately, even if he hasn't made any turns. This might be explained if I let the new client touch any client's heap, but I don't! I also tracked the buffer array, but it doesn't get harmed in any way.
Here's the code:
#include <sys/types.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <netinet/in.h>
#include <stdio.h>
#include <poll.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
void die (const char *s, int errcode);
int mkservsock();
void acceptclient();
void dropclient (int client);
void make_turn (int client);
enum
{
port = 3333,
buf_s = 100,
limit = 3
};
void die (const char *s, int errcode)
{
perror (s);
exit (errcode);
}
struct pollfd fds[limit + 1]; // client socket descriptors array
int left[limit + 1]; // how many matches left
int nfd = 1; // number of the next client
char buffer[buf_s]; // buffer for communicating
// creates a single socket to poll
int mkservsock()
{
int s = socket (AF_INET, SOCK_STREAM, 0);
struct sockaddr_in addr;
addr.sin_family = AF_INET;
addr.sin_port = htons (port);
addr.sin_addr.s_addr = INADDR_ANY;
if (bind (s, (struct sockaddr *)&addr, sizeof(addr)) == -1)
die ("Can't bind socket", 1);
if (listen (s, 1) == -1)
die ("Can't start listening", 1);
return s;
}
// adds new client to descriptors array or rejects it, if number of connections exceeds the limit
void acceptclient()
{
int c = accept (fds[0].fd, 0, 0);
fds[nfd].fd = c;
fds[nfd].events = POLLIN;
if (nfd == limit + 1)
{
sprintf (buffer, "Server is busy, try again later\n");
send (fds[nfd].fd, buffer, strlen (buffer), 0);
close (fds[nfd].fd);
return;
} else
{
left[nfd] = 21;
sprintf (buffer, "Matches available: %d\nTake 1, 2 or 3!\n", left[nfd]);
send (fds[nfd].fd, buffer, strlen (buffer), 0);
nfd++;
}
}
// disconnects a client in case of match ending or inappropriate data sent
void dropclient (int client)
{
int i, j;
close (fds[client].fd);
for (i = client; i < nfd - 1; i++)
{
fds[i] = fds[i + 1];
left[i] = left[i + 1];
}
nfd--;
}
void make_turn (int client)
{
int n = recv (fds[client].fd, buffer, buf_s, 0);
if (n == 0) {
dropclient (client);
return;
} else if (n > 3)
{
// input counts as incorrect if it contains more than 1 symbol,
// since we expect a single digit and nothing else
// (yep, we get two extra bytes when receiving a message)
sprintf (buffer, "I can break rules, too. Goodbye.\n");
send (fds[client].fd, buffer, strlen (buffer), 0);
dropclient (client);
return;
}
// way to extract a digit from the character
int received = buffer[0] - '0';
if (received > 3 || received <= 0)
{
sprintf (buffer, "You're allowed to take 1, 2 or 3 matches only\n");
send (fds[client].fd, buffer, strlen (buffer), 0);
return;
} else if (received > left[client])
{
sprintf (buffer, "You can't take more than %d\n", left[client]);
send (fds[client].fd, buffer, strlen (buffer), 0);
return;
} else
{
// obviously, it happens only when there's the only match,
// and the client has to take it
if (left[client] == received)
{
sprintf (buffer, "You lost!\n");
send (fds[client].fd, buffer, strlen (buffer), 0);
dropclient (client);
return;
} else
{
// sort of "keeping the game up"
left[client] -= 4;
sprintf (buffer, "Matches left: %d (I took %d)\n", left[client], 4 - received);
send (fds[client].fd, buffer, strlen (buffer), 0);
return;
}
}
}
int main (int argc, char *argv[])
{
fds[0].fd = mkservsock ();
fds[0].events = POLLIN;
for (;;)
{
int status, i;
status = poll (fds, nfd, -1);
if (status == -1)
die ("Error while polling", 1);
if (fds[0].revents & POLLIN)
acceptclient();
for (i = 1; i < nfd; i++)
{
if (fds[i].revents & POLLERR)
{
printf ("Got troubles on %d\n", i);
continue;
}
if (fds[i].revents & POLLIN)
make_turn (i);
}
}
}
And here's the thing happening to the first client after reaching max. number of connections:
Matches available: 21
Take 1, 2 or 3!
(don't take anything, meanwhile someone connects and gets rejected)
(after this, post any number and it'll say that there's only one match in the heap)
1
You lost!
Note that you lose if and only if your input is equal to a number of matches left. So, what's going on?
Copyright Notice:Content Author:「a small orange」,Reproduced under the CC 4.0 BY-SA copyright license with a link to the original source and this disclaimer.
Link to original article:https://stackoverflow.com/questions/13377305/strange-program-behaviour-after-closing-a-linux-socket