I'm trying to build a game where a client gets to play against a server in a game of Tic Tac Toe. I've built the game and followed a somehow structured design of how the game is supposed to happen, with the client and the server exchanging messages, but they seem to lose synchronisation at the beginning of the game.

The server sends a "GOO" message, which is supposed to start the game, but it seems the client never receives it. The server doesn't even care about that and just goes on, reaching a point where both ends expect a message in return but if one of them gives a message, the run fails because of the error detection I've put in place.

Here are both codes, I would be really glad if you could help me out, I'm really new to all the socket programming stuff.

Here is the server side:

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <errno.h>
#include <string.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <sys/wait.h>
#include <signal.h>


#define MYPORT 5555    // Le port ou se connectent les utilisateurs
#define BACKLOG 10 // Le nombre de connections acceptees



int sockfd, new_fd;  
        // listen on sock_fd, new connection on new_fd
struct sockaddr_in my_addr;
      // my address information
struct sockaddr_in their_addr;
      // connector's address information
unsigned int sin_size;






//-------------------------------- Le Serveur ----------------------------------


int setup_listener()
{
    printf("\nsetup_listener\n");
    // Socket que l'on va "listen".
    sockfd = socket(PF_INET, SOCK_STREAM, 0);
    if (sockfd < 0)
    {
        perror("Erreur a l'ouverture du socket 'listener'.");
        exit(EXIT_FAILURE);
    }

    // Mise a 0 de la memoire pour le serveur.
    memset(&my_addr, 0, sizeof(my_addr));

    // Set up des infos du serveur
    my_addr.sin_family = AF_INET;   
    my_addr.sin_addr.s_addr = INADDR_ANY;   
    my_addr.sin_port = htons(MYPORT);       

    // Bind des infos du serveur au socket.
    if (bind(sockfd, (struct sockaddr *) &my_addr,
             sizeof(my_addr)) < 0)
    {
        perror("Erreur au binding du socket 'listener'.");
        exit(EXIT_FAILURE);
    }
}

void get_client()
{
    printf("\nget_client\n");
    if (listen(sockfd, BACKLOG) < 0)
    {
        perror("Erreur lors du listen.");
        exit(EXIT_FAILURE);
    }

    sin_size = sizeof(struct sockaddr_in);

    // Mise a zero de la memoire pour le client.
    memset(&their_addr, 0, sin_size);

    new_fd = accept (sockfd, (struct sockaddr *)&their_addr, &sin_size);

    if (new_fd < 0)
    {
        perror("Erreur lors de l'accept.");
        exit(EXIT_FAILURE);
    }

    printf("Serveur:  connection recue du client %s\n",
     inet_ntoa(their_addr.sin_addr));

}




//---------------------------------- Le Jeu ------------------------------------

void write_client_int(int msg)
{
    printf("\nwrite_client_int\n");
    int n = write(new_fd, &msg, sizeof(int));
    if (n < 0)
    {
        perror("Erreur lors de l'ecriture d'entier.");
        exit(EXIT_FAILURE);
    }
}

void write_client_msg(char * msg)
{
    printf("\nwrite_client_msg\n");
    int n = write(new_fd, msg, strlen(msg));
    if (n < 0)
    {
        perror("Erreur lors de la transmission de message");
        exit(EXIT_FAILURE);
    }
}

int recv_int()
{
    printf("\nrecv_int\n");
    int msg = 0;
    int n = read(new_fd, &msg, sizeof(int));

    if (n < 0 || n != sizeof(int))
        {
        perror("Erreur lors de la reception de message(int).");
        exit(EXIT_FAILURE);
        }

    return msg;
}

int get_player_move()
{ 
    printf("\nget_player_move\n");
    write_client_msg("TRC");
    // Demande ce que le joueur veut faire.
    return recv_int();
}


int check_move(char board[3][3], int move)
{
    printf("\ncheck_move\n");
    if ((move == 9) || (board[move/3][move%3] == ' '))
       return 1;
   else 
       return 0;
}

void update_board(char board[3][3], int move)
{
    printf("\nupdate_board\n");
    board[move/3][move%3] = 'X';
}

void send_update(int move)
{
    printf("\nsend_update\n");
    write_client_msg("MAJ");
    // Envoie le resultat apres un placement de morpion.
    write_client_int(move); 
}


int check_board(char board[3][3], int last_move)
{
    printf("\ncheck_board\n");

    int row = last_move/3;
    int col = last_move%3;

    if ( board[row][0] == board[row][1] && board[row][1] == board[row][2]) 
    {
        // Si victoire atteinte sur une ligne.
        if (board[row][0] == 'X')
            return 1;
        else if (board[row][0] == 'O')
            return 2;
    }

    else if ( board[0][col] == board[1][col] && board[1][col] == board[2][col] ) 
    {
        // Si victoire atteinte sur une colonne.
        if (board[0][col] == 'X')
            return 1;
        else if (board[0][col] == 'O')
            return 2;
    }

    else if ( (last_move == 0 || last_move == 4 || last_move == 8) &&
         (board[1][1] == board[0][0] && board[1][1] == board[2][2]) )
    {
        // Si victoire atteinte sur la diagonale descendante.
        if (board[1][1] == 'X')
            return 1;
        else if (board[1][1] == 'O')
            return 2;
    }

    else if ( (last_move == 2 || last_move == 4 || last_move == 6) &&
         (board[1][1] == board[0][2] && board[1][1] == board[2][0]) )
    {
        // Si victoire atteinte sur la diagonale montante.
        if (board[1][1] == 'X')
            return 1;
        else if (board[1][1] == 'O')
            return 2;
    }


    return 0;
}

