简体   繁体   English

通过c中的半双工管道,父进程和2个子进程之间的通信出现问题

[英]problems with communication between parent and 2 child processes via half-duplex pipes in c

I have a project that im working on. 我有一个正在进行的项目。 Its a distributed tic tac toe game which has a controller and two players. 它是一款具有控制器和两名玩家的分布式井字游戏。 It utilizes half-duplex pipes to communicate between processes. 它利用半双工管道在进程之间进行通信。 The problem is that processes are not communicating correctly and I cant see why. 问题是进程无法正确通信,我看不出原因。 Any idea or help will be very much appreciated. 任何想法或帮助将不胜感激。 Here is the code: 这是代码:

 // 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;
}

Add logging to your code. 将日志记录添加到您的代码中。 That way, you can get a history of which code was executed and why. 这样,您可以获得有关执行了哪些代码以及执行原因的历史记录。 By reading the log, you can then see if the actual events match what you think should happen. 通过阅读日志,您可以查看实际事件是否符合您认为应该发生的事件。

In the most simple form, use printf() . 以最简单的形式,使用printf() I suggest to wrap each print call in D() : 我建议将每个打印调用包装在D()

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

You can then switch all logging on and off by defining the symbol DEBUG 然后,您可以通过定义符号DEBUG打开和关闭所有日志记录

The problem seems to be right at the beginning of the controller process. 该问题似乎在控制器过程的开始就正确了。 Instead of checking message from playerO it jumps to playerX, and I don't understand why. 它没有检查来自playerO的消息,而是跳转到playerX,我不明白为什么。

In each turn you send two 'c' messages to the current player - one at the beginning (comment tell player1 to start a game and make a move ) and the other at the end of the turn (comment update players status ). 在每个回合中,您都会向当前玩家发送两条“ c”消息-一个在开始时(注释tell player1 to start a game and make a move ),另一则在回合结束时(注释update players status )。 But the player, when it receives a 'c' message, draws the grid as well as scans for input, so, input is requested from playerO again at the end of its first turn and nearly at the same time from playerX at the beginning of its first turn. 但是,当玩家收到“ c”消息时,它会绘制网格并扫描输入,因此,玩家O在其第一回合结束时再次请求输入,而几乎在同一时间从玩家X处请求输入。它的第一轮。 It seems you need two separate message types here: one for asking for move input, and the other for updating the grid display. 似乎您在这里需要两种单独的消息类型:一种用于请求移动输入,另一种用于更新网格显示。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM