c - C POSIX进程管道/套接字通信练习第二个进程被卡住

标签 c sockets select pipe posix

我的想法是,我有一个“main.c”程序,它使用fork()启动两个进程P和G,并创建管道所需的一些文件描述符(在3个管道中,只有1个与我有关:代码会相应地进行调整)。

这两个进程通过套接字(G是服务器,P是客户端)进行通信。 P进程还具有两个输入管道末端,必须从中检查通过选择是否输入了新数据。其中一个管道实际上承载着来自G进程的数据,因此最后只有一条信息在G和P之间循环,并且实际上在处于P之前(通过预定义的公式)被修改了,然后再次发送给G。另一条管道暂时不相关。

我的问题是,在第一次迭代之后,似乎G进程被卡住了,因此没有数据通过G从管道传输到P。但是P中的select()一直在承认P正在接收某些内容(因此P再次发送了数据发送到G)。但是G在终端上什么也不显示。

需要传递的数据是计算值和时间戳的组合,因此我选择创建一个结构。我使用Ubuntu来测试代码,并使用“gcc”进行编译。

这是我的代码(config.h,main.c,P.c,G.c):

config.h

//This header stores all the definitions that the nodes need
#ifndef CONFIG_H
#define CONFIG_H

#ifndef M_PI
#define M_PI 3.14159265358979323846
#endif

#define NEXT_IP '192.168.1.233' //IP address of the next machine in the chain
#define NEXT_PORT 5000          //chosen port for the communication

#define buff 250000
#define SIZE 10

#define h_addr h_addr_list[0] /* for backward compatibility */

int run_mode = 0; //set to 0 to go debug mode, 1 for multiple machine

float rf = 1; //sine wave frequency

useconds_t waiting_time = 1000000; //waiting time (in microseconds) applied by process P before sending the updated token

struct message
{
    time_t timestamp;
    float value;
    int status;
};
struct message msg[buff]; //definition of the message

typedef struct
{
    float token_value;
    time_t token_timestamp;
} token_struct;

#endif

main.c
#include <stdlib.h>
#include <stdio.h>
#include <unistd.h>
#include <string.h>
#include <time.h>
#include <errno.h>
#include <netdb.h>
#include <sys/wait.h>
#include <sys/types.h>
#include <netinet/in.h>
#include <sys/socket.h>
#include <sys/time.h>
#include <signal.h>
#include <syslog.h>
#include <fcntl.h>
#include <math.h>
#include "config.h"

// This is the main, you have to only execute this command: ./main (you can also run sudo netstat -tulpn for socket troubleshooting)
// The duty of this piece of code is to load config data and to launch all the needed processes (S, P, G and L)

int main(int argc, char *argv[])
{
    int P, G, S, L;

    int pfd1[2]; //file descriptors for pipe 1
    int pfd2[2]; //file descriptors for pipe 2
    int pfd3[2]; //file descriptors for pipe 3

    int wait_status = 0;

    if (pipe(pfd1) < 0) //error condition on pipe 1
    {
        perror("Pipe 1 creation error");
        return -1;
    }
    if (pipe(pfd2) < 0) //error condition on pipe 2
    {
        perror("Pipe 2 creation error");
        return -1;
    }
    if (pipe(pfd3) < 0) //error condition on pipe 2
    {
        perror("Pipe 3 creation error");
        return -1;
    }

    char read1[SIZE];
    char write1[SIZE];
    char read2[SIZE];
    char write2[SIZE];
    char read3[SIZE];
    char write3[SIZE];

    sprintf(read1, "%d", pfd1[0]);  //load the fd input/output (3rd arg.) into the char array (1st arg.),
    sprintf(write1, "%d", pfd1[1]); //while formatting it as stated in 2nd arg.
    sprintf(read2, "%d", pfd2[0]);
    sprintf(write2, "%d", pfd2[1]);
    sprintf(read3, "%d", pfd3[0]);
    sprintf(write3, "%d", pfd3[1]);

    argv[0] = read1;  //pipe1: read
    argv[1] = write1; //pipe1: write
    argv[2] = read2;  //pipe2: read
    argv[3] = write2; //pipe2: write
    argv[4] = read3;  //pipe3: read
    argv[5] = write3; //pipe3: write
                      //I will be passing to each node all the pipe ends, by transforming their fd in char and then reverting them to integers

    G = fork();

    if (G < 0) //error condition on fork
    {
        perror("Fork G");
        return -1;
    }

    if (G == 0) //G process
    {
        char *node_name = "./G";
        if (execvp(node_name, argv) < 0) //error handling for file G
        {
            perror("Exec failed for G");
            return -1;
        }
    }
    else if (G > 0)
    {
        P = fork();

        if (P < 0) //error condition on fork
        {
            perror("Fork P");
            return -1;
        }

        if (P == 0) //P process
        {
            char *node_name = "./P";
            if (execvp(node_name, argv) < 0) //error handling for file P
            {
                perror("Exec failed for P");
                return -1;
            }
        }

        printf("[all processes setup and running]\n");

        wait(&wait_status);
        close(pfd1[0]);
        close(pfd1[1]);
        close(pfd2[0]);
        close(pfd2[1]);
        close(pfd3[0]);
        close(pfd3[1]);
        return 0;
    }
}

