Order of opening named pipes causing possible race condition? - c

I am trying to create a very basic client server communication between two processes using IPC via named pipes.
I have 2 pipes, namely fifo_client and fifo_server
I have the following two classes fifoclient.c and fifoserver.c that has the following lines of code to open the two pipes.
fifoclient.c
int client = open("fifo_client",O_WRONLY);
int server = open("fifo_server",O_RDONLY);
fifoserver.c
int client = open("fifo_client",O_RDONLY);
int server = open("fifo_server",O_WRONLY);
However, on simply changing the order of opening the client and server pipes in fifoserver.c, the program freezes.
This is how the code is written when it freezes:
fifoserver.c
int server = open("fifo_server",O_WRONLY);
int client = open("fifo_client",O_RDONLY);
Notice that the server pipe is opened before the client pipe in this case. This leads to the program not responding (possible race condition?).
Can someone explain what is happening and why?
EDIT:
Here's the entire code for both the classes:
fifoserver.c
#define BUFSIZE 20
#include<stdio.h>
#include<fcntl.h>
int main()
{
char buf[BUFSIZE];
int client = open("fifo_client",O_RDONLY);
int server = open("fifo_server",O_WRONLY);
if( server<0 || client < 0)
{
printf("Couldn't open file\n");
exit(1);
}
read(client,buf,BUFSIZE*sizeof(char));
printf("Client Says: %s\n",buf);
write(server,"Fine, Thank You!",BUFSIZE*sizeof(char));
close(server);
close(client);
return 0;
}
fifoclient.c
#define BUFSIZE 20
#include<stdio.h>
#include<fcntl.h>
int main()
{
char buf[BUFSIZE];
int client = open("fifo_client",O_WRONLY);
int server = open("fifo_server",O_RDONLY);
if(client <0 || server <0)
{
printf("ERROR! Couldn't open file!\n");
exit(1);
}
write(client,"Hello! How are you?",BUFSIZE*sizeof(char));
read(server,buf,BUFSIZE*sizeof(char));
printf("Server Says: %s\n",buf);
close(server);
close(client);
return 0;
}

From man 7 fifo:
The kernel maintains exactly one pipe object for each FIFO special file that is
opened by at least one process. The FIFO must be opened on both ends (reading and
writing) before data can be passed. Normally, opening the FIFO blocks until the
other end is opened also.
In other words, your open() call will block until there is a process on the other end of the pipe. This is not a race condition -- rather, it is a deadlock. If the processes do not open the pipes in the same order, they will wait forever on one another. So, as you noticed, the solution is that they must both open the fifos in the same order.

fifoclient.c
int client = open("fifo_client",O_WRONLY);
This open in the client will block until the FIFO is opened for reading.
fifoserver.c
int client = open("fifo_client",O_RDONLY);
This open, in the server, will unblock the previous open in the client.
Now, when you swap the lines in the server to look like
int server = open("fifo_server",O_WRONLY);
int client = open("fifo_client",O_RDONLY);
the client is blocked opening the client FIFO but the server is trying to open the server FIFO for writing (which will block until somebody opens it for reading). None of them can proceed to the line which will unblock the other.

Related

Designing a proxy with non-blocking pipe forwarding to another server

I have written a proxy which also duplicates traffic. I am trying to duplicate network traffic to a replica server which should receive all the inputs and also process all the requests. However only the responses on the main server are visible to the client. The high level workflow is as follows
Thread 1. Take input from client forward it to a pipe in non-blocking way, and to the server
Thread 2. Read from server and send to client
Thread 3. Read from pipe and forward to replica server
Thread 4. Read from replica server and drop
The code is available in this gist: https://gist.github.com/nipunarora/679d49e81086b5a75195ec35ced646de
The test seems to work for smaller data and transactions, but I seem to be getting the following error when working with iperf and larger data sets:
Buffer overflow? : Resource temporarily unavailable
The specific part in the code where the problem is stemming from:
void forward_data_asynch(int source_sock, int destination_sock) {
char buffer[BUF_SIZE];
int n;
//put in error condition for -1, currently the socket is shutdown
while ((n = recv(source_sock, buffer, BUF_SIZE, 0)) > 0)// read data from input socket
{
send(destination_sock, buffer, n, 0); // send data to output socket
if( write(pfds[1],buffer,n) < 0 )//send data to pipe
{
//fprintf(stats_file,"buffer_overflow \n");
//printf("format string" ,a0,a1);
//int_timeofday();
perror("Buffer overflow? ");
}
//DEBUG_PRINT("Data sent to pipe %s \n", buffer);
}
shutdown(destination_sock, SHUT_RDWR); // stop other processes from using socket
close(destination_sock);
shutdown(source_sock, SHUT_RDWR); // stop other processes from using socket
close(source_sock);
}
The reading process is as follows:
void forward_data_pipe(int destination_sock) {
char buffer[BUF_SIZE];
int n;
sleep(10);
//put in error condition for -1, currently the socket is shutdown
while ((n = read(pfds[0], buffer, BUF_SIZE)) > 0)// read data from pipe socket
{
//sleep(1);
//DEBUG_PRINT("Data received in pipe %s \n", buffer);
send(destination_sock, buffer, n, 0); // send data to output socket
}
shutdown(destination_sock, SHUT_RDWR); // stop other processes from using socket
close(destination_sock);
}
Please note, the pipe has been defined as follows:
/** Make file descriptor non blocking */
int setNonblocking(int fd)
{
int flags;
/* If they have O_NONBLOCK, use the Posix way to do it */
#if defined(O_NONBLOCK)
/* Fixme: O_NONBLOCK is defined but broken on SunOS 4.1.x and AIX 3.2.5. */
if (-1 == (flags = fcntl(fd, F_GETFL, 0)))
flags = 0;
return fcntl(fd, F_SETFL, flags | O_NONBLOCK);
#else
/* Otherwise, use the old way of doing it */
flags = 1;
return ioctl(fd, FIOBIO, &flags);
#endif
}
Could anyone help in fixing what could be the reason of the error?
The problem in your case is that data is sent too fast to the socket that has been set to non-blocking mode. You have several options:
Accept the fact that data may be lost. If you do not want to delay the processing on the main server, this is your only option.
Don't set the socket to non-blocking mode. The default mode, blocking, seems like a better fit for your application if you don't want data to be lost. However, this will also mean that the system may be slowed down.
Use poll(), select(), kqueue(), epoll(), /dev/poll or similar to wait until the socket has enough buffer space available. However, when using this, you should consider why you set the socket to non-blocking mode in the first place if you nevertheless want to block on it. This also leads to slowdown of the system.

