通过半双工管道在父母和2个子进程之间进行通信的问题

时间:2013-11-07 13:04:09

标签: c ipc pipe

我有一个正在努力的项目。它是一个分布式的tic tac toe游戏,有一个控制器和两个玩家。它利用半双工管道在进程之间进行通信。问题是进程没有正确通信,我不明白为什么。任何想法或帮助将非常感谢。这是代码:

 // This tic tac toe program illustrates a three process application 
// where two peer processes communicate through an oracle or server.
// A parent process acts as a controller and two child processes 
// acts as PlayerX and PlayerO. Players send their choice of input to
// controller which checks the input and displays results.

// Libraries
#include <stdio.h>
#include <unistd.h>
#include <string.h>
#include <sys/wait.h>
#include <sys/types.h>

// communication pipes constants
const int PIPE_READ = 0;
const int PIPE_WRITE = 1;

// position structure
struct _position {
    int row;
    int column;
};
typedef struct _position POSITION;

#define DEF_POSN(p, r, c) { p.row = r; p.column = c; }

//buffer for position
POSITION posn;

// message structure
struct _msg {
    char cmd;
    int data;
};
typedef struct _msg MESSAGE;

#define DEF_MSG(m, s, d) { m.cmd = s; m.data = d; }

// buffer for message
MESSAGE msg;

// controller and players can talk
void controllerSays(char * s){
    printf("Controller says: %s\n", s);
}

void playerOSays(char * s ){
    printf("Player O says: %s\n", s);
}

void playerXSays(char * s){
    printf("Player X says: %s\n", s);
}

//controller will be a state machine
enum gameStatus {
    QUIT = 0,
    READY = 1,  
    CONTINUE =2,
   P1_WIN = 3,
   P2_WIN = 4,
   DRAW = 5,    
};
typedef enum gameStatus GameStatus;

// Global variables
char array[3][3] = {
    {' ', ' ', ' '},
    {' ', ' ', ' '},
    {' ', ' ', ' '}
};
// Functions
int checkInput(int column, int row, int playerId)
{
// ERRORS:
// (-1) -> Wrong boundaries.
// (-2) -> Movement not allowed.


    if( !((column >= 1 && column <= 3) && ( row >= 1 && row <= 3)) ) {
        return -1; // Wrong boundaries
    }

    column -= 1;
    row -= 1;
    if( array[column][row] == ' ' ) { // Move is allowed
        array[column][row] = (playerId == 1) ? 'O' : 'X';
    } else {
        return -2; // Error. There is already a mark in that position
    }
    return 1; // Successfull
}
void init_board()
{
  int i, j;
  for( i = 0; i < 3; i++ )
    for( j = 0; j < 3; j++ )
      array[i][j] = ' ';
}

void draw_grid()
{
  // Print out the grid
  printf("  1   2   3 \n");
  printf("1 %c | %c | %c \n", array[0][0], array[0][1], array[0][2]);
  printf(" ---+---+--- \n");
  printf("2 %c | %c | %c \n", array[1][0], array[1][1], array[1][2]);
  printf(" ---+---+--- \n");
  printf("3 %c | %c | %c \n", array[2][0], array[2][1], array[2][2]);

}

int winnerExists()
{
    // Variables of function winnerExists.
    int i, j;

    for( i = 0; i < 3; i++ ) {
        // Check horizontal for player 1
        if( (array[i][0] == 'O' ) && (array[i][1] == 'O') && (array[i][2] == 'O') )
            return P1_WIN;
        // Check horizontal for player 2
        else if( (array[i][0] == 'X') && (array[i][1] == 'X') && (array[i][2] == 'X') )
            return P2_WIN;

        // Check vertical for player 1
        if( (array[0][i] == 'O') && (array[1][i] == 'O') && (array[2][i] == 'O') )
            return P1_WIN;
        // Check vertical for player 2
        else if( (array[0][i] == 'X') && (array[1][i] == 'X') && (array[2][i] == 'X') )
            return P2_WIN;
    }

    // Diagonal check for player 1
    if( (array[0][0] == 'O') && (array[1][1] == 'O') && (array[2][2] == 'O') ) {
            return P1_WIN;
    }
    else if( (array[0][2] == 'O') && (array[1][1] == 'O') && (array[2][0] == 'O') ) {
            return P1_WIN;
    }

    // Diagonal check for player 2
    if( (array[0][0] == 'X') && (array[1][1] == 'X') && (array[2][2] == 'X') ) {
        return P2_WIN;
    }
    else if( (array[0][2] == 'X') && (array[1][1] == 'X') && (array[2][0] == 'X') ) {
        return P2_WIN;
    }

    for( i = 0; i < 3; i++ ) {
        for( j = 0; j < 3; j++ ) {
            if( array[i][j] == ' ' )
                return CONTINUE; // No winner yet.
        }
    }
    // This is a tie. Nobody wins.
    return DRAW;
}