P.c
#include <stdlib.h>
#include <stdio.h>
#include <unistd.h>
#include <string.h>
#include <time.h>
#include <errno.h>
#include <netdb.h>
#include <sys/wait.h>
#include <sys/types.h>
#include <netinet/in.h>
#include <sys/socket.h>
#include <sys/time.h>
#include <signal.h>
#include <syslog.h>
#include <fcntl.h>
#include <math.h>
#include "config.h"

//This node is the computational core. It is also the nevralgic waypoint of communications: all other nodes involved are
//in some way or another bond to P

/*
Instructions for compiling this node:
gcc P.c -o P -lm
*/

void error(const char *m) //Display a message about the error on stderr and then aborts the program
{
    perror(m);
    exit(0);
}

int main(int argc, char *argv[])
{
    close(atoi(argv[1]));
    close(atoi(argv[3]));
    close(atoi(argv[4]));

    int state = 1;

    token_struct token;
    token.token_value = 1;
    token.token_timestamp = time(NULL);

    struct timeval tv;
    int retval;

    float dt = 0; //time delay between reception and delivery time instants of the token
    clock_t t = 0;

    pid_t Ppid;
    Ppid = getpid();
    printf("P: my PID is %d\n", Ppid);
    //new token = received token + DT x (1. - (received token)^2/2) x 2 pi x RF

    struct message msg;
    char address[13] = "192.168.1.233";

    int sockfd; //socket file descriptor
    int portno; //stores the port number on which the server accepts connections
    int n;
    struct sockaddr_in serv_addr;
    struct hostent *server;
    portno = 5000;                            //port number definition
    sockfd = socket(AF_INET, SOCK_STREAM, 0); //create a new socket
    if (sockfd < 0)
    {
        error("Error creating a new socket\n");
    }

    if (!run_mode)
    {
        server = gethostbyname("ZenBook");
    }
    else
    {
        server = gethostbyname(address);
        portno = NEXT_PORT;
    }

    if (server == NULL)
    {
        fprintf(stderr, "Could not find matching host name\n");
        exit(0);
    }
    bzero((char *)&serv_addr, sizeof(serv_addr)); //the function bzero() sets all values inside a buffer to zero
    serv_addr.sin_family = AF_INET;               //this contains the code for the family of the address
    bcopy((char *)server->h_addr, (char *)&serv_addr.sin_addr.s_addr, server->h_length);
    serv_addr.sin_port = htons(portno);
    if (connect(sockfd, (struct sockaddr *)&serv_addr, sizeof(serv_addr)) < 0)
        error("Connection failed");

    printf("[P node sending FIRST message]\n");
    n = write(sockfd, &(token), sizeof(token_struct));
    if (n < 0)
        error("Error writing to socket\n");

    while (1)
    {
        tv.tv_sec = 2;                   //amount of seconds the select listens for incoming data from either pipe 1 and 2
        tv.tv_usec = 0;                  //same as the previous line, but with microseconds
        fd_set readfds;                  //set of involved pipes from which P needs to read through the select
        FD_ZERO(&readfds);               //inizialization of the set
        FD_SET(atoi(argv[0]), &readfds); //addition of the desired pipe ends to the set
        FD_SET(atoi(argv[2]), &readfds);

        if (state == 1) //running situation
        {
            retval = select(atoi(argv[2]) + 1, &readfds, NULL, NULL, &tv);
            printf("retval: %d\n", retval);

            if (retval == -1)
            {
                perror("Select failed\n");
            }

            else if (retval > 0)
            {
                if (FD_ISSET(atoi(argv[0]), &readfds)) //read of first pipe (data coming from S) is ready
                {
                    //code
                }
                if (FD_ISSET(atoi(argv[2]), &readfds)) //read of second pipe (data coming from G) is ready
                {

                    //code



                    msg.timestamp = time(NULL); //get the current time

                    //This section is related to the communication with G, as the one with L is all set
                    t = clock() - t;
                    dt = ((float)t) / ((float)CLOCKS_PER_SEC); //by doing like this, the first cycle (and only that one) has a meaningless dt value
                        token.token_value = msg.value + dt * (((float)1) - (powf(msg.value, ((float)2)) / ((float)2))) * ((float)2) * ((float)M_PI) * rf;
                    t = clock();
                    printf("[P node sending message]\n");
                    usleep(waiting_time);
                    token.token_timestamp = msg.timestamp;
                    n = write(sockfd, &token, sizeof(token_struct));
                    if (n < 0)
                        error("Error writing to socket\n");
                }
            }
            else if (retval == 0)
                printf("No data written sent to pipes in the last 2 seconds\n");
        }

        else if (state == 0) //pausing sitation
        {
            //code
        }
    }
    close(atoi(argv[0]));
    close(atoi(argv[2]));
    close(atoi(argv[5]));
    close(sockfd);
    return 0;
}

