通过 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 个子进程之间进行通信的问题的主要内容,如果未能解决你的问题,请参考以下文章