2

私は Linux/UNIX ソケットを研究しているので、それに基づいた「21 マッチ」という非常に単純な「ゲーム」を書きました。21 のマッチで構成されるヒープがあり、各プレイヤーはそこから 1 つ、2 つ、または 3 つのマッチを取ります。最後の試合を取った人がゲームに負けます。

明らかに、勝利への鍵は対戦相手のターンを最大 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 つだけであると表示されます)

1
You lost!

入力が残りの試合数と等しい場合にのみ負けることに注意してください。どうしたの?

4

1 に答える 1

0

left[]を呼び出すときに上書きし、新しい接続を保存するacceptclient()余地があるかどうかを確認しません。fds

最初に新しいクライアントfdsを初期化し、その後接続を拒否しますが、配列を超えて 1 つのエントリを書き込んでいるため、手遅れです。配列を超えleft[]て、1 または 0 が格納されている配列があります。したがって、最初のクライアントがどのように応答しても、彼は常に最後のクライアントを取ります。

于 2012-11-14T11:06:55.180 に答える