Linux : Creating a simple non-blocking server and client to communicate over named pipes

I am trying to create a simple example of named pipes (FIFO). Here, the server will listen for message from the client, which writes on the named pipe, common to both of them. The special thing to be implemented is that the FIFO should be non-blocking (usage of O_NONBLOCK).
By non blocking, I mean that the writer should return immediately after writing, if there is no reader. Similarly, reader should return immediately if there is no message(no writer).
I have created the blocking version though and its working fine. I am then trying to convert it to non-blocking.
Here's the client :
#include <stdio.h>
#include <stdlib.h>
#include <fcntl.h>
#define FIFO "/tmp/myFIFO"
/*
This acts as the client, writing to the FIFO
*/
int main(int argc, char *argv[])
{
FILE *fp;
int fifo_fd;
if(argc != 2)
{
printf("Usage : ./fifo_client <message> \n");
exit(1);
}
fifo_fd = open(FIFO, O_WRONLY | O_NONBLOCK);
if(fifo_fd < 0)
{
perror("Error while open call");
exit(1);
}
fp = fdopen(fifo_fd, "w");
if(fp == NULL)
{
perror("Error while opening fd");
exit(1);
}
fputs(argv[1],fp);
/* Close the fp */
fclose(fp);
return 0;
}
Here's the server :
#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <unistd.h>
#include <fcntl.h>
#define FIFO "/tmp/myFIFO"
/*
This acts as a server waiting for strings to be written by the client, over the FIFO
*/
int main()
{
FILE *fp;
int fifo_fd;
char buf[1024];
/* Create a FIFO */
umask(0);
if(mkfifo(FIFO,0666) < 0) /* 0666 is read and write permission*/
{
perror("Error creating FIFO");
exit(1);
}
while(1) /*endless wait, keep reading strings and print*/
{
fifo_fd = open(FIFO, O_RDONLY | O_NONBLOCK);
if(fifo_fd < 0)
{
perror("Error while open call");
exit(1);
}
fp = fdopen(fifo_fd, "w");
if(fp == NULL)
{
perror("Error while opening fd");
exit(1);
}
if(!fgets(buf,1024,fp))
printf("Nothing to read\n");
else
printf("Message Recieved : %s\n", buf);
fclose(fp);
sleep(1);
}
return 0;
}
I run the server first.
Secondly, on second terminal, when I run the client, I get the error :
Error while open call: No such device or address
What am I missing? I did man, and the parameters seem to be correct.
EDIT
Moving the open and close calls out of the while loop, did the job. But now if the client is started without starting the server, throws the following error :
Error while open call: No such device or address
The file /tmp/myFIFO exists on file-system from previous execution of the server, must be used by the client.
The main problem is opening and closing the file in a loop. This makes no sense. There is a very short time interval between opening and closing, and your client must hit it. It has little chance to do so. The "No such device or address" message happens exactly because the client misses the instant when the file is open. This is the main problem. Try moving open, fopen and fclose out of the server loop.
You also opening for reading but fopening for writing, but I suppose it's just a typo. This combination will not run. You need to change the mode of fopen to "r".
There are other, smaller issues.
You are not checking errors in the client. In your program the client will fail to open most of thee time, but sometimes open will succeed and write will fail.
It makes little sense to use stdio for the pipe in this program. read and write would do just fine.
Last but not least, sleep is an indication of a design issue. Indeed, in this program blocking I/O would make more sense. It's OK to use sleep if you just want to experiment with non-blocking I/O, but in real programs it should be avoided.