G.c
#include <stdlib.h>
#include <stdio.h>
#include <unistd.h>
#include <string.h>
#include <time.h>
#include <errno.h>
#include <netdb.h>
#include <sys/wait.h>
#include <sys/types.h>
#include <netinet/in.h>
#include <sys/socket.h>
#include <sys/time.h>
#include <signal.h>
#include <syslog.h>
#include <fcntl.h>
#include <math.h>
#include "config.h"

//This node can be run in 2 modes: debug mode (single machine) or normal mode (communicating with other PCs);
//in the first case it receives tokens from P and then sends them back to it, in the other scenario it still
//receives data from P, but the token is sent to another machine

void error(const char *m) //display a message about the error on stderr and then abort the program
{
    perror(m);
    exit(1);
}

int main(int argc, char *argv[])
{
    close(atoi(argv[0]));
    close(atoi(argv[1]));
    close(atoi(argv[2]));
    close(atoi(argv[4]));
    close(atoi(argv[5]));

    int sockfd; //socket file descriptor
    int newsockfd;
    int portno; //port of the server for the client connection
    socklen_t clilen;
    struct sockaddr_in serv_addr, cli_addr;
    int n;

    token_struct token;
    token.token_value = 5;
    token.token_timestamp = time(NULL);

    pid_t Gpid;
    Gpid = getpid();
    printf("G: my PID is %d\n", Gpid);

    sockfd = socket(AF_INET, SOCK_STREAM, 0); //create a new socket
    if (sockfd < 0)
        error("Error creating a new socket\n");
    bzero((char *)&serv_addr, sizeof(serv_addr)); //the function bzero() sets all values inside a buffer to zero
    portno = 5000;
    serv_addr.sin_family = AF_INET; //this contains the code for the family of the address
    serv_addr.sin_addr.s_addr = INADDR_ANY;
    serv_addr.sin_port = htons(portno);
    if (bind(sockfd, (struct sockaddr *)&serv_addr, sizeof(serv_addr)) < 0) //the bind() system call binds a socket to an address
        error("Error on binding\n");
    listen(sockfd, 5); //system call that allows the process to listen for connections over the socket
    printf("[G node waiting for messages]\n");
    if (!run_mode)
    {
        while (1)
        {
            clilen = sizeof(cli_addr);
            newsockfd = accept(sockfd, (struct sockaddr *)&cli_addr, (socklen_t *)&clilen);
            //The accept() system call causes the process to block until a client connects to the server
            if (newsockfd < 0)
            {
                perror("'accept()' system call failed\n");
                return 1;
            }
            else
            {
                puts("Connection accepted\n");

                n = read(newsockfd, &(token), sizeof(token_struct));
                if (n < 0)
                    error("Error reading from socket\n");

                printf("Here is the message: %f | received at: %li\n", token.token_value, token.token_timestamp);
                write(atoi(argv[3]), &(token), sizeof(token_struct));
                printf("G: I tried to write on the pipe\n");
            }
        }
    }
    else //This is the code relative to the multiple machine case
    {
        while (1)
        {
            //code
        }
    }

    close(sockfd);
    return 0;
}

