2012-11-14 3 views
2

Я изучаю сокеты Linux/UNIX, поэтому я написал очень простую «игру» на ее основе «21 матч». Есть куча, состоящая из 21 матча, и каждый игрок занимает одно, два или три матча от него. Тот, кто принимает последний матч, теряет игру.Странное поведение программы после закрытия Linux-сокета

Очевидно, что ключом к победе является дополнить ход вашего оппонента до 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! 

Обратите внимание, что вы потеряете, если и только , если ваш вклад равен числу матчей осталось. Так, что происходит?

+0

Вы уверены, что используете C++? –

+0

C или C++, это не имеет большого значения в этом вопросе. В любом случае, сменив ярлык на C и очистив этот код, чтобы предотвратить ввод в заблуждение –

+0

Вы пробовали пройти через него в отладчике, по очереди, чтобы узнать, что произойдет в этом случае? –

ответ

0

Вы переписываете left[], когда вы звоните acceptclient() и не проверяете, есть ли место в fds для хранения нового соединения.

Сначала вы инициализируете нового клиента fds и впоследствии отклоняете соединение, но потом уже слишком поздно, потому что вы написали одну запись за пределы массива. И за пределами массива существует массив left[], который теперь имеет 1 или 0, хранящихся в нем. Таким образом, независимо от того, как отвечает первый клиент, он всегда берет последнее.

+0

Нет, я делаю owerwrite это только тогда, когда есть достаточно места для нового клиента, иначе новый человек будет стартовать. –

+0

Да, но сначала вы храните 'c' в' fds [nfd] ', который уже является одной записью. Сначала вы должны проверить количество клиентов и отклонить или зарезервировать еще одно место в 'fds', что будет' fds [limit + 2] '. –

+0

Вот что случилось! Человек, ты спас мне хорошую нагрузку. –

Смежные вопросы