So I am playing around with the idea of ports and client/server communication.
I have a server.c program that can open a port, open a listening descriptor, and upon receiving a connection, fork a child to handle communication with a connecting client. I have a client.c program that takes in 5 commandline arguments. Basically the first 3 arguments are practice strings to send to server and the 4th is hostname and the 5th is the port number.
So far connecting these two has worked fine, however, when client tries to write the 3 different strings (argv[1],argv[2], and argv[3]) to the server.c, server.c seems to only be able to read the first one then it seems to be stuck and not continue on with the additional reads even though client will finish writing all the strings to the communication file descriptor. I have been stuck for over 4 hours trying to figure out what should have been a simple practice program to better learn servers and clients. I don't wanna get anymore lost then I already am so I hope someone could anyone give me any advice on how to handle this issue or what I am doing wrong.
Client.c
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include "uici.h"
#include "func.h"
int main(int argc, char *argv[]){
int fd;
u_port_t portnum;
if(argc != 6){
fprintf(stderr, "Usage: %s string1 string2 string3 host port\n",argv[0]);
return -1;
}
portnum = (u_port_t)atoi(argv[5]);
if((fd = u_connect(portnum, argv[4])) == -1){
perror("Failled to establish connection");
return 1;
}
fprintf(stderr, "[%ld]:connection made to %s\n", (long)getpid(), argv[4]);
if((write(fd, argv[3], strlen(argv[3])+1)) == -1){
fprintf(stderr, "Failed to write %s to fd", argv[3]);
r_close(fd);
return 0;
}
if((write(fd, argv[1], strlen(argv[1])+1)) == -1){
fprintf(stderr, "Failed to write %s to fd", argv[1]);
r_close(fd);
return 0;
}
if((write(fd, argv[2], strlen(argv[2])+1)) == -1){
fprintf(stderr, "Failed to write %s to fd", argv[2]);
close(fd);
return 0;
}
fprintf(stderr, "Everything has been written\n");
return 0;
}
Server.c
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <limits.h>
#include <sys/types.h>
#include <sys/wait.h>
#include "func.h"
#include "uici.h"
int main(int argc, char *argv[])
{
u_port_t portnumber;
int listenfd;
int fd;
char client[MAX_CANON];
int bytes_copied;
pid_t child;
if (argc != 2) {
fprintf(stderr, "Usage: %s port\n", argv[0]);
return 1;
}
portnumber = (u_port_t) atoi(argv[1]);
if ((listenfd = u_open(portnumber)) < 0) {
perror("Listen endpoint creation failed");
return 1;
}
fprintf(stderr, "[%ld]: Waiting for the first connection on port %d\n",
(long)getpid(), (int)portnumber);
for ( ; ; ) {
if ((fd = u_accept(listenfd, client, MAX_CANON)) != -1) {
fprintf(stderr, "[%ld]: A connection has been received from %s\n",
(long) getpid(), client);
if ((child = fork()) == -1)
perror("Could not fork a child");
if (child == 0) { /* child code */
r_close(listenfd);
int MAXSZ = 1024;
char str3[MAXSZ];
char str1[MAXSZ];
char str2[MAXSZ];
int bytesread = 0;
fprintf(stderr, "Beginning the reads\n");
read(fd,str3, MAXSZ);
fprintf(stderr, "Finished 1st read\n");
read(fd,str1, MAXSZ);
fprintf(stderr, "Finished 2nd read\n");
read(fd,str2, MAXSZ);
fprintf(stderr, "str3: %s\n",str3);
fprintf(stderr, "str1 = %s\n",str1);
fprintf(stderr, "str2 = %s\n",str2);
close(fd);
return 0;
} else { /* parent code */
close(fd);
while (waitpid(-1, NULL, WNOHANG) > 0) ; /* clean up zombies */
}
}
else
perror("Accept failed");
}
}
First child forked closes the listener with r_close(listenfd);
Then when client send argv[1] no listener are available and, I think, u_accept(listenfd, client, MAX_CANON) return an error because of listenfd is not valid.
Note: There is no guarantee that read or write will read or write all the data from/to the file descriptor, and no guarantee that the data blocks the client is writing will be read in the same way on the server.
You have to make sure to check the bytes written and continue to write data to the fd until you have written them all. When reading, you have to have the client send some kind of header data describing the amount of data that is to be expected, or in your case, you could read 1 byte at a time and look for a '\0' character, indicating the end of a string.
Since MAXSZ is likely larger than the strings you are sending, the server could be reading all of the strings at once into the first buffer, then blocking on the subsequent read.
Related
Hi there i am trying to make such a program in which the the two fds stdin and fifo monitor by select() and communicate with each other.
select() will monitor either fifo ready for reading or stdin.
server.c
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <fcntl.h>
#include <sys/stat.h>
#include <string.h>
#include <sys/select.h>
int main(int argc,char *argv[]){
int f,fifo_read,fifo_write,status;
fd_set readset;
FD_ZERO(&readset);
char str[512]="start";
if(argc !=2)
if(argc != 2){
printf("\nError: %s required argument [Fifo Name]\n\n",argv[0]);
exit(EXIT_FAILURE);
}
if ((open(argv[1], O_RDWR)) < 0){
f = mkfifo(argv[1],S_IRWXU);
if(f<0){
perror("Error While Creating FIFO ");
exit(EXIT_FAILURE);
}
else
printf("FIFO Created Successfully...\n");
}
while(strcmp(str,"end")!=0){
fifo_write= open(argv[1],O_WRONLY);
FD_SET(fifo_read, &readset);
FD_SET(STDIN_FILENO, &readset);
status = select(fifo_read+1, &readset, NULL, NULL, NULL);
if(status==-1){
perror("Error While Calling select() system call ");
//exit(EXIT_FAILURE);
}
if(FD_ISSET(STDIN_FILENO,&readset)){
if(fifo_write<0)
perror("\nError while writing on pipe ");
else{
printf("\nServer>> ");
scanf("%s",str);
write(fifo_write,str,strlen(str));
close(fifo_write);
}
}
fifo_read=open(argv[1],O_RDONLY);
if(FD_ISSET(fifo_read,&readset)){
if(fifo_read<0)
perror("\nError while reading from pipe ");
else{
read(fifo_read,str,strlen(str));
close(fifo_read);
printf("\nJiya%s",str);
}
}
}
return 0;
}
client.c
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <fcntl.h>
#include <sys/stat.h>
#include <string.h>
#include <sys/select.h>
int main(int argc,char *argv[]){
int f,fifo_read,fifo_write,status;
fd_set readset;
FD_ZERO(&readset);
char str[512]="start";
while(strcmp(str,"end")!=0){
fifo_write= open(argv[1],O_WRONLY);
FD_SET(fifo_read, &readset);
FD_SET(STDIN_FILENO, &readset);
status = select(fifo_read+1, &readset, NULL, NULL, NULL);
if(status==-1){
perror("Error While Calling select() system call ");
//exit(EXIT_FAILURE);
}
if(FD_ISSET(fifo_read,&readset)){
if(fifo_read<0)
printf("\nError opening read pipe");
else{
read(fifo_read,str,strlen(str));
close(fifo_read);
printf("\n%s",str);
}
}
fifo_read=open(argv[1],O_RDONLY);
if(FD_ISSET(STDIN_FILENO,&readset)){
if(fifo_write<0)
printf("\nError opening write pipe");
else{
printf("\nClient>> ");
scanf("%s",str);
write(fifo_write,str,strlen(str));
close(fifo_write);
}
}
}
return 0;
}
I have posted a working, somewhat simplified version of your code below. This code reads from STDIN in the client and sends that input through the pipe created by mkfifo to the server. The server loops reading from its end of the pipe and echoes what it read to STDOUT.
Client output:
CLIENT > Message1
CLIENT > Message2
CLIENT > Yet another message.
CLIENT > A fourth message, but this one is quite a bit longer than the first messages.
CLIENT > end
Server output:
SERVER: Got 8 byte message: 'Message1'
SERVER: Got 8 byte message: 'Message2'
SERVER: Got 20 byte message: 'Yet another message.'
SERVER: Got 77 byte message: 'A fourth message, but this one is quite a bit longer than the first messages.'
EOF encountered...
I'll highlight some of the main differences:
I first create the pipe special file via mkfifo in the server. I then open the special file in the server and the client. This happens before the loop, and the file descriptors stay open during the loop instead of being repeatedly opened and closed.
The client only writes to the pipe and the server only reads from the pipe. At least on Linux, pipes are unidirectional (pipe man page: Pipes and FIFOs (also known as named pipes) provide a unidirectional interprocess communication channel. A pipe has a read end and a write end...) Two pipes may be used for bidirectional communication on Linux, and even on systems that support bidirectional pipes, using two pipes would still be supported and would be more portable.
When calling read, you should use the total size of the str buffer, not strlen(str). Also, str should be cleared between reads, so as to not contain old data (memset in new code).
I used fgets instead of scanf.
After the server opens the FIFO, I unlink the file, so that it is automatically deleted from the file system. The underlying file object will still exist until processes using it have terminated. This is optional.
Server:
#include <fcntl.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/select.h>
#include <sys/stat.h>
#include <unistd.h>
int main(int argc, char *argv[]) {
int fifo_read = -1;
char str[512]= "";
fd_set readset;
FD_ZERO(&readset);
if (argc != 2) {
printf("Usage: %s FIFO_NAME\n", argv[0]);
exit(EXIT_FAILURE);
}
/* Server will make the FIFO, both sides will open it */
if (mkfifo(argv[1], S_IRWXU) == -1) {
perror("mkfifo()");
exit(EXIT_FAILURE);
}
if ((fifo_read = open(argv[1], O_RDONLY)) == -1) {
perror("open()");
exit(EXIT_FAILURE);
}
if (unlink(argv[1]) == -1) {
perror("unlink()");
exit(EXIT_FAILURE);
}
while (1) {
FD_SET(fifo_read, &readset);
if (select(fifo_read + 1, &readset, NULL, NULL, NULL) == -1) {
perror("select()");
exit(EXIT_FAILURE);
}
if (FD_ISSET(fifo_read, &readset)) {
ssize_t bytes_read;
memset(str, 0, sizeof(str));
bytes_read = read(fifo_read, str, sizeof(str));
if (bytes_read == -1) {
perror("read()");
exit(EXIT_FAILURE);
}
else if (bytes_read == 0) {
printf("EOF encountered...\n");
break;
}
printf("SERVER: Got %ld byte message: '%s'\n", bytes_read, str);
}
}
if (close(fifo_read) == -1) {
perror("close()");
exit(EXIT_FAILURE);
}
exit(EXIT_SUCCESS);
}
Client:
#include <fcntl.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/select.h>
#include <sys/stat.h>
#include <unistd.h>
int main(int argc, char *argv[]) {
int fifo_write = -1;
char str[512] = "";
fd_set readset;
FD_ZERO(&readset);
if (argc != 2) {
printf("Usage: %s FIFO_NAME\n", argv[0]);
exit(EXIT_FAILURE);
}
if ((fifo_write = open(argv[1], O_WRONLY)) == -1) {
perror("open()");
exit(EXIT_FAILURE);
}
while (1) {
printf("CLIENT > ");
fflush(stdout);
FD_SET(STDIN_FILENO, &readset);
if (select(STDIN_FILENO + 1, &readset, NULL, NULL, NULL) == -1) {
perror("select()");
exit(EXIT_FAILURE);
}
if (FD_ISSET(STDIN_FILENO, &readset)) {
memset(str, 0, sizeof(str));
if (!fgets(str, sizeof(str), stdin)) {
printf("fgets() failed to read a line.\n");
exit(EXIT_FAILURE);
}
// See https://stackoverflow.com/questions/2693776/removing-trailing-newline-character-from-fgets-input
str[strcspn(str, "\r\n")] = 0;
if (strcmp(str, "end") == 0) {
break;
}
if (write(fifo_write, str, strlen(str)) == -1) {
perror("write()");
exit(EXIT_FAILURE);
}
}
}
if (close(fifo_write) == -1) {
perror("close()");
exit(EXIT_FAILURE);
}
exit(EXIT_SUCCESS);
}
Here i Got the Solution :)
server.c
#include <stdio.h>
#include <unistd.h>
#include <fcntl.h>
#include <stdlib.h>
#include <sys/select.h>
#include <string.h>
#include <sys/stat.h>
#include <errno.h>
int main(int argc, char *argv[])
{
if(argc != 2){
printf("\nError: %s required argument [Fifo Name]\n\n",argv[0]);
exit(EXIT_FAILURE);
}
int fd,wr,rd,ret;
fd_set readset;
if(mkfifo(argv[1],S_IRWXU)==-1){
if(errno!=EEXIST)
perror("Error unable to create FIFO ");
else
perror("Error unable to create FIFO ");
exit(EXIT_FAILURE);
}
else
printf("FIFO created Successfully!\n\n");
fd = open(argv[1],O_RDWR);
if(fd==-1){
perror("Error Failed to open fifo\n");
exit(EXIT_FAILURE);
}
while(!0){
FD_ZERO(&readset);
FD_SET(fd,&readset);
FD_SET(STDIN_FILENO,&readset);
sleep(1);
ret = select(fd+1,&readset,NULL,NULL,NULL);
if(ret==-1){
perror("Error select() ");
exit(EXIT_FAILURE);
}
char str[512]="";
if(FD_ISSET(STDIN_FILENO,&readset)){
//fprintf(stderr, ">> ");
rd = read(STDIN_FILENO,str,sizeof(str));
if(rd==-1){
perror("Error while reading from fifo");
exit(EXIT_FAILURE);
}
char temp[512]="Server :: ";
strcat(temp,str);
wr = write(fd,temp,sizeof(temp));
if(wr==-1){
perror("Error while writing to fifo ");
exit(EXIT_FAILURE);
}
continue;
}
if(FD_ISSET(fd,&readset)){
rd = read(fd,str,sizeof(str));
if(rd==-1){
perror("Error while reading from fifo");
exit(EXIT_FAILURE);
}else if(rd==0)
continue;
//fprintf(stderr,"P2: %s\n",str);
printf("%s\n",str);
//write(STDOUT_FILENO,str,sizeof(str));
}
}
}
client.c
#include <stdio.h>
#include <unistd.h>
#include <fcntl.h>
#include <stdlib.h>
#include <sys/select.h>
#include <string.h>
#include <sys/stat.h>
#include <errno.h>
int main(int argc, char *argv[])
{
if(argc != 2){
printf("\nError: %s required argument [Fifo Name]\n\n",argv[0]);
exit(EXIT_FAILURE);
}
int fd,wr,rd,ret;
fd_set readset;
fd = open(argv[1],O_RDWR);
if(fd==-1){
perror("Error Failed to open fifo\n");
exit(EXIT_FAILURE);
}
while(!0){
FD_ZERO(&readset);
FD_SET(fd,&readset);
FD_SET(STDIN_FILENO,&readset);
sleep(2);
ret = select(fd+1,&readset,NULL,NULL,NULL);
if(ret==-1){
perror("Error select() ");
exit(EXIT_FAILURE);
}
char str[512]="";
if(FD_ISSET(fd,&readset)){
rd = read(fd,str,sizeof(str));
if(rd==-1){
perror("Error while reading from fifo");
exit(EXIT_FAILURE);
}else if(rd==0)
continue;
//fprintf(stderr,"P2: %s\n",str);
printf("%s\n",str);
//write(STDOUT_FILENO,str,sizeof(str));
}
if(FD_ISSET(STDIN_FILENO,&readset)){
//fprintf(stderr, ">> ");
rd = read(STDIN_FILENO,str,sizeof(str));
if(rd==-1){
perror("Error while reading from fifo");
exit(EXIT_FAILURE);
}
char temp[512]="Client :: ";
strcat(temp,str);
wr = write(fd,temp,sizeof(temp));
if(wr==-1){
perror("Error while writing to fifo ");
exit(EXIT_FAILURE);
}
continue;
}
}
}
and the output is :
I am trying to write a code in C with named pipes (fifo), where the client is asking for information about a directory.
The server checks for the existence of the directory, and sends back the size of the directory, and the number of files and subdirectories.
The request can also specify to get the name of the files and subdirectories.
The client gets the name of the directory as an argument, also the specification by -d option.
The server executes a shell script in order to solve the problem.
I already asked a question about this topic and got some improvements in the code, but still can't get it running correctly.
Here is the link to the question: How to pass multiple arguments to client (fifo)?
My problem is now that the server prints out only one file name instead of all filenames and subdirectories inside the directory that was given as an argument to the client.
Here is the modified server code:
#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <string.h>
#include <stdlib.h>
#include <unistd.h>
#include "header.h"
int f;
Message msg;
int main() {
if (mkfifo(FIFONAME, S_IFIFO | 0666) < 0) { /*Creating server fifo*/
perror("Failed creating own fifo");
printf("Server: Failed creating fifo_%d file\n", getpid());
unlink(FIFONAME);
exit(1);
}
if ((f = open(FIFONAME, O_RDONLY)) < 0) {
perror("Failed opening fifo");
unlink(FIFONAME);
exit(1);
}
printf("Server is working\n");
while (1) { /*Infinite loop, waiting for client requests*/
if ((read(f, &msg, sizeof(msg)))) {
if (strcmp(msg.dir, "exit") == 0) {
close(f);
unlink(FIFONAME);
exit(1);
}
switch (fork()) {
case -1: {
perror("Fork error\n");
exit(1);
}
case 0: {
char command[MAXLEN];
sprintf(command,"./shell.sh %s %s", msg.dir, msg.spec);
FILE *g;
if ((g = popen(command, "r")) == NULL) {
perror("Popen error");
exit(1);
}
fgets(msg.dir, MAXLEN, g);
fgets(msg.spec, MAXLEN, g);
char result[MAXLEN];
sprintf(result, "fifo_%d", msg.pid);
msg.pid = getpid();
int op;
op = open(result, O_WRONLY);
write(op, &msg, sizeof(msg));
close(op);
exit(0);
}
}
}
}
return 0;
}
And the client code:
#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <string.h>
#include <stdlib.h>
#include <unistd.h>
#include "header.h"
int f, fc;
Message msg;
char fifoname[MAXLEN];
int main(int argc, char *argv[]) {
if (argc == 1) {
printf("Usage: %s directory name\n",argv[0]);
exit(1);
}
sprintf(fifoname, "fifo_%d", getpid());
if (strcmp(argv[1], "0"))
if (mkfifo(fifoname, S_IFIFO | 0666) < 0) { /*Creating own FIFO file for result*/
perror("Failed creating own clientfifo");
printf("Client error: Failed creating fifo_%d file\n", getpid());
exit(2);
}
if ((f = open(FIFONAME, O_WRONLY)) < 0) { /*Opening serverfifo for writing*/
perror("Failed connecting to server");
exit(3);
}
strcpy(msg.dir, argv[1]);
strcpy(msg.spec, argv[2]);
msg.pid = getpid();
write(f, &msg, sizeof(msg));
if (strcmp(argv[1], "exit")) { /* The client is not expecting any result
because the server stopped*/
if ((fc = open(fifoname, O_RDONLY)) < 0) { /*Opening own fifo for reading*/
perror("Failed opening own fifo");
printf("Client error: Failed opening own %s file\n", fifoname);
exit(4);
}
read(fc, &msg, sizeof(msg));
printf("Client %d, received: %s%s\n", getpid(), msg.dir, msg.spec);
close(fc);
}
unlink(fifoname);
close(f);
exit(0);
}
The common header file:
#include <unistd.h>
#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <string.h>
#include <stdlib.h>
#define FIFONAME "server_fifo"
#define MAXLEN 1000
typedef struct {
int pid; /*folyamat azonositoja*/
char dir[MAXLEN];
char spec[MAXLEN];
} Message;
And the output I get is:
-bash-4.1$ ./client dir -d
Client 42723, received: 16K,2 directories, 2 files
a
While it should look like this :
-bash-4.1$ ./client dir -d
Client 42723, received: 16K,2 directories, 2 files
a
b
dir1
dir2
What needs to be modified in order to get the full output?
The problem is at line 52 inside server.c.
You are using fgets() to copy the output to msg.spec.
But fgets() stops taking input at newline charater ('\n').
Hence you only see one result.
To overcome this, you can do something like:
char str[100]; // arbitrary length
while(fgets(str, MAXLEN, g))
{
strcat(msg.spec, str);
}
This keeps taking input every iteration and concatenates each line to previous output.
So, I'm trying to create a pipe that sends char arrays back and forth through pipes that connect through argv[]. Right now, I'm stuck at receiving the array (param which is sent to c_param from the parent to the child.) in interface.c to receiving the characters 3 and 5 at db.c. I know that 3 and 5 are the index for argv[] that my pipes are at, but I'm not sure how to take that and print out my message in db.c.
interface.c creates the pipes, forks into a parent process and a child process. The char array param is transfered to the child process to char array c_param. Using snprintf, I made my pipe into a char to send using execl with my char array c_param.
interface.c:
int main (int argc, char *argv[])
{
int to_Child[2];
int to_Parent[2];
int id, toChildPipe, toParentPipe, err;
char param[100] = "This is the parameter!";
char sendPipe[100];
char recPipe[100];
/*CREATING PIPE*/
toChildPipe = pipe(to_Child);
toParentPipe = pipe(to_Parent);
if(toChildPipe == -1 || toParentPipe == -1)
{
printf ("Error on pipe creation: %d", errno);
exit (1);
}
/*Creating Child Process*/
id = fork();
if(id == 0)
{
/**
*
* IN THE CHILD Process
*
*/
close(to_Child[1]); //reading
close(to_Parent[0]); //writing
char c_param[100];
toChildPipe = read(to_Child[0], c_param, 100);
if (toChildPipe == -1)
{
//If failed
printf("Error on read from pipe from parent: %d\n",errno);
//exit with error
exit(2);
}//Error pipe from parent
snprintf(sendPipe,sizeof(sendPipe), "%d",to_Parent[0]);
snprintf(recPipe,sizeof(recPipe), "%d",to_Child[0]);
err = execl("./db","db",sendPipe,recPipe,(char *)0);
if(err == -1)
{
printf("Error on execl: %d\n", errno);
}//Error execl
toChildPipe = read(to_Child[0], c_param, 100);
if (toChildPipe == -1)
{
//If failed
printf("Error on read from pipe from parent: %d\n",errno);
//exit with error
exit(2);
}//Error pipe from parent
}//CHILD PROCESS
else if (id > 0)
{
/**
*
*IN THE PARENT PROCESS
*
*/
close(to_Child[0]); //writing
close(to_Parent[1]); //reading
toChildPipe = write(to_Child[1],param,100);
if(toChildPipe == -1)
{
printf("Error on write to pipe: %d", errno);
exit(3);
}
/*Piping was successful!*/
exit(0);
}//PARENT PROCESS
else
{
exit(4);
}
}
db.c started up from interface.c execl and should receive the parameters over argv[], which then should print it out.
db.c
#include <errno.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/types.h>
#include <unistd.h>
int main(int argc, char *argv[])
{
FILE *finput;
int j = 0;
int fd;
int toChildPipe;
char c_param[100];
if(argc > 1)
{
for(j ; j < argc ; j++)
printf("argv = %s\n", argv[j]);
printf("argc = %d\n",argc);
}
fd = atoi(argv[1]);
printf("Statement: %s\n", argv[fd]);
strcpy(c_param, argv[3]);
printf("filename: %s\n", c_param);
}
This is the current output I'm getting, I'm aware that 5 and 3 are the indexes I need to send a message and receive the message that I'm currently trying to print in db.c
output(db.c):
argv = db
argv = 5
argv = 3
argc = 3
Statement: TERM=xterm
I hope I gave you enough information, I appreciate any help you are willing to give me. Thank you in advance!
There were lots of little things wrong. Your biggest problems were your assumptions/assertions in db.c about the parameters passed to it by interface.c — there was a total mismatch between what was passed and what was expected. There was also a good deal of extraneous code in interface.c. In particular, the child read from the pipe before executing db, so there was nothing left on the pipe for db to read.
Here's the 'fixed' code, with some debug code still in place.
interface.c
#include <errno.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
int main(void)
{
int to_Child[2];
int to_Parent[2];
int id;
char param[100] = "This is the parameter!";
char sendPipe[100];
char recPipe[100];
if (pipe(to_Child) == -1 || pipe(to_Parent) == -1)
{
printf("Error on pipe creation: %d", errno);
exit(1);
}
printf("Pipes: C(%d,%d), P(%d,%d)\n", to_Child[0], to_Child[1], to_Parent[0], to_Parent[1]);
id = fork();
if (id == 0)
{
close(to_Child[1]); // Child does not write to itself
close(to_Parent[0]); // Child does not read what it writes
snprintf(sendPipe, sizeof(sendPipe), "%d", to_Parent[1]);
snprintf(recPipe, sizeof(recPipe), "%d", to_Child[0]);
execl("./db", "db", sendPipe, recPipe, (char *)0);
fprintf(stderr, "Error on execl: %d\n", errno);
exit(2);
}
else if (id > 0)
{
close(to_Child[0]); // Parent does not read childs input
close(to_Parent[1]); // Parent does not
int nbytes = write(to_Child[1], param, 100);
if (nbytes == -1)
{
fprintf(stderr, "Error on write to pipe: %d\n", errno);
exit(3);
}
close(to_Child[1]);
if ((nbytes = read(to_Parent[0], param, 100)) <= 0)
{
fprintf(stderr, "Error on read from pipe: %d\n", errno);
exit(5);
}
printf("Data from pipe: [%.*s]\n", nbytes, param);
exit(0);
}
else
{
perror("fork failed");
exit(4);
}
}
### db.c
#include <assert.h>
#include <errno.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
int main(int argc, char *argv[])
{
printf("argc = %d\n", argc);
for (int j = 0; j < argc; j++)
printf("argv[%d] = %s\n", j, argv[j]);
if (argc != 3)
{
fprintf(stderr, "Usage: %s write-fd read-fd\n", argv[0]);
return 1;
}
int ofd = atoi(argv[1]);
int ifd = atoi(argv[2]);
printf("ifd = %d; ofd = %d\n", ifd, ofd);
char c_param[100];
int nbytes = read(ifd, c_param, sizeof(c_param));
if (nbytes <= 0)
{
fprintf(stderr, "Error: failed to read any data (%d)\n", errno);
return 1;
}
printf("Child: [%.*s]\n", nbytes, c_param);
assert(strlen(c_param) + sizeof(" - sent back to parent") <= sizeof(c_param));
strcat(c_param, " - sent back to parent");
if (write(ofd, c_param, nbytes) != nbytes)
{
fprintf(stderr, "Error: failed to write all the data (%d)\n", errno);
return 1;
}
return 0;
}
Sample run
Pipes: C(3,4), P(5,6)
argc = 3
argv[0] = db
argv[1] = 6
argv[2] = 3
ifd = 3; ofd = 6
Child: [This is the parameter!]
Data from pipe: [This is the parameter! - sent back to parent]
Note that the code reports errors to standard error (that's what it is for). It also delimits the printed data which can make it easier to spot
unexpected problems. It doesn't assume that the data is null padded; it limits the length printed to the length read, though in fact the data has numerous nulls at the end.
In a client-server program, need check EOF for read() on a FIFO?
Questions:
Does EOF in FIFO return 0, or -1 with errno set?
Does the rule also apply to other IPC facilities?
#Update
I still found the result wield, so need to continue ask about it.
Following are the source code:
cs_fifo.h:
// fifo header
#ifndef _CS_FIFO
#define _CS_FIFO
#define CLIENT_DATA_SIZE 2
#define SERVER_DATA_SIZE 10
#define SERVER_FIFO_PATH "/tmp/server_fifo"
#define CLIENT_COUNT 3
#endif
fifo_server.c:
// client - server fifo, server part,
#include <stdio.h>
#include <fcntl.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <unistd.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>
#include "cs_fifo.h"
int fifo_server() {
int flag;
int fd;
char buf[CLIENT_DATA_SIZE];
// remove fifo, before create
remove(SERVER_FIFO_PATH);
// create fifo
mode_t mode = 0644;
if((flag = mkfifo(SERVER_FIFO_PATH, mode)) == -1) {
printf("error while mkfifo(): %s\n", strerror(errno));
return -1;
}
printf("server fifo created, path: %s\n", SERVER_FIFO_PATH);
// open for read
if((fd = open(SERVER_FIFO_PATH, O_RDONLY)) == -1) {
printf("error while open(): %s\n", strerror(errno));
exit(-1);
}
// loop to receive data from client,
while(1) {
// read from fifo
if((flag = read(fd, buf, CLIENT_DATA_SIZE)) == -1) {
printf("error while read(): %s\n", strerror(errno));
exit(0);
} else if(flag == 0) { // no data
printf("no data\n");
sleep(1);
continue;
}
// data received,
printf("receive data: %s\n", buf);
// send data back to client's fifo,
// TODO
}
// remove fifo, after finish using,
remove(SERVER_FIFO_PATH);
return 0;
}
int main(int argc, char * argv[]) {
return fifo_server();
}
fifo_client.c:
// client - server fifo, client pool part,
#include <stdio.h>
#include <fcntl.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <unistd.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>
#include "cs_fifo.h"
int fifo_client_pool() {
int flag;
int server_fd;
char data[CLIENT_DATA_SIZE];
int i = 0;
pid_t cpid;
char identity;
// open for write
if((server_fd= open(SERVER_FIFO_PATH, O_WRONLY)) == -1) {
printf("error while open(): %s\n", strerror(errno));
exit(-1);
}
// create child processes as clients,
while(i < CLIENT_COUNT) {
switch(cpid=fork()) {
case -1: // failed
printf("error while fork(): %s\n", strerror(errno));
exit(errno);
case 0: // success, child process goes here
printf("child process created, pid [%d], parent pid [%d]\n",(int)getpid(), (int)getppid());
identity = i + 65; // start from 'A'
// prepare data
data[0] = identity;
data[1] = '\0';
// write to fifo
if((flag = write(server_fd, data, CLIENT_DATA_SIZE)) == -1) {
printf("[%c] error while write(): %s\n", identity, strerror(errno));
_exit(-1);
}
printf("[%c] send data to server\n", identity);
_exit(0);
break;
default: // success, parent process goes here
// sleep a while,
sleep(1);
break;
}
i++;
}
if((flag = close(server_fd)) != 0) {
printf("error while close(): %s\n", strerror(errno));
}
return 0;
}
int main(int argc, char * argv[]) {
return fifo_client_pool();
}
Compile:
Server: gcc -Wall fifo_server.c -o server
Client: gcc -Wall fifo_client_pool.c -o client_pool
Execute:
First start server: ./server
Then start client pool: ./client_pool
Result:
Server start, and blocks before client start.
Then client start, and server receive 1 request from each of the 3 clients, that's 3 in total.
Then all client processes terminated, then server's read() continue return 0 without blocked.
The future question is:
After all clients terminated, shouldn't server's read() block? Since it's in blocking mode?
All reads from a descriptor where read returns zero means "closed" or "end".
If you have a blocking descriptor (the default) then read will block if there's currently nothing to read. If the descriptor is non-blocking then read returns -1 with errno set to EAGAIN or EWOULDBLOCK if there's nothing to read.
It's a homework question. I have 3 programs A, B and C, they are not father/child processes, but separate programs. B must write a message ("Hello") to the stdin of A and read another message ("Hello") from stdout of C. Which concept i should use to implement it? I looked around for some time but i couldn't find anything proper. I thought i should use FIFO, but i couldn't redirect pipes. Can i use dup2 with FIFO? if yes, how? It's not the homework's itself, it just the way it should work. Then I will implement other things on it. (I can post my fifo base code, if it's the case, just don't do it now because i'm not sure)
Let me know if I missunderstood the question.
Let's say you create programs A, B and C. Programmatically, you could use man 1 mkfifo or man 3 mkfifo, to create named pipes.
Then each one of your processes would open(2) them and use dup2(2) according to their needs.
For instance, program A could redirect its stdout like this:
int fifo = open("fifo_1", O_WRONLY);
dup2(fifo, 1);
or program B could redirect both its stdin and stdout like this:
int fifo_in = open("fifo_1", O_RDONLY);
int fifo_out = open("fifo_2", O_WRONLY);
dup2(fifo_in, 0);
dup2(fifo_out, 1);
or whatever else you need.
I modified this code as #chrk explained. It's a FIFO example from the book "Advanced Unix Programming". A simple server-client example. Client sends three lowerletter strings to the server, server make them upperletter and send back to the client via FIFO("fifo#clientpid" is the name of client's fifo). Client prints them to its stdout. So i modified it in a way that after receiving the message from client, server writes upperletter strings to the STDINT of the client. And client reads them from STDINT and print to its stdout. It works as i expected. Thanks for all help. First i implemented in a wrong way. With the help of chrk's code i wrote it again. Here is the code:
client's output
client 2941 started
client 2941: applesauce --> APPLESAUCE
client 2941: tiger --> TIGER
client 2941: mountain --> MOUNTAIN
Client 2941 done
server:
#include <stdio.h>
#include <stdlib.h>
#include <errno.h>
#include <unistd.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <ctype.h>
#define ec_neg1(s,m) if((s) == -1) {perror(m); exit(errno);}
#define SERVER_FIFO_NAME "fifo_server"
#define PERM_FILE 0664
struct simple_message {
pid_t sm_clientpid;
char sm_data[200];
};
int main()
{
int fd_server, fd_client, i;
ssize_t nread;
struct simple_message msg;
char fifo_name[100];
printf("server started\n");
if (mkfifo(SERVER_FIFO_NAME, PERM_FILE) == -1 && errno != EEXIST)
{perror("can't make fifo"); exit(errno); }
ec_neg1( fd_server = open(SERVER_FIFO_NAME, O_RDWR), "cant open fd_server" )
while (1)
{
ec_neg1( nread = read(fd_server, &msg, sizeof(msg)), "can't read from fd_server")
if (nread == 0) {
errno = ENETDOWN;
perror("nread == 0"); exit(errno);
}
for (i = 0; msg.sm_data[i] != '\0'; i++)
msg.sm_data[i] = toupper(msg.sm_data[i]);
make_fifo_name(msg.sm_clientpid, fifo_name, sizeof(fifo_name));
ec_neg1( fd_client = open(fifo_name, O_WRONLY), "can't open fifo_name" )
ec_neg1( write(fd_client, &msg, sizeof(msg)), "can't write to fd_client" )
ec_neg1( close(fd_client), "can't close fd_client" )
}
/* never actually get here */
ec_neg1( close(fd_server), "can't close fd_server" )
exit(EXIT_SUCCESS);
return 0;
}
int make_fifo_name(pid_t pid, char *name, size_t name_max)
{
snprintf(name, name_max, "fifo%ld", (long)pid);
return 0;
}
client
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>
#include <unistd.h>
#include <sys/stat.h>
#include <fcntl.h>
#define ec_neg1(s,m) if((s) == -1) {perror(m); exit(errno);}
#define SERVER_FIFO_NAME "fifo_server"
#define PERM_FILE 0664
struct simple_message {
pid_t sm_clientpid;
char sm_data[200];
};
int make_fifo_name(pid_t pid, char *name, size_t name_max)
{
snprintf(name, name_max, "fifo%ld", (long)pid);
return 0;
}
int main()
{
int fd_server, fd_client = -1, i;
ssize_t nread;
struct simple_message msg;
char fifo_name[100];
char *work[] = {
"applesauce",
"tiger",
"mountain",
NULL
};
printf("client %ld started\n", (long)getpid());
msg.sm_clientpid = getpid();
make_fifo_name(msg.sm_clientpid, fifo_name,
sizeof(fifo_name));
if (mkfifo(fifo_name, PERM_FILE) == -1 && errno != EEXIST)
{perror("can't make fifo"); exit(errno); }
ec_neg1( fd_server = open(SERVER_FIFO_NAME, O_WRONLY), "can't open fd_server" )
for (i = 0; work[i] != NULL; i++)
{
strcpy(msg.sm_data, work[i]);
ec_neg1( write(fd_server, &msg, sizeof(msg)),"can't write to fd_server" )
if (fd_client == -1){
ec_neg1( fd_client = open(fifo_name, O_RDWR), "can't open fifo_name" )
ec_neg1(dup2(fd_client, 0), "can't duplicate stdin")
}
ec_neg1( nread = read(0, &msg, sizeof(msg)), "can't read from fd_client" )
if (nread == 0) {
errno = ENETDOWN;
perror("nread == 0"); exit(errno);
}
printf("client %ld: %s --> %s\n", (long)getpid(),
work[i], msg.sm_data);
}
ec_neg1( close(fd_server), "can't close fd_server" )
ec_neg1( close(fd_client), "can't close fd_client" )
ec_neg1( unlink(fifo_name), "can't unlink fifo_name" )
printf("Client %ld done\n", (long)getpid());
exit(EXIT_SUCCESS);
}