void server_play(char board[3][3])
{
    printf("\nserver_play\n");
    int random_move;

    do
    {
        random_move = rand()%8;
        if (check_move(board, random_move) == 1)
        {
            update_board(board, random_move);
        }
    }
    while (!check_move(board, random_move));

}

void Play_game()
{
    printf("\nPlay_game\n");
    printf("Writing message to client GOO. 0");
    char board[3][3] = { {' ', ' ', ' '}, {' ', ' ', ' '}, {' ', ' ', ' '} };
    int game_over = 0;
    int turns = 0;
    printf("Writing message to client GOO. 1");
    write_client_msg("GOO");
    printf("Writing message to client GOO. 2");

    while (game_over == 0)
    {
        int valid_move = 0;
        int move = 0;
        while (!valid_move)
        {
            move = get_player_move();
            if (move < 0)
            {
                perror("Erreur lors de la reception du placement indique.");
                exit(EXIT_FAILURE);
            }
            valid_move = check_move(board, move);
        }

        update_board(board, move);
        game_over = check_board(board, move);
        send_update(move);

        if (game_over == 0)
            server_play(board);

        send_update(move);


        game_over = check_board(board, move);
        if (game_over == 1)
        {
            write_client_msg("WIN");
        }

        else if (game_over == 2)
        {
            write_client_msg("LOS");
        }

        else if (turns == 8)
        {
            write_client_msg("EGL");
            game_over = 3;
        }

        ++turns;
    }

    close(sockfd);

}




int main(int argc, char** argv) 
{
    printf("\nmain\n");
    setup_listener();

    while (1)
    {
        get_client();
        // REQUESTS INT FOR SOME REASON
        Play_game();
    }

    return (EXIT_SUCCESS);
}

And here is the client side:

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <errno.h>
#include <string.h>
#include <netdb.h>
#include <sys/types.h>
#include <netinet/in.h>
#include <sys/socket.h>


#define MYPORT 5555;
#define MY_BUFFER_SIZE 10;

int sockfd;
struct sockaddr_in their_addr;
struct hostent *server;


// Lit un message du socket serveur.
void recv_msg(char * msg)
{
    printf("\nrecv_msg\n");
    // Tous les messages font 3 bytes.
    memset(msg, 0, 4);

    int n = read(sockfd, msg, 3);

    if (n < 0 || n != 3)
    {
        perror("Erreur lors de la lecture de message sur le socket serv.");
        exit(EXIT_FAILURE);
    }
}

// Lit un entier du socket serveur.
int recv_int()
{
    printf("\nrecv_int\n");
    int msg = 0;
    int n = read(sockfd, &msg, sizeof(int));

    if (n < 0 || n != sizeof(int)) 
    {
        perror("Erreur lors de la lecture d'entier sur le socket serv.");
        exit(EXIT_FAILURE);
    }


    return msg;
}

// Ecrit un entier sur le socket serveur.
void write_server_int(int msg)
{
    printf("\nwrite_server_int\n");
    int n = write(sockfd, &msg, sizeof(int));
    if (n < 0)        
    {
        perror("Erreur lors de l'ecriture d'entier sur le socket serv.");
        exit(EXIT_FAILURE);
    }
}

int connect_to_server(char * hostname)
{
    printf("\nconnect_to_server\n");
    sockfd = socket(AF_INET, SOCK_STREAM, 0);

    if (sockfd < 0) 
    {
        perror("Erreur lors de l'ouverture du socket du serveur.");
        exit(EXIT_FAILURE);
    }

    server = gethostbyname(hostname);

    if (server == NULL) 
    {
        perror("Erreur lors de la recherche du serveur (hostname)");
        exit(EXIT_FAILURE);
    }

    memset(&their_addr, 0, sizeof(their_addr));

    their_addr.sin_family = AF_INET;
    their_addr.sin_addr = *((struct in_addr*)server->h_addr);
    //PORT
    their_addr.sin_port = htons(5555); 

    if (connect(sockfd, (struct sockaddr *) &their_addr, sizeof(their_addr)) < 0)
    {
        perror("Erreur lors de la connection au serveur.");
        exit(EXIT_FAILURE);
    }
}

void draw_board(char board[3][3])
{
    printf("\ndraw_board\n");
    printf(" %c | %c | %c \n", board[0][0], board[0][1], board[0][2]);
    printf("___________\n");
    printf(" %c | %c | %c \n", board[1][0], board[1][1], board[1][2]);
    printf("___________\n");
    printf(" %c | %c | %c \n", board[2][0], board[2][1], board[2][2]);
}

