我的想法是,我有一个“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/