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.
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 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.
I am trying to share a socket descriptor with another local process on the same Linux machine. These processes are "unrelated" i.e., they are not parent/child related and are not forked. They are independent. Ultimately, the flow I want is something like:
| [Process1]
| -> master_socket = socket()
| -> setsockopt(master_socket...)
| -> fcntl(master_socket...)
| -> bind(master_socket...)
| -> listen(master_socket...)
| -> Share master_socket with Process2
|
| [Process2]
| -> Receive and store master_socket
| -> Use select() on master_socket
| -> Use accept() on master_socket to receive connections...
Based on a few related threads, it seems as though this is possible using a Unix domain socket which will track that the socket handle was sent from Process1 to Process2 in the kernel, giving it permission (e.g., here, here, and here).
What I am trying to determine is whether or not the descriptor is possible to share over a POSIX message queue. Oddly enough, if I create the socket BEFORE opening the queue, it seems to work OK. However, if I create the socket AFTER opening the queue, the descriptor read on Process2 comes up as "invalid."
Example Programs
Here is a sample program that sends a socket descriptor via a message queue. If init_socket() is called before open_queue(), then the descriptor received is valid. If visa-versa then it comes across invalid.
send.c: gcc -o send send.c -lrt
#include <stdio.h>
#include <stdlib.h>
#include <stdint.h>
#include <unistd.h>
#include <syslog.h>
#include <signal.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <sys/fcntl.h>
#include <string.h>
#include <errno.h>
#include <mqueue.h>
int init_socket();
int open_queue();
mqd_t msgq_id;
int main() {
int socket;
char msg_str[16];
// HERE: ordering matters. If open_queue() is done before init_socket(), then
// the descriptor is received invalid. If init_socket() is called BEFORE open_queue()
// then the descriptor received is valid.
open_queue();
socket = init_socket();
// Put the socket on the queue
memset(msg_str, '\0', sizeof(msg_str));
snprintf(msg_str, sizeof(msg_str), "%d", socket);
if(mq_send(msgq_id, msg_str, strlen(msg_str)+1, 1) == -1) {
printf("Unable to send the message on the queue: %s\n", strerror(errno));
return -1;
}
}
int open_queue() {
// Create a queue to share the socket
if(msgq_id = mq_open("/share_socket", O_RDWR | O_CREAT | O_EXCL, S_IRWXU | S_IRWXG, NULL)==-1) {
if(errno != EEXIST) {
printf("Failed to create IPC queue: %s\n", strerror(errno));
return -1;
}
// Re-open the already existing queue.
if((msgq_id = mq_open("/share_socket", O_RDWR)) != -1) {
printf("Re-opened the IPC queue: %s\n", "/share_socket");
} else {
printf("Failed to re-open IPC queue %s: %s\n", "/share_socket", strerror(errno));
return -1;
}
}
return 1;
}
int init_socket() {
int master_socket;
int opt=1;
struct sockaddr_in loc_addr = { 0 };
// Create the high level master socket
if( (master_socket = socket(AF_INET, SOCK_STREAM, 0)) == 0) {
printf("Unable to create master_socket\n");
return EXIT_FAILURE;
}
// Set socket to accept multiple connections
if( setsockopt(master_socket, SOL_SOCKET, SO_REUSEADDR, (char *)&opt, sizeof(opt)) < 0 ) {
printf("Error setting socket to accept multiple connections\n");
return EXIT_FAILURE;
}
// Set the socket type
bzero(&loc_addr, sizeof(struct sockaddr_in));
loc_addr.sin_family = AF_INET;
loc_addr.sin_addr.s_addr = htonl(INADDR_ANY);
loc_addr.sin_port=htons(1200);
// Set the socket to nonblocking
if (fcntl(master_socket, F_SETFL, O_NDELAY) < 0) {
printf("Can't set socket to non-blocking\n");
return EXIT_FAILURE;
}
// Bind to the socket
if (bind(master_socket, (struct sockaddr *)&loc_addr, sizeof(loc_addr)) < 0) {
return EXIT_FAILURE;
}
// Now, listen for a maximum of 6 pending clients
if(listen(master_socket, 6) < 0) {
printf("Could not set the socket to listen\n");
close(master_socket);
return EXIT_FAILURE;
}
return master_socket;
}
read.c: gcc -o read read.c -lrt
#include <stdio.h>
#include <stdlib.h>
#include <stdint.h>
#include <unistd.h>
#include <syslog.h>
#include <signal.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <sys/fcntl.h>
#include <string.h>
#include <errno.h>
#include <mqueue.h>
#define MAX_MSG_LEN 10000
int main() {
mqd_t msgq_id;
int master_socket;
unsigned int sender;
int bytes;
char msgcontent[MAX_MSG_LEN];
// Re-open the already existing queue.
if((msgq_id = mq_open("/share_socket", O_RDWR)) != -1) {
printf("Re-opened the IPC queue: %s\n", "/share_socket");
} else {
printf("Failed to re-open IPC queue %s: %s\n", "/share_socket", strerror(errno));
return -1;
}
// Try to read from the queue.
if((bytes = mq_receive(msgq_id, msgcontent, MAX_MSG_LEN, &sender)) == -1)
{
// If the failure was due to there being no messages, just return 0.
if(errno==EAGAIN)
return 0;
printf("Unable to read from the queue\n");
return -1;
}
sscanf(msgcontent, "%d", &master_socket);
printf("Got master socket value: %d\n", master_socket);
if(master_socket != 0 && (fcntl(master_socket, F_GETFD) != -1 || errno != EBADF))
printf("... socket is valid\n");
else
printf("... SOCKET IS INVALID\n");
return 1;
}
How to run: Just go ahead and run ./read and it will wait on the message queue and then run ./send. If you compiled send.c with init_socket() before open_queue(), you will see:
$ ./read
Re-opened the IPC queue: /share_socket
Got master socket value: 3
... socket is valid
Otherwise:
$ ./read
Re-opened the IPC queue: /share_socket
Got master socket value: 4
... SOCKET IS INVALID
What would cause this behavior where the ordering is important?
Problem One:
Both programs will start with file descriptors 0, 1, & 2 open and valid.
When you run send with init_socket first the returned file descriptor for the socket witl be 3. Then you open the queue which will be 4. You send 3 to read process.
In read you open the queue which will be file descriptor 3. You read the queue and find you were sent a 3, which is actually the fd of the queue. So you are "testing" the queue file descriptor.
Conversely when in send you open the queue first and then the socket you are sending the file descriptor 4 to read. There is no file descriptor 4 open in read (the queue will still be opened as 3) so it naturally fails the "test".
Problem 2:
The test is wrong.
if (master_socket != 0 && (fcntl(master_socket, F_GETFD) != -1 || errno != EBADF))
printf("... socket is valid\n");
else
printf("... SOCKET IS INVALID\n");
All this is telling you is that it isn't file descriptor 0 and you can read the file descriptor flags. A test that the message queue file descriptor will pass.
A better test is something like this:
void isSocket(int fd)
{
struct stat statbuf;
if (fstat(fd, &statbuf) == -1)
{
perror("fstat");
exit(1);
}
if (S_ISSOCK(statbuf.st_mode))
printf("%d is a socket\n", fd);
else
printf("%d is NOT a socket\n", fd);
}
I haven't tested your pgms but if you print out all your file descriptors from both programs as you open them and try the above test it should bare out what I am saying.
I am trying to make this work but no luck, basically i need to write to the pipe and then make the pipe return back with the text i sent. I have a server.c and client.c , so i make the server.c run..., open a new terminal and then run the client.. the problem is that the client doesnt do anything when i run it.. I am sure i am missing something.. like closing the pipe. i am not sure.. I would really appreciate some guidance
server.c
#include <stdio.h>
#include <errno.h>
#include <ctype.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <string.h>
#define PIPE1 "PIPE1"
#define PIPE5 "PIPE5"
#define MAX_BUF_SIZE 255
int main(int argc, char *argv[])
{
int rdfd1,rdfd2,rdfd3,rdfd4, wrfd1,wrfd2,wrfd3,wrfd4,ret_val, count, numread1,numread2,numread3,numread4;
char buf1[MAX_BUF_SIZE];
char buf2[MAX_BUF_SIZE];
char buf3[MAX_BUF_SIZE];
char buf4[MAX_BUF_SIZE];
/* Create the first named - pipe */
ret_val = mkfifo(PIPE1, 0666);
if ((ret_val == -1) && (errno != EEXIST)) {
perror("Error creating the named pipe");
return 1;
}
ret_val = mkfifo(PIPE5, 0666);
if ((ret_val == -1) && (errno != EEXIST)) {
perror("Error creating the named pipe");
return 1;
}
/* Open the first named pipe for reading */
rdfd1 = open(PIPE1, O_RDONLY);
/* Open the first named pipe for writing */
wrfd1 = open(PIPE5, O_WRONLY);
/* Read from the pipes */
numread1 = read(rdfd1, buf1, MAX_BUF_SIZE);
buf1[numread1] = '0';
printf("Server : Read From the pipe : %sn", buf1);
/*
* Write the converted content to
* pipe
*/
write(wrfd1, buf1, strlen(buf1));
}
client.c
#include <errno.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <ctype.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#define PIPE1 "PIPE1"
#define PIPE5 "PIPE5"
#define MAX_BUF_SIZE 255
int main(int argc, char *argv[ ]) {
pid_t childpid;
int error;
int i;
int nprocs;
/* check command line for a valid number of processes to generate */
int wrfd1, rdfd1, numread;
char rdbuf[MAX_BUF_SIZE];
if ( (argc != 2) || ((nprocs = atoi (argv[1])) <= 0) ) {
fprintf (stderr, "Usage: %s nprocs\n", argv[0]);
return 1;
}
for (i = 1; i < nprocs; i++) {
/* create the remaining processes */
if ((childpid = fork()) == -1) {
fprintf(stderr, "[%ld]:failed to create child %d: %s\n", (long)getpid(), i, strerror(errno));
return 1;
}
/* Open the first named pipe for writing */
wrfd1 = open(PIPE5, O_WRONLY);
/* Open the second named pipe for reading */
rdfd1 = open(PIPE1, O_RDONLY);
if (childpid)
break;
char string1[100];
if(sprintf(string1, "This is process %d with ID %ld and parent id %ld\n", i, (long)getpid(), (long)getppid())) {
write(wrfd1,string1, strlen(string1));
}
/* Read from the pipe */
numread = read(rdfd1, rdbuf, MAX_BUF_SIZE);
rdbuf[numread] = '0';
printf("Full Duplex Client : Read From the Pipe : %sn", rdbuf);
}
return 0;
}
It seems like both server and client read from PIPE1 and write to PIPE5. Shouldn't one of them write to PIPE1 so that the other can read it from the other end?
Also, if you're testing with ./client 1, your for (i = 1; i < nprocs; i++) loop will never execute.
One last thing, see this question. I'm not entirely sure it applies to your code, but it's worth keeping in mind.
Shouldn't this line be '\0' ?
buf1[numread1] = '0';