void take_turn()
{   
    printf("\ntake_turn\n");
    //BUFFER_SIZE
    char buffer[10];

    while (1) 
    {
        printf("Choisissez un nombre de 0 a 8 pour jouer."
                "Sur 0 commence la 1ere ligne, sur 3 la 2eme, sur 6 la 3eme.");

        fgets(buffer, 10, stdin); // Lit ce qui est recu par l'utilisateur

        // - '0' = -48 pour annuler les 48 premiers chr de la table ASCII
        // Car stdin prend l'input en tant que char -> ASCII et move est un int
        int move = buffer[0] - '0';

        if (move <= 8 && move >= 0)
        {
            write_server_int(move);   
            break;
        } 
        else
            printf("\n");
            printf("Mauvais input. Veuillez suivre les indications.");
    }
}

void get_update(char board[3][3])
{
    printf("get_update");
    // Recoit le placement de morpion du serveur.
    int move = recv_int(sockfd);

    // Met a jour le jeu.
    board[move/3][move%3] = 'O';    
}


int main(int argc, char** argv) 
{
    printf("main");
    if (argc != 2)
    {
        perror("Veuillez specifier le nom de la machine distante en argument.");
        exit(EXIT_FAILURE);
    }

    sockfd = connect_to_server(argv[1]);

    printf("Waiting for server message to start.");

    char msg[4];
    char board[3][3] = { {' ', ' ', ' '}, {' ', ' ', ' '}, {' ', ' ', ' '} };

    draw_board(board);

    // THE ERROR HAPPENS HERE PLZ HELP

    printf("Waiting for server message to start.");


    do
    {
        recv_msg(msg);
    }
    while (strcmp(msg, "GOO"));

    printf(msg);

    draw_board(board);

    while(1)
    {

        recv_msg(msg);

        if (!strcmp(msg, "TRC"))
        {
            printf("A vous de jouer: ");
            take_turn(sockfd);
        }

        else if (!strcmp(msg, "MAJ"))
        {
            get_update(board);
            draw_board(board);
        }

        else if (!strcmp(msg, "WIN")) { /* Winner. */
            printf("Victoire! Vous avez battu la machine.");
            break;
        }
        else if (!strcmp(msg, "LOS")) { /* Loser. */
            printf("Vict... presque! On apprend beaucoup quand on perd <3");
            break;
        }
        else if (!strcmp(msg, "EGL")) { /* Game is a draw. */
            printf("Egalite. Bah au moins vous avez pas perdu! Excellent.");
            break;
        }

        printf("Revenez un de ces jours! Regardez comme je m'amuse...");

        close(sockfd);

        return 0;
    }



    return (EXIT_SUCCESS);
}

And here are both outputs when I try to run them:

Server:

main

setup_listener

get_client
Serveur:  connection recue du client 192.168.209.1

Play_game
Writing message to client GOO. 0Writing message to client GOO. 1
write_client_msg
Writing message to client GOO. 2
get_player_move

write_client_msg

recv_int

RUN TERMINATED (exit value 1, total time: 2m 3s)

Client:

E:\University\BA2\Algorithmique\OXO_Client>gcc client.c -o client

E:\University\BA2\Algorithmique\OXO_Client>client Reinstall
main
connect_to_server
Waiting for server message to start.
draw_board
   |   |
___________
   |   |
___________
   |   |
Waiting for server message to start.
recv_msg

Erreur lors de la lecture de message sur le socket serv.: No error

E:\University\BA2\Algorithmique\OXO_Client>

The client is run through the terminal and receives the computer's name as an argument. As you can see, the server just sends its GOO message and doesn't care if the client receives it nor what it does with it.

closed as off-topic by SuperBiasedMan, JS1, ChrisWue, Hosch250, RubberDuck Dec 20 '15 at 20:31

This question appears to be off-topic. The users who voted to close gave this specific reason:

  • "Questions containing broken code or asking for advice about code not yet written are off-topic, as the code is not ready for review. After the question has been edited to contain working code, we will consider reopening it." – SuperBiasedMan, JS1, ChrisWue, Hosch250, RubberDuck
If this question can be reworded to fit the rules in the help center, please edit the question.

Comments

Your comments are part English and part French. Pick a language and stick to it, don't alternate between languages.

Hard-coded values

There are a lot of hard-coded values in your code. For example:

else if ( (last_move == 0 || last_move == 4 || last_move == 8) &&
     (board[1][1] == board[0][0] && board[1][1] == board[2][2]) )
{

That's 11 in two lines!

The design looks sub-optimal, so this could simply be a side effect of doing things in a not too straightforward manner. However, you can negate this problem by using more variables (make those that don't move const and declare them in a .h).

That second line would look a lot clearer if you used the row and col variable names I've seen earlier.

     (board[row][col] == board[row-1][col-1] && board[row][col] == board[row+1][col+1]) )

By the way, that construct looks like it can be written in more straightforward manner but my C is too rusty to make the translation.

Not the answer you're looking for? Browse other questions tagged or ask your own question.