2

我正在研究Linux/UNIX套接字,所以我基于它写了一个非常简单的“游戏”,叫做“21匹配”。有一个由 21 场比赛组成的堆,每个玩家从中取一场、两场或三场比赛。拿下最后一场比赛的人输掉比赛。

显然,获胜的关键是在对手的回合中补充最多 4 场比赛,因此他必须参加最后一场比赛(仅当您进行第一回合时才有效)。因此,客户端连接到服务器并与服务器“玩”直到他输了。可以连接多个客户端,因此我限制了连接数,使我的主机拒绝任何其他客户端。

我唯一无法解决或解释的问题是,当有人被拒绝时,第一个客户会立即输掉比赛,即使他没有转弯。如果我让新客户端接触任何客户端的堆,这可能会得到解释,但我没有!我还跟踪了缓冲区数组,但它没有受到任何损害。

这是代码:

#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);
    }
  }
}

这是第一个客户在达到最大值后发生的事情。连接数:

Matches available: 21
Take 1, 2 or 3!

(不要拿任何东西,同时有人连接并被拒绝)

(在此之后,发布任何数字,它会说堆中只有一个匹配项)

1
You lost!

请注意,当且当您的输入等于剩余的匹配数时,您才会输。发生什么了?

4

1 回答 1

0

你覆盖left[], 当你打电话时acceptclient(),不要检查是否有空间fds来存储新的连接。

您首先初始化新客户端fds,然后拒绝连接,但为时已晚,因为您在数组之外写入了一个条目。除了数组之外,还有一个left[]数组,它现在存储了一个 1 或 0。因此,无论第一个客户如何回应,他总是选择最后一个。

于 2012-11-14T11:06:55.180 回答