感谢任何愿意提供帮助的人。我已经尝试了几天,但没有成功。我绝不是一名程序员,到目前为止,这个项目对我来说是一个巨大的挑战。

最佳答案

G.c中的循环需要重新设计。看起来您在做的是accept()一个连接请求,从套接字读取,然后再次循环回到accept(),在这一点上,进程G然后阻止等待新的连接请求进入。

在进入循环之前,您需要执行accept(),循环仅使用阻塞调用不断从套接字读取数据。

P.c中的select()超时,这意味着如果在计时器到期之前未收到任何内容,则select()将返回指示超时。我看不到从P中的套接字实际读取的位置。如果没有读取套接字上等待的数据,那么当您在循环中再次执行select()以检查更多数据时,您会在此处得到指示仍然是要读取的数据。这可能就是为什么尽管G没有进一步发送消息,但仍看到来自P的消息的原因。

因此,您需要在G.c中执行以下操作。这只是一起扔,没有经过测试,但它读对了。该代码段中可能存在语法错误,因此您需要进行测试和调整的可能性更大。

{
        clilen = sizeof(cli_addr);
        newsockfd = accept(sockfd, (struct sockaddr *)&cli_addr, (socklen_t *)&clilen);
        //The accept() system call causes the process to block until a client connects to the server
        if (newsockfd < 0)
        {
            perror("'accept()' system call failed\n");
            return 1;
        }
        else {
            puts("Connection accepted\n");
        }
    while (1)
    {
        {
            n = read(newsockfd, &(token), sizeof(token_struct));
            if (n < 0)
                error("Error reading from socket\n");

            printf("Here is the message: %f | received at: %li\n", token.token_value, token.token_timestamp);
            write(atoi(argv[3]), &(token), sizeof(token_struct));
            printf("G: I tried to write on the pipe\n");
        }
    }
}

并且在P.c中,您需要在select()完成后从套接字读取。

关于c - C POSIX进程管道/套接字通信练习第二个进程被卡住,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/62375435/

相关文章:

mysql - SQL IN 语句 - 返回结果时保留重复项

mysql - Mysql中如何从两个不同的表中选择内容?

计算C中数组的集合差?

c - 为什么OpenBSD <sys/queue.h> 中的尾队列使用指向指针的指针?

c++ - Protocol Buffer 和实际传输选项 - 套接字或中间件

c - recvfrom() 何时使用 UDP 套接字返回 0?

tsql - 如何递归读取所有记录并按级别深度显示 TSQL

c - "["标记之前的预期表达式

c++ - 如何检查鼠标是否在 OpenGl 屏幕的一侧?

挑战: Calculate transfer rate via UDP socket