[英]Why my pipe doesn't work in my multithreads client-server program?
我用线程实现了一个客户端 - 服务器程序。 服务器程序必须从两个客户端读取带有套接字的100条消息,然后必须在管道上写入此数据,以使第四个程序读取它。 我成功地从套接字读取数据,但我无法在管道上写入; 我的“写”系统调用不起作用:我该怎么办?
#include <stdio.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <pthread.h>
#include <arpa/inet.h>
#include <netdb.h>
#include <sys/time.h>
#include <sys/wait.h>
#include <unistd.h>
#include <string.h>
#include <stdlib.h>
#include <errno.h>
#include <signal.h>
#include <time.h>
#define DIM 100
void logFile(char *msgStatus) {
FILE *f;
f = fopen("logFileEs1.log", "a+");
time_t currentTime;
char* timeString;
currentTime = time(NULL);
timeString = ctime(¤tTime);
fprintf(f, "%sPID %d. %s: %s\n",timeString, getpid(), msgStatus, strerror(errno));
fclose(f);
} //function for the creation of the log file
int j=0;
void error(char *msg)
{
perror(msg);
exit(1);
}
struct message { //dichiarazione struct
time_t timestamp;
char g; //process identifier
int x;
};
struct message m1[DIM];
struct message m2;
void *func_thread(void *p)
{
int nfd;
nfd= *(int*) p;
int n; //for reading
while(read(nfd,&m2,sizeof(m2))!=0) { //reading
printf("Here is the message: %d from process %d at time %ld %d\n",m2.x, m2.g, m2.timestamp, j);
fflush(stdout);
m1[j]=m2;
j++;
}
pthread_exit(NULL);
}
int main(int argc, char *argv[])
{
FILE *f;
f = fopen("logFileEs2.log", "w");
fclose(f);
pthread_t id[2];
void *dummy;
int iret1, i=0, d, t;
int pipeState, execState1, data;
pid_t child;
int sockfd,newsockfd, portno, clilen;
portno=6076;
struct sockaddr_in serv_addr, cli_addr; //adress of the server and the client
/*if (argc < 2) {
fprintf(stderr,"ERROR, no port provided\n");
exit(1);
}*/
sockfd = socket(AF_UNIX, SOCK_STREAM, 0); //new socket creation
if (sockfd < 0)
error("ERROR opening socket");
serv_addr.sin_family = AF_UNIX;
serv_addr.sin_addr.s_addr =inet_addr("127.0.0.1");
serv_addr.sin_port = htons(portno);
if (bind(sockfd, (struct sockaddr *) &serv_addr, sizeof(serv_addr)) < 0)
error("ERROR on binding");
listen(sockfd,5);
printf("Listening\n");
while(i<2) {
clilen = sizeof(cli_addr);
newsockfd = accept(sockfd, (struct sockaddr *) &cli_addr, &clilen);
if (newsockfd < 0)
error("ERROR on accept");
iret1 = pthread_create(&id[i], NULL, func_thread, &newsockfd);
if (iret1) {
perror("pthread_create");
return -1;
}
i++;
}
pthread_join(id[0], &dummy);
pthread_join(id[1], &dummy);
char readPipe[5]; //string for reading pipe
char writePipe[5]; //string for writing pipe
int fd[2]; //file descriptor pipe1
pipeState = pipe (fd); //creation first pipe
if (pipeState < 0) {
perror("Pipe error");
return -1;
}
close(fd[0]);
for (t=0; t<DIM; t++) {
printf("%d\n", m1[t].x);
}
data = write(fd[1], &m1, sizeof(m1));
if (data < 0) { //if written value is not valid
perror("Write error\n");
return -1;
}
printf("Data written on pipe\n");
close(fd[1]);
printf("Data written on pipe\n");
fflush(stdout);
//fd conversion from integer to string
sprintf(readPipe, "%d", fd[0]);
sprintf(writePipe, "%d", fd[1]);
char *function[] = {"M", readPipe, writePipe, NULL};
child=fork();
/*if (child1 != 0) {
logFile("Creation of the child1: ");
}*/
if (child < 0) {
perror ("Fork error in child1");
return -1;
}
else if (child == 0) {
execState1=execve("M", function,NULL);
exit (EXIT_SUCCESS);
}
else { wait(NULL);
exit (EXIT_SUCCESS);
}
return 0;
}
谢谢关注:)
您的代码中至少有三个竞争条件 ,其中一个线程使用数据,而另一个线程可能修改数据。
此代码创建竞争条件:
struct message m1[DIM];
struct message m2;
void *func_thread(void *p)
{
int nfd;
nfd= *(int*) p;
int n; //for reading
while(read(nfd,&m2,sizeof(m2))!=0) { //reading
printf("Here is the message: %d from process %d at time %ld %d\n",m2.x, m2.g, m2.timestamp, j);
fflush(stdout);
m1[j]=m2;
j++;
}
pthread_exit(NULL);
}
每个线程共享相同的m1
和m2
数据结构,当它们读入m2
时会覆盖彼此的数据。 它们还同时更新j
,因此在任一线程中都不能信任它的值。
此外,您不知道实际读取了多少字节。
此代码创建另一个数据竞争:
while(i<2) {
clilen = sizeof(cli_addr);
newsockfd = accept(sockfd, (struct sockaddr *) &cli_addr, &clilen);
if (newsockfd < 0)
error("ERROR on accept");
iret1 = pthread_create(&id[i], NULL, func_thread, &newsockfd);
if (iret1) {
perror("pthread_create");
return -1;
}
i++;
}
结合它
void *func_thread(void *p)
{
int nfd;
nfd= *(int*) p;
并且子线程正在从主线程访问newsockfd
,但是当子线程访问它时, newsockfd
可能具有不同的值。
更好的方法:
struct message m1[DIM];
int j = 0
pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;
void *func_thread(void *p)
{
// each thread needs its own struct
struct message m2;
// pass the socket by **value**
int nfd = ( intptr_t ) p;
for ( ;; )
{
// don't put code that needs error checks inside conditions
// because you can't handle errors, nor in this case partial
// read results
ssize_t bytes_read = read( nfd, &m2, sizeof( m2 ) );
if ( bytes_read == 0 )
{
break;
}
// really should put code here to handle a partial read()
printf("Here is the message: %d from process %d at time %ld %d\n",
m2.x, m2.g, m2.timestamp, j);
fflush(stdout);
// another race condition if this isn't mutex'd
pthread_mutex_lock( &mutex );
// get a local copy of the current value of j so
// the structure assignment can be moved outside
// the mutex-locked critical section
int my_j = j;
j++;
pthread_mutex_unlock( &mutex );
// stay within the bounds of the array
if ( my_j >= DIM )
{
break;
}
m1[my_j]=m2;
}
pthread_exit(NULL);
}
请注意, newsockfd
现在按值而不是地址传递,因此pthread_create()
调用需要:
iret1 = pthread_create(&id[i], NULL, func_thread, ( void * )( intptr_t ) newsockfd);
这有点像一个黑客,它依赖于你的平台能够传递一个int
值,例如newsockfd
作为void *
,但是你现在使用的任何系统几乎都可以做到。
我想你在谈论代码的这一部分(空白修改):
int fd[2]; //file descriptor pipe1 pipeState = pipe(fd); //creation first pipe if (pipeState < 0) { perror("Pipe error"); return -1; } close(fd[0]); for (t=0; t<DIM; t++) { printf("%d\\n", m1[t].x); } data = write(fd[1], &m1, sizeof(m1)); if (data < 0) { //if written value is not valid perror("Write error\\n"); return -1; } printf("Data written on pipe\\n"); close(fd[1]); printf("Data written on pipe\\n"); fflush(stdout);
我无法确定预期的行为。 我观察到管道的读取端在管道成功创建后立即关闭,这使得管道无用。 这不应该导致写端的后续write()
s无限期地阻塞,而只是因为它们应该通过EPIPE
失败。
如果你想通过管道与另一个进程通信,那么你应该在创建管道之后但在关闭任一端之前fork()
那个其他进程。 然后父母和孩子各自关闭他们不打算使用的结尾副本。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.