I am trying to simulate conversation between a caller and a receiver using pipes. I am forking a process and making the parent process the receiver and the child process the caller.
Here is the code:
#include <stdio.h>
#include <string.h>
#include <stdbool.h>
#include <sys/types.h>
#include <unistd.h>
#define BUF_LEN 25
#define READ_END 0
#define WRITE_END 1
int main()
{
int fd[2];
if (pipe(fd) == -1) {
fprintf(stderr, "Pipe failed");
return 1;
}
pid_t pid = fork();
if (pid < 0) {
fprintf(stderr, "Fork failed");
return 1;
}
// the parent process is the receiver
if (pid > 0) {
close(fd[WRITE_END]);
char buffer[BUF_LEN + 1] = "";
do {
read(fd[READ_END], buffer, sizeof buffer);
if (strcmp(buffer, "")) {
printf("Received %s\n", buffer);
}
strcpy(buffer, "");
} while (strcmp(buffer, "Bye!"));
close(fd[READ_END]);
} else {
close(fd[READ_END]);
// const char *msg = "Hello";
char buffer[BUF_LEN + 1] = "";
bool end_call = false;
do {
printf("Caller: ");
fgets(buffer, sizeof buffer, stdin);
if (strcmp(buffer, "Bye!")) {
end_call = true;
}
// printf("Sent %s\n", buffer);
write(fd[WRITE_END], buffer, strlen(buffer) + 1);
} while (!end_call);
close(fd[WRITE_END]);
}
return 0;
}
But when I run this, I get this unexpected output:
Caller: Hi
Received Hi
HI
Hello
Bye!
^C
The receiver stops working, it is not receiving the inputs I give. Also there are extra newlines appearing in the output. Why is this occuring?
Edit:
As pointed by out Dmitri, I have changed the strcmp test in the caller and the printf statement in the receiver.
#include <stdio.h>
#include <string.h>
#include <stdbool.h>
#include <sys/types.h>
#include <unistd.h>
#define BUF_LEN 25
#define READ_END 0
#define WRITE_END 1
int main()
{
int fd[2];
if (pipe(fd) == -1) {
fprintf(stderr, "Pipe failed"); return 1; }
pid_t pid = fork();
if (pid < 0) {
fprintf(stderr, "Fork failed");
return 1;
}
// the parent process is the receiver
if (pid > 0) {
close(fd[WRITE_END]);
char buffer[BUF_LEN + 1] = "";
do {
read(fd[READ_END], buffer, sizeof buffer);
if (strcmp(buffer, "")) {
printf("Received %s", buffer);
}
strcpy(buffer, "");
} while (strcmp(buffer, "Bye!"));
close(fd[READ_END]);
} else {
close(fd[READ_END]);
// const char *msg = "Hello";
char buffer[BUF_LEN + 1] = "";
bool end_call = false;
do {
printf("Caller: ");
fgets(buffer, sizeof buffer, stdin);
if (!strcmp(buffer, "Bye!")) {
end_call = true;
}
// printf("Sent %s\n", buffer);
write(fd[WRITE_END], buffer, strlen(buffer) + 1);
} while (!end_call);
close(fd[WRITE_END]);
}
return 0;
}
But it is still not exiting after receiving "Bye!".
Caller: hi
Received hi
Caller: Hello
Received Hello
Caller: Bye!
Received Bye!
Caller: Bye!
Received Bye!
Caller: ^C
The strcmp() returns 0 on success. But there are also several other problems with your code:
The string will never be equal to "Bye!", there is going to be a new line attached as well as the null character indicating the end of string (total of 6 chars).
The pipes use streams not "packets" you never know how many bytes you will receive from one call to read(). It might be incomplete string or if the data is sent very fast you might get 2 strings glued to each other. You need to implement your own "protocol" to parse the data out of the stream.
You are not checking if the pipe was closed on the other side (read would return 0)
You get extra new line in the output because it is attached to the string read by fgets()
Output may be messed up because you have no control on when processes flush to stdout (sort of a racing condition but it will not crash).
Because of the newline captured by fgets you need to test the exit condition for Bye!\n which includes that newline. strcmp(buffer, "Bye!\n")
This is in addition to fixing the negation issue pointed out by #blackpen.
Your program has several issues.
First, when fgets() reads a line, the newline at the end is included (if there was sufficient space in the buffer). The extra newlines you're seeing are because the string you send contains one, then you add another when you print it in the receiver. Also, you're looking for the string, "Bye!" to decide when to quit... but the string you actually get is "Bye!\n". You need to either strip the newline off the end of the string you read from stdin in the sender, or account for the newline already in the string when you print it and in your comparisons.
Second, in the sender, you have your logic inverted when checking when to quit: You're setting end_call = true; when buffer doesn't contain "Bye!", instead of when it does. This causes the sender to exit after sending the first string rather than looping (also, the comparison needs to be fixed to account for the newline as mentioned above). It's not the receiver that stops early, it's the sender... the receiver runs forever due to the next issue.
In the receiver, you're clearing the buffer at the end of your loop, right before you check for "Bye!" in the loop test. That prevents the comparison in the loop test from ever finding a match, so the loop is infinite. Clear the buffer at the start of the loop before fgets() instead. (And once again, fix the comparison to account for the newline at the end).
Change the test in client to the following. That will let the child/parent communicate.
if (!strcmp(buffer, "Bye!")) {
The client has newline in the buffer it reads ("Bye!" following by a newline character). That fails to match exit test. You could try to remove it.
fgets(buffer, sizeof buffer, stdin);
buffer[strlen(buffer)-1]='\0';
In the server side, don't initialze the buffer (just before while-check). Because that would reinitialize the very string that you are about to check.
strcpy(buffer, "");
If you want, you could put that just before you run the read function.
You may also get into problem buffering problem (in matching for end condition) and race conditions (while closing fds) as pointed out by other answers. But, I guess it is good enough example for just "learning". Good luck.
Related
So, I'm playing with pipes in c, and I have an exercise where I call a program through command line as this: "./self 1" which then calls itself with execlp but with an argument 2: "./self 2" which further on calls itself with argument 3: "./self 3". The point of these processes is this: process1 takes a line from keyboard and puts it in pipe1, then process2 gets the line from pipe1 and puts it in pipe2, then process3 gets it from pipe2 and counts the number of space characters. This code never works if I dont print a newline character on the screen before taking inputs with fprintf(stdout,"\n"); . Why is that?
Here is the code:
#include <fcntl.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/types.h>
#include <unistd.h>
int main(int argc, char* argv[]) {
if (strcmp(argv[1], "1") == 0) {
int fdpipe1[2];
if (pipe(fdpipe1)) {
printf("Error pipe1\n");
return 0;
}
pid_t p;
p = fork();
if (p == 0) {
close(fdpipe1[1]);
dup2(fdpipe1[0], 0);
execlp("./self", "./self", "2", NULL);
} else {
close(fdpipe1[0]);
fprintf(stdout, "\n");
dup2(fdpipe1[1], 1);
char input[100];
gets(input);
puts(input);
wait(NULL);
}
}
else if (strcmp(argv[1], "2") == 0) {
int fdpipe2[2];
if (pipe(fdpipe2)) {
printf("Error pipe2\n");
return 0;
}
pid_t p;
p = fork();
if (p == 0) {
close(fdpipe2[1]);
dup2(fdpipe2[0], 0);
execlp("./self", "./self", "3", NULL);
} else {
close(fdpipe2[0]);
fprintf(stdout, "\n");
dup2(fdpipe2[1], 1);
char input[100];
gets(input);
puts(input);
wait(NULL);
}
}
else if (strcmp(argv[1], "3") == 0) {
char input[100];
gets(input);
int i = 0, counter = 0;
while (input[i] != '\0') {
if (input[i++] == ' ') counter++;
}
printf("%d\n", counter);
}
return;
}
In this kind of construct, when you connect stdout from a process to stdin of another process via unnamed pipe, a newline character is added usually to ensure the stream is sent, i.e. the stdout buffer is flushed, as a parallel example, when you use scanf, only when you hit enter (a newline is added to stdin) is the stream read, a similar principle applies here.
I would suggest you use STDIN_FILENO and STDOUT_FILENO
built in macros instead of the hard coded file descriptors, if not for anything else, it makes the code more readable for someone who is unfamiliar with the matter.
Please avoid using gets, this is a dangerous function, it does not check the bounds of the destination buffer, it can cause all kinds of trouble, so much so it was deprecated and later removed from the standard, though it still can be used with some compilers, for legacy reasons I would imagine, check this fantastic answer on a post about this topic:
Why is the gets function so dangerous that it should not be used?
The advice is to use fgets instead.
I am learning pipes and I'm triying to process comunication with ordinary pipe. Following code writes once but it doesn't write or read again. What is the problem of that?
Edit:Yes, I removed close() parts but, it cannot completely read because write doesn't finish yet.
Ex:
Write: Hello
Read: He
Read: llo
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/types.h>
#include <string.h>
#define BUFFER_SIZE 25
#define READ_END 0
#define WRITE_END 1
int main(void)
{
char write_msg[BUFFER_SIZE] = "Game Started";
char read_msg[BUFFER_SIZE];
pid_t pid;
int fd[2]; // an array of 2 integers fd[0] and fd[1]
if (pipe(fd) == -1) { fprintf(stderr,"Pipe failed"); return 1;}
pid = fork();
if (pid < 0) { fprintf(stderr, "Fork failed"); return 1; }
while(1){
if (pid > 0) {
sprintf(write_msg,"Hello %d",rand());
write(fd[WRITE_END], write_msg, strlen(write_msg)+1);
}
else { /* child process */
int status = read(fd[READ_END], read_msg, BUFFER_SIZE);
if(status != -1)
printf("child read1: %s\n *********************************\n",read_msg);
}
}
return 0;
}
A way to think about pipes is to think of actual pipes that you have in your home. Water (or other fluids) flow from one end to the other. If you continuously fill the pipe at one end using buckets of water, at the other end there's no way to distinguish which bucket delivered the water currently flowing out.
It's basically the same with computer pipes: Bytes flow from one end to the other, without any specific kind of message boundaries. If you need a boundary, you need to add it yourself. And in a way you already do that, as you include the string null-terminator in the data you send.
Since you have a "message end byte" (the string null-terminator), a simple way to make sure you receive a full message is to read byte by byte in a loop, until you get to the null-terminator. Once you have the terminator you can then display the message, and go back to read the next message.
In pseudo-ish code it might look something like this:
char ch;
while (read(pipe_read_fd, &ch, 1) == 1)
{
if (ch == '\0')
{
// End of message, print the buffer
}
else
{
// Append character to buffer
}
}
I'm experimenting on how to communicate between a thread and the main function in C
There is a behavior that I don't understand in the following code :
#include <pthread.h>
#include <string.h>
#include <stdio.h>
#include <unistd.h>
void* output(void* pipe1);
int main(int argc, const char *argv[])
{
pthread_t tid0;
int pipe1[2];
char buffer[200];
// Creating the pipe
pipe(pipe1);
// Creating the thread and passing the pipe as argument
pthread_create(&tid0, NULL, output, &pipe1);
// Input from user
scanf("%s", buffer);
// Writing to the pipe
write(pipe1[1], buffer, strlen(buffer));
return 0;
}
void* output(void* pipe1) {
char buffer[200];
// Reading the pipe and print the buffer
read(((int*)pipe1)[0], buffer, strlen(buffer));
printf("thread say: %s\n", buffer);
pthread_exit(NULL);
}
Why the read function doesn't block on the pipe's file descriptor ?
Maybe I should close the end of the pipe but since they share the same memory space, the error "bad file descriptor" is returned when I will call read or write.
Maybe you can guide me to other methods if pipe is really a bad solution (with an example it will be amazing ! :) )
Many thanks !
EDIT: SOLUTION
Many thank for your answer here is the code that have the expected behavior
#include <pthread.h>
#include <string.h>
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
void* output(void* pipe1);
int main(int argc, const char *argv[])
{
pthread_t tid0;
int pipe1[2];
char buffer[200];
// Creating the pipe
pipe(pipe1);
// Creating the thread and passing the pipe as argument
pthread_create(&tid0, NULL, output, &pipe1);
// Input from user
scanf("%s", buffer);
// Writing to the pipe
if (write(pipe1[1], buffer, strlen(buffer)) < 0) {
perror("write");
exit(1);
}
// join so the main "wait" for the thread
pthread_join(tid0, NULL);
return 0;
}
void* output(void* pipe1) {
char buffer[200];
int nread;
// Reading the pipe and print the buffer
nread = read(((int*)pipe1)[0], buffer, sizeof buffer - 1);
if (nread < 0) {
fprintf(stderr, "ERROR\n");
perror("read");
exit(1);
}
buffer[nread] = '\0';
fprintf(stderr, "thread say: %s\n", buffer);
pthread_exit(NULL);
}
char buffer[200];
read(((int*)pipe1)[0], buffer, strlen(buffer));
You are calling strlen on an uninitialized buffer. This is allowed to crash your program. Instead, you got lucky, and all it did was tell read to read zero bytes, so read returned without doing anything.
What you actually want is
ssize_t nread = read(((int *)pipe1)[0], buffer, sizeof buffer - 1);
if (nread < 0) {
perror("read");
return 0;
}
buffer[nread] = '\0';
What read wants to be told is how much space you are giving it to read into, not the length of any string that may or may not already be in that space. That's sizeof buffer, minus one so we always have space to add a string terminator.
It's correct to use strlen when writing, because you only want to write the actual string, not any junk that might be beyond the end of the string; but then write doesn't write the string terminator to the pipe, so read doesn't read one, so you have to add it by hand. And, of course, always check for errors.
Also, keep in mind that the threads run simultaneously. Even after fixing this bug, the write may already have happened by the time the reader-thread calls read, and if it hasn't, it probably will happen very soon. If you want to observe the reader-thread actually blocking in read you need to delay before calling write.
I am doing a simple project in C involving fork() and socketpairs. The program forks a process then creates a socketpair for the processes to talk to one another. The parent process reads lines from stdin one at a time until EOF, and then sends the lines one by one the the child. The child converts the lines to uppercase and sends them back to the parent, who sends them to stdout. Here is my code
#include <stdio.h>
#include <stdlib.h>
#include <ctype.h>
#include <sys/types.h>
#include <sys/socket.h>
int main(int argc, char *argv[]) {
/* code */
int returnCode, status, socket[2], j;
char* buffer;
size_t bufferSize = 100;
size_t line;
buffer = (char *)malloc(bufferSize * sizeof(char));
socketpair(AF_UNIX, SOCK_STREAM, 0, socket);
returnCode = fork();
if(returnCode == -1){
printf("Fork failed\n");
}
if(returnCode > 0){
while(getline(&buffer, &bufferSize, stdin) != -1){
write(socket[1], buffer, bufferSize);
read(socket[1], buffer, bufferSize);
printf("%s", buffer);
}
}
else{
read (socket[0], buffer, bufferSize);
for(j = 0; j[buffer];j++){
buffer[j] = toupper(buffer[j]);
}
write(socket[0], buffer, bufferSize);
}
return 0;
}
My program prints the first line from stdin capitalized, but then it hangs. I have to ctrl-c to exit. How do i get the sockets to loop thru until EOF printing the modified lines?
My program prints the first line from stdin capitalized, but then it
hangs. I have to ctrl-c to exit. How do i get the sockets to loop thru
until EOF printing the modified lines?
Your child process's routine (i.e. the one that does the toupper() calls) only calls read() one time, and then calls write() one time, and then exits. That is why only one line is processed. To get it to do multiple lines, you'd need to put the code into a loop:
[...]
else{
while(read(socket[0], buffer, bufferSize) > 0)
{
for(j = 0; j[buffer];j++){
buffer[j] = toupper(buffer[j]);
}
write(socket[0], buffer, bufferSize);
}
}
return 0;
}
I used a simple fork() to simulate client / server then a very simple pipe to send / receive a char buffer of max 30 length, but it ends up printing non printable characters (small "?" and a box with 4 ones and zeroes) AFTER the desired word.
#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>
#include <sys/wait.h>
#include <string.h>
int main () {
int pipefd[2];
int cpid;
char buf[31];
if (pipe(pipefd) == -1) {
perror("pipe");
exit(EXIT_FAILURE)
}
cpid = fork();
if (cpid == -1) P
perror("cpid");
exit(EXIT_FAILURE);
}
if (cpid == 0) { // child reads from pipe
close (pipefd[1]); // close unused write end
read (pipefd[0], &buf, 30); // if I use 30 instead of strlen(buf) it prints Server transmit: Server receives. It does not wait for write. Makes no sense
printf ("Server receives: %s", buf);
close (pipefd[0])l
exit (EXIT_SUCCESS);
}
else { // parent writes to pipe
close (pipefd[0]); // closing unused read end;
char buf2[30];
printf("Server transmits: ");
scanf ("%s", buf2);
write (pipefd[1], buf2, strlen(buf2));
close(pipefd[1]);
wait(NULL);
exit(EXIT_SUCCESS);
}
return 0;
}
Also if I write more than one word it forgets about the second. In c++ I used getline (cin, string) but that's not an option here.
Also used read (pipefd[0], &buf, sizeof(buf));, now it prints in the correct order (no idea why strlen did not work) but I still get non printable characters at the end.
When you write (pipefd[1], buf2, strlen(buf2)); You neglect to put the '\0' in the stream. Change that to:
write (pipefd[1], buf2, strlen(buf2)+1);
And your string will now contain the null terminator, preventing the garbage at the end.
Using read (pipefd[0], &buf, strlen(buf)) did not work because buf is uninitialized. strlen is a simple function which looks for the terminating null at the end on the string, stopping when it's found. Unlike the length functions of C++ vectors, C functions have no way of accessing memory metadata. (sizeofis an operator)