Knowing when Client has finished reading from the socket in C

I am writing a client-server program in c where I have to send multiple image files from the server to the client. Is there any way for the server to know when the client has finished reading from socket, creating the image file locally, and successfully written to it? P.S. I already tried sending a message to the socket and when I try to read the socket from the server, the program hangs. Any help will be much appreciated.
Here is a function from the Server code which sends the file to socket:
while(1)
{
unsigned char buf[256] = {0};
int n = fread(buf,1,256,fp);
if(n>0) { send(sockfd,buf,n,0); }
if(n<256) {
if(feof(fp))
printf("Sent to socket\n");
break;
}
}
fclose(fp);
}
char buf[5]
read(sockfd,buf,5);
if(strcmp(buf,"ready")==0) //send more files
And here is a function from the client to write to the file:
FILE* fp;
fp = fopen(file_path,"ab");
char buf[256];
int num;
int total=0;
while(( num = recv(sockfd,buf,256,0))>0)
{
total+=num;
fwrite(buf,1,num,fp);
}
fclose(fp);
write(sockfd,"ready",5);
}
When I do a read on the server after one file transfer, the program hangs.
You problem is here in the client:
while(( num = recv(sockfd,buf,256,0))>0)
recv() will only return 0 at end-of-file - ie when the server shuts down the sending side of the socket. However your server isn't doing that - it's waiting for a response from the client. This means you deadlock.
If you want to send multiple images in the one connection, you need to send your client some information to allow it to tell when one image ends. One way to do this is to first send your client the size of the file it should expect, then the file data. The client can then keep track of how many bytes it's recieved, and send the "ready" reponse after that.
You need a kind of protocol to allow :
the server to tell the client that all has been written
the client to tell the server that all has been read
If you only send one file, the simplest way is to use shutdown(sockfd, SHUT_WR) server side after all data has beed sent. That way the client will get a 0 as return from recv signaling end of data and will be able to send its acknowledgement. But you can no longer write on the socket server side.
If you want to be able to send more than one file, you will have to imagine a cleverer protocol. A common one would be to send blocs of data preceded by their size
short sz = htons(n); /* deals with possible endianness problems */
send(sockfd, &sz, sizeof(short), 0)
send(sockfd, buf, n);
And a sz == 0 (with no buf ...) would signal end of data
This would still be a simple protocol with no error recovery, but at least it can work when no incident happens.

Open file descriptor in C

I have client-server application. The server is in C.
Server have this structure:
int main (... ) {
FILE * fp;
fp = fopen("serverLog.log","w");
//init variables
//bind server
//listen server on port
while(1) {
//some code
//accept
//some code
int check = pthread_create(&thread, NULL, handle_client,&ctx);
}
fclose(fp);
return EXIT_SUCCSESS;
}
I run the server, and close the server using CTRL+C. What happens with filedescriptor fd? I suppose, that it stays open. If yes, what can I do with that?
Thx
No, it will be closed by the operating system. When your process exits (whether cleanly or forcibly) the kernel will clean up all dangling handles.

Problem with C server function

Hi I have a problem with my function, which responsible for contact between client and server:
#define MAX 1024
void connection(int sock)
{
char buffer[MAX];
int newsock;
int n;
int r;
if(write(sock,"Hello!\n", 6) < 0)
{
perror("Error: ");
}
do {
if(write(sock, "\n> ",3) < 0)
{
perror(" Error: ");
}
memset(buffer,'0',MAX); // fill buffer
n = read(sock,buffer,MAX -1 );
if (strncmp("get",buffer,3) == 0)
{
execl("/usr/bin/top","/usr/bin/top","-n 1");
}
else if (strncmp("quit",buffer,4) == 0)
{
write(sock, "Exit from program\n",17);
close(sock);
}
else
{
write(sock,"Wrong order!\n", 12);
}
}
while(n);
}
When client send "get" the program should sends him view from "top" order, unfortunately it does not work in my program.
Secondly, please judge this code. This is my first server program. I will be very grateful
And finally, how to change function to give clients possibility to action in program after send "get" order.
Regards and Happy New Year!
You are calling exec without calling fork. So you are replacing your entire server process with a copy of top. This is really unlikely to do what you want.
Very likely, you could accomplish your aims by opening a suitable pseudo-file from the /proc file system, reading the information, and sending it into your socket.
If you really want to use top, you have to use pipe, fork and exec(l) to run top, read it's output from a pipe, and then send that output to the client.
It occurs to me that you might be running in an environment in which the server automatically forks you (like some sort of CGI gateway), in which case your problem is that you need to fdopen to move the socket to be descriptor #1 before exec-ing. It would really help if you would tell us all about your environment by editing your question.
The output of "top" goes to the server's stdout, not out through the socket to the client. You'd have to adjust the stdout of the "top" process for this to work.

Resources