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)
Related
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'm supposed to create two programs (main and aux), where main forks a child to execute aux. The parent takes input from the user, until blank line '\n', and the child executes aux, which is supposed to print the input back out. I'm able to get it to work in main with the commented code instead of execlp(), but cannot get execlp(aux) to work correctly. Any help is appreciated.
"main.c"
#include <stdlib.h>
#include <stdio.h>
#include <unistd.h>
int main() {
int fd[2], i;
char line[100], buffer[100];
pipe(fd);
pid_t pid = fork();
if (pid < 0) {
printf("Fork Failed\n");
exit(-1);
}
else if (pid > 0) {
close(fd[0]);
while(fgets(line, sizeof(line), stdin) && line[0] != '\n') {
write(fd[1], line, sizeof(line));
}
close(fd[1]);
}
else {
close(fd[1]);
dup2(fd[0], STDIN_FILENO);
//while(read(fd[0], buffer, sizeof(buffer)))
// printf("> %s", buffer);
execlp("./aux", "aux", (char *)0);
}
return 0;
}
"aux.c"
#include <stdio.h>
#include <stdlib.h>
int main() {
char data[100];
while(fgets(data, sizeof(data), stdin))
printf(">%s\n", data);
return 0;
}
sample input/output
this
>this
is a test
>
> test
only prints larger text with random \n
>
>ts larger text with random \n
Your call to write(2) is wrong (you always write 100 bytes even for shorter line-s):
write(fd[1], line, sizeof(line)); // WRONG
should probably be using strlen(3)
size_t ll = strlen(line);
ssize_t wc = write(fd[1], line, ll);
if (wc != ll)
fprintf(stderr, "write was wrong (only %d, wanted %d) - %s\n",
(int) wc, (int) ll, strerror(errno));
Since you want to write only the filled bytes of the line buffer, not always 100 bytes each time (some of them not being initialized).
In your case sizeof(data) is 100 since you declared char data[100];
Please read carefully the documentation of every used function (and also ALP or some other book on Unix/POSIX/Linux programming). The documentation of strerror(3) and of errno(3) tells that you need to add:
#include <string.h>
#include <errno.h>
Actually, if you want to use read(2) and write(2) directly (without stdio(3)) you should prefer using larger buffers (e.g. 4Kbytes each at least for efficiency) and you need to manage partial read-s and write-s and do your buffering by yourself.
BTW, compile with all warnings and debug info: gcc -Wall -Wextra -g and learn to use the gdb debugger and strace(1) (and valgrind). In general, be scared of undefined behavior (however, at a first glance, your program don't seem to have UB).
Notice that execlp(3) could fail. Consider adding some call to perror(3) after it.
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.
I'm confused about what I'm doing wrong when I'm attempting to output to a file after I've execed a second program.
Say I have input file that has the following names:
Marty B. Beach 7 8
zachary b. Whitaker 12 23
Ivan sanchez 02 15
Jim Toolonganame 9 03
After my programs finish, it will convert the student's names to their usernames and output it to a file such as this:
mbb0708
zbw1223
is0215
jt0903
Currently as my program stands, it outputs nothing to the file and the terminal seems to be in an infinite loop despite self testing my converter program before and making sure it outputs names correctly to stdout.
I'm not sure what I'm doing wrong here? First time programming with pipes. I know have to make use of the read and write commands extract the data, but with the dup2 command is that necessary for the read command alone?
manager.c
#include <sys/types.h>
#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>
#include <sys/wait.h>
int main(int argc, char** argv)
{
pid_t pid;
int nbytes;
/*Buffer to hold data from pipe*/
char buffer[BUFSIZ + 1];
/*Pipe Information*/
int commpipe[2];
if(pipe(commpipe))
{
fprintf(stderr, "Pipe failed.\n");
return EXIT_FAILURE;
}
if((pid = fork()) == -1)
{
fprintf(stderr,"Fork error. Exiting.\n");
exit(1);
}
else if(pid == 0)
{
/*This is the child process. Close our copy of the write end of the file descriptor.*/
close(commpipe[1]);
/* Connect the read end of the pipe to standard input*/
dup2(commpipe[0], STDIN_FILENO);
/*Program will convert the Student's name to their respective names*/
execl("converter","converter",NULL);
/*Exit if failure appears*/
exit(EXIT_FAILURE);
}
else
{
FILE *file;
file = fopen("usernames.txt","a+"); //append a file(add text to a file or create a file it does not exist)
/*Close or copy of the read end of the file descriptor */
//close(commpipe[1]);
nbytes = write(commpipe[1], buffer, BUFSIZ);
//Read from pipe here first?
//Output to usernames.txt the usernames of the user from the pipe.
fprintf(file, "%s", buffer);
/*Wait for the child process to finish*/
waitpid(pid, NULL, 0);
}
return 0;
}
One problem is that after manager has sent all the data to converter, the manager is not closing commpipe[1]. Because of that, converter will never get EOF on stdin so will not exit.
Most likely manager isn't getting any data back from converter due to buffering. Some implementations of stdio use full-buffer buffering (as opposed to line-buffering) when not writing to a terminal. Once you fix the previous error and get the process to close, that will flush stdout. You can also consider adding fflush(stdout) after your puts line.
Have a look at the OpenGroup site, there's an example that looks similar to yours. I suggest you get the sample working first with some hard coded. Once that is working, add the code to read and write the results.
I made some minor changes to get the example working:
#include <stdlib.h>
#include <unistd.h>
#include <stdio.h>
#include <assert.h>
int main(int argc, char** argv){
int fildes[2];
const int BSIZE = 100;
char buf[BSIZE];
ssize_t nbytes;
int status;
status = pipe(fildes);
if (status == -1 ) {
/* an error occurred */
printf("Error!\n");
exit(-1);
}
printf("Forking!\n");
switch (fork()) {
case -1: /* Handle error */
printf("Broken Handle :(\n");
break;
case 0: /* Child - reads from pipe */
printf("Child!\n");
close(fildes[1]); /* Write end is unused */
nbytes = read(fildes[0], buf, BSIZE); /* Get data from pipe */
/* At this point, a further read would see end of file ... */
assert(nbytes < BSIZE); /* Prevent buffer overflow */
buf[nbytes] = '\0'; /* buf won't be NUL terminated */
printf("Child received %s", buf);
close(fildes[0]); /* Finished with pipe */
fflush(stdout);
exit(EXIT_SUCCESS);
default: /* Parent - writes to pipe */
printf("Parent!\n");
close(fildes[0]); /* Read end is unused */
write(fildes[1], "Hello world\n", 12); /* Write data on pipe */
close(fildes[1]); /* Child will see EOF */
/* Note that the Parent should wait for a response from the
child here, because the child process will be terminated once
the parent exits */
exit(EXIT_SUCCESS);
}
return 0;
}
As I understand, your converter program reads lines from stdin and writes them to stdout. As a pipe is a uni-directional entity, you will need TWO of them to communicate with the manager - one to send data to the converter and one to receive output from it.
Maybe you should consider enhancing the converter to take (as optional arguments) the name of an input and output file.