void playerOprocess(int READ, int WRITE){
    char reply = ' ';
    int row;
    int column;
    GameStatus status = READY;

    while (status != QUIT) {
playerOSays("in the loop p1");
        // check what controller says
        read(READ, &msg, sizeof(msg));
        // want to play?

        if (msg.cmd == 'r'){
            playerOSays("Controller asks if you want to play a game? (Y/N): ");
            scanf("%c", &reply);
            DEF_MSG(msg, reply, 0);
            write(WRITE, &msg, sizeof(msg));
        // if submited data is invalid
        }   else if (msg.cmd == 'e') {
            if (msg.data == -1){
                playerOSays("Error: Wrong boundaries!\n");
            } else if (msg.data == -2) {
                playerOSays("Error: There is already a mark there!\n");
            }
            draw_grid();
            playerOSays("Please enter the row and column,\n where you wish to place your mark (O): ");
            scanf("%d %d", &row, &column);
            DEF_POSN( posn, row, column);
            write(WRITE, &posn, sizeof(posn)); 
        // if everything is ok and we continue playing
        } else if (msg.cmd == 'c') {
            draw_grid();
            playerOSays("Please enter the row and column,\n where you wish to place your mark (X): ");
            scanf("%d %d", &row, &column);
            DEF_POSN( posn, row, column);
            write(WRITE, &posn, sizeof(posn)); 
        // if player1 wins
        } else if (msg.cmd == 'o') {
            playerOSays("Hooray! I am the winner!");
        // if player2 wins
        } else if (msg.cmd == 'x') {
            playerOSays("Nooo! I lost the game!");
        // if a draw
        } else if (msg.cmd == 'd') {
            playerOSays("Dammit! Its a draw!");
        // if quit
        } else if (msg.cmd == 'q') {
            playerOSays("I'm outta here!");
            status = QUIT;
        }
    }
    close(READ);
    close(WRITE);
}

void playerXprocess(int READ, int WRITE){
    char reply = ' ';
    int row;
    int column;
    GameStatus status = READY;

    while (status != QUIT){
playerXSays("in the loop p2");
        // check what controller says
        read(READ, &msg, sizeof(msg));
        // want to play?
        if (msg.cmd == 'r'){
            playerXSays("Controller asks if you want to play a game? (Y/N): ");
            scanf("%c", &reply);
            DEF_MSG(msg, reply, 0);
            write(WRITE, &msg, sizeof(msg));
        // if submited data is invalid
        }   
        if (msg.cmd == 'e') {
            if (msg.data == -1){
                playerXSays("Error: Wrong boundaries!\n");
            } else if (msg.data == -2) {
                playerXSays("Error: There is already a mark there!\n");
            }
            draw_grid();
            playerXSays("Please enter the row and column,\n where you wish to place your mark (X): ");
            scanf("%d %d", &row, &column);
            DEF_POSN( posn, row, column);
            write(WRITE, &posn, sizeof(posn)); 
        // if everything is ok and we continue playing
        } else if (msg.cmd == 'c') {
            draw_grid();
            playerXSays("Please enter the row and column,\n where you wish to place your mark (X): ");
            scanf("%d %d", &row, &column);
            DEF_POSN( posn, row, column);
            write(WRITE, &posn, sizeof(posn)); 
        // if player2 wins
        } else if (msg.cmd == 'x') {
            playerXSays("Hooray! I am the winner!");
        // if player1 wins
        } else if (msg.cmd == 'o') {
            playerXSays("Nooo! I lost the game!");
        // if a draw
        } else if (msg.cmd == 'd') {
            playerXSays("Dammit! Its a draw!");
        // if quit
        } else if (msg.cmd == 'q') {
            playerXSays("I'm outta here!");
            status = QUIT;
        }
    } 
    close(READ);
    close(WRITE);
}

