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

Posted

技术标签:

【中文标题】通过 c 中的半双工管道在父进程和 2 个子进程之间进行通信的问题【英文标题】:problems with communication between parent and 2 child processes via half-duplex pipes in c 【发布时间】:2013-11-07 13:04:08 【问题描述】:

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

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

【问题讨论】:

您希望我们通过 100 行代码为您找出问题所在吗?为什么不帮助我们帮助您并提出具体问题? 问题是我不知道想要是错的。它可以编译,但是当程序运行时,它会从一个进程跳转到另一个进程,而不是遵循执行流程。我是 C 新手,可能会遗漏一些小细节。 现在是开始使用调试器的时候了,或者如果你真的不能,那么至少使用一些打印语句来看看发生了什么。 问题似乎就在控制器进程的开始。它没有检查来自 playerO 的消息,而是跳转到 playerX,我不明白为什么。 【参考方案1】:

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

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

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

然后您可以通过定义符号DEBUG 来打开和关闭所有登录

【讨论】:

【参考方案2】:

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

在每个回合中,您向当前玩家发送两条“c”消息 - 一条在开始时(评论 tell player1 to start a game and make a move),另一条在回合结束时(评论 update players status)。但是当玩家收到“c”消息时,它会绘制网格并扫描输入,因此,在第一轮结束时再次从 playerO 请求输入,并且在开始时几乎同时从 playerX 请求输入它的第一轮。看来您需要两种不同的消息类型:一种用于请求移动输入,另一种用于更新网格显示。

【讨论】:

以上是关于通过 c 中的半双工管道在父进程和 2 个子进程之间进行通信的问题的主要内容,如果未能解决你的问题,请参考以下文章

进程通信 之 管道 标准流管道

进程间通信方式及特点

多进程编程之进程间通信

linux中用管道实现兄弟进程通信

UNIX网络编程进程间通信之管道

多进程通信之管道运用