void controller(int READ_X, int WRITE_X, int READ_O, int WRITE_O){
    int playerID = 1;
    int row;
    int column;
    int inputCheck;

    GameStatus status = READY;

    while (status != QUIT){
controllerSays("in controller loop");
        // send message to player 1 asking if he wants to play
        // and get response from him
        DEF_MSG(msg, 'r', 0);
        write(WRITE_O, &msg, sizeof(msg));
        sleep(5);
        read(READ_O, &msg, sizeof(msg));
        if ((msg.cmd == 'y') || (msg.cmd == 'Y')){
        // send message to player 2 asking if he wants to play
        // and get response from him
            DEF_MSG(msg, 'r', 0);
            write(WRITE_X, &msg, sizeof(msg));
            read(READ_X, &msg, sizeof(msg));
            if (msg.cmd == 'y' || msg.cmd == 'Y'){
                status = CONTINUE;
            }else {
            // if they dont want to play tell them to quit
                DEF_MSG(msg, 'q', 0);
                write(WRITE_O, &msg, sizeof(msg));
                write(WRITE_X, &msg, sizeof(msg));
                status = QUIT;
            }           
        } else {
        // if they dont want to play tell them to quit
            DEF_MSG(msg, 'q', 0);
            write(WRITE_O, &msg, sizeof(msg));
            write(WRITE_X, &msg, sizeof(msg));
            status = QUIT;
        }           

        init_board(); // Initialize array

        while (status == CONTINUE) {            
            if (playerID == 1){
                // tell player1 to start a game and make a move
                DEF_MSG(msg, 'c', 0);
                write(WRITE_O, &msg, sizeof(msg));
                read(READ_O, &posn, sizeof(posn));
                row = posn.row;
                column = posn.column;
                // check if input is valid
                inputCheck = checkInput(row, column, playerID);
                // if not valid tell player1 to make a valid move
                while (inputCheck != 1) {
                    DEF_MSG(msg, 'e', inputCheck);
                    write(WRITE_O, &msg, sizeof(msg));
                    read(READ_O, &posn, sizeof(posn));
                    row = posn.row;
                    column = posn.column;
                    inputCheck = checkInput(row, column, playerID);
                }
                // if move is  valid check the status of the game
                // and construct a message with status update
                status = winnerExists();
                if (status == CONTINUE) {
                    DEF_MSG (msg, 'c', 0);
                } else if (status == P1_WIN) {
                    DEF_MSG (msg, 'o', 0);
                    status = READY;
                } else if (status == P2_WIN) {
                    DEF_MSG (msg, 'x', 0);
                    status = READY;
                } else if (status == DRAW) {
                    DEF_MSG (msg, 'd', 0);
                    status = READY;
                }
                // update players status
                write(WRITE_O, &msg, sizeof(msg));
                write(WRITE_X, &msg, sizeof(msg));
                // flip players
                (playerID== 1) ? 2 : 1;
            } else if (playerID == 2) {
                // tell player2 to start a game and make a move
                DEF_MSG(msg, 'c', 0);
                write(WRITE_X, &msg, sizeof(msg));
                read(READ_X, &posn, sizeof(posn));
                row = posn.row;
                column = posn.column;
                // check if input is valid
                inputCheck = checkInput(row, column, playerID);
                // if not valid tell player2 to make a valid move
                while (inputCheck != 1) {
                    DEF_MSG(msg, 'e', inputCheck);
                    write(WRITE_X, &msg, sizeof(msg));
                    read(READ_X, &posn, sizeof(posn));
                    row = posn.row;
                    column = posn.column;
                    inputCheck = checkInput(row, column, playerID);
                }
                draw_grid(); // Draw initial grid
                // if move is  valid check the status of the game
                // and construct a message with status update
                status = winnerExists();
                if (status == CONTINUE) {
                    DEF_MSG (msg, 'c', 0);
                } else if (status == P1_WIN) {
                    DEF_MSG (msg, 'o', 0);
                    status = READY;
                } else if (status == P2_WIN) {
                    DEF_MSG (msg, 'x', 0);
                    status = READY;
                } else if (status == DRAW) {
                    DEF_MSG (msg, 'd', 0);
                    status = READY;
                }
                // update players status
                write(WRITE_X, &msg, sizeof(msg));
                write(WRITE_O, &msg, sizeof(msg));
                // flip players
                (playerID == 1) ? 2 : 1;
            }
        }
    }
    // close pipes
    close(READ_X);
    close(WRITE_X);
    close(READ_O);
    close(WRITE_O);
}
int main() {
  // pipes for communication with playerX
  int fd_toPlayerX[2];
    int fd_toControllerX[2];
  // pipes for communication with playerO
    int fd_toPlayerO[2];
    int fd_toControllerO[2];

    pipe(fd_toPlayerX);
    pipe(fd_toControllerX);

    pipe(fd_toPlayerO);
    pipe(fd_toControllerO);

    pid_t playerX, playerO;
    // fork parent process with two children.
    playerX = fork();

    if ( playerX < 0 ){
    fprintf(stderr, "Fork failure\n");
    return -1;
  }

    if (playerX == 0) {
        // playerX code 
        // Close pipes 
        close(fd_toPlayerX[PIPE_WRITE]);
        close(fd_toControllerX[PIPE_READ]);

        // call for playerX routine
        playerXprocess(fd_toPlayerX[PIPE_READ], fd_toControllerX[PIPE_WRITE]);

    } else {
        playerO = fork();

        if ( playerO < 0 ){
            fprintf(stderr, "Fork failure\n");
            return -1;
        }

        if (playerO == 0) {
            // playerO code 
            // Close pipes 
            close(fd_toPlayerO[PIPE_WRITE]);
            close(fd_toControllerO[PIPE_READ]);

            // call for playerO routine
            playerOprocess(fd_toPlayerO[PIPE_READ], fd_toControllerO[PIPE_WRITE]);

        } else {
            // Controller code 
            // Close pipes on playerX side
            close(fd_toPlayerX[PIPE_READ]);
            close(fd_toControllerX[PIPE_WRITE]);

            // Close pipes on playerO side
            close(fd_toPlayerO[PIPE_READ]);
            close(fd_toControllerO[PIPE_WRITE]);

            // Call for controller routine
            controller(fd_toControllerX[PIPE_READ], fd_toPlayerX[PIPE_WRITE], fd_toControllerO[PIPE_READ], fd_toPlayerO[PIPE_WRITE]);
            wait(NULL);
        }
    }
    return 0;
}

2 个答案:

答案 0 :(得分:0)

将记录添加到您的代码中。这样,您就可以获得执行代码的历史记录以及原因。通过阅读日志,您可以查看实际事件是否与您认为应该发生的事件相符。

以最简单的形式,使用printf()。我建议将每个打印调用打包在D()

#ifdef DEBUG
# define D(x) x
#else
# define D(x)
#endif

然后,您可以通过定义符号DEBUG

来打开和关闭所有登录

答案 1 :(得分:0)

  

问题似乎恰好在控制器的开头   处理。而不是检查来自playerO的消息,它跳转到playerX,   我不明白为什么。

在每个回合中你发送两个&#39; c&#39;发送给当前玩家的消息 - 一个在开头(评论tell player1 to start a game and make a move),另一个在转弯结束时(评论update players status)。但是玩家,当它收到一个&#39; c&#39;消息,绘制网格以及扫描输入,因此,在第一个回合结束时再次从playerO请求输入,并且在第一个回合开始时几乎同时从playerX请求输入。这里似乎需要两种不同的消息类型:一种用于请求移动输入,另一种用于更新网格显示。