Named Pipe client and server, message truncated on server? - c

I got a server that is always running, it creates a log file that receives via named pipe one argument and stores it on the log.txt file.
Clients sent a message via argument to the named pipe.
cliente side i guess its ok, if i cat /tmp/talk its there the full message, but on the server its only storing the first char. why is that?
And a simplier question, is there a better way to implement the server cycle to check the pipe?
client
int main(int argc, char const *argv[]){
char *myfifo = "/tmp/talk"; int fd,n;
fd = open(myfifo,O_WRONLY);
write(fd,argv[1],strlen(argv[1])+1); printf("Sent to server: %s \n",argv[1]);
close(fd);
}
server
int main(int argc, char const *argv[]){
char *myfifo = "/tmp/talk";
char buffer[2024];
//char *log = "log.txt";
int fd,n;
mkfifo(myfifo, 0666);
int log = open("log.txt",O_CREAT|O_APPEND|O_WRONLY, 0666);
fd = open(myfifo,O_RDONLY);
while(1) {
if(n = read(fd,buffer,1024)>0) {
write(log,buffer,n);
write(1,buffer,n);
//printf("Client connected sent: %s",buffer);
}
}
}

n = read(fd,buffer,1024)>0
evaluates like
n = (read(fd,buffer,1024)>0)
so 1 is stored in n (instead of the number of bytes read) if read returns a positive value. Use instead:
(n = read(fd,buffer,1024))>0
as conditional, then it should work as expected.

Related

How to create an unique Named Pipe for each Client

I have a typical Client-Server architecture where the Server is constantly reading from the pipe to see if any Client has sent something. Usually what is done is that there's also a named pipe so that the Server is able to also send whatever it needs to the Client.
The problem I have is that I need to send a specific message to specific Clients, according to my needs. For example, at the moment, with Client A and Client B, if Client A sends a message through the pipe with "process", I need the Server to send back a reply with "OK" to Client A, but what happens is that a random one gets it (not really sure what happens here, but I assume that once one of the Client reads what's in the pipe, the other one won't be able to read it anymore).
How am I able to create a Named Pipe for each Client, so that the Server can send a specific message to a specific Client? Below is the code that I have:
Client.c
#define FIFO_FILE_1 "../tmp/fifo"
#define FIFO_FILE_2 "../tmp/fifo2"
int main(int argc, char *argv[]){
/*if(mkfifo("fif", 0666) < 0){
perror("mkfifo");
}*/
int client_to_server;
int server_to_client;
if((client_to_server = open(FIFO_FILE_1, O_WRONLY))<0){
perror("open fifo");
exit(1);
}
if((server_to_client = open(FIFO_FILE_2, O_RDONLY))<0){
perror("open fifo");
exit(1);
}
if(argc>2){
write(client_to_server, "process\n", 9);
}
int bytes_read = 0;
char buf[1024];
while(1){
while((bytes_read = read(server_to_client, buf, 1024)) > 0){
write(1,"Received",7);
}
}
close(client_to_server);
close(server_to_client);
unlink(FIFO_FILE_1);
unlink(FIFO_FILE_2);
return 0;
}
Server.c
#define FIFO_FILE_1 "../tmp/fifo"
#define FIFO_FILE_2 "../tmp/fifo2"
int main(int argc, char *argv[]){
// Create named pipes
if(mkfifo(FIFO_FILE_1, 0666) < 0){
perror("mkfifo");
}
if(mkfifo(FIFO_FILE_2, 0666) < 0){
perror("mkfifo");
}
int client_to_server;
int server_to_client;
if((client_to_server = open(FIFO_FILE_1, O_RDONLY))<0){
perror("open fifo");
exit(1);
}
if((server_to_client = open(FIFO_FILE_2, O_WRONLY))<0){
perror("open fifo");
exit(1);
}
char buf[1024];
char bufaux[1024];
while(1){
int n = readCommand(client_to_server, buf); //Just reads until \n, shouldn't matter for the problem
if (n<=0)
continue;
strcpy(bufaux,buf);
char * token = first_arg(bufaux); //Returns the token until it hits a space, in this case it will return "process".
if(strcmp(token,"process")==0){
write(server_to_client,"OK", 3);
}
close(client_to_server);
close(server_to_client);
unlink(FIFO_FILE_1);
unlink(FIFO_FILE_2);
return 0;
}
There's aditional functions that aren't shown, that are simply used to parse whatever comes through the Pipe from the Client. Just assume that strcmp(token,"process")==0 will always be true.
How and when would I create a Pipe for each Client? My idea was to send some sort of identifier whenever the Client sends the first message, that would then be used to write a message to that same Client.
Obviously the problem is much more complex, there's data in memory that is what's going to be sent into each specific Client, but as an example, consider I just need to send an "OK". Also, I can't use sockets.
A simple way to solve your problem is to let the clients create the pipes for the return messages.
You can use the PID of the process as part of the name to make it unique per process.
Then in the clients communication with the server, it always include the PID so the server knows which pipe to write the response to.

Pipe "bad address" on pipe open

So, im trying to start a webserver that uses pipes to comunicate between process.
I was thinking to make a struct named ctx to send other info also.
My code looks like this:
webserver.h
typedef struct
{
int pipefd[2];
} ctx_t;
webserver.c
int main(int argc, char *argv[]){
ctx_t *ctx = {0};
if(pipe(ctx->pipefd) == -1){
perror("ctx pipe error");
exit(EXIT_FAILURE);
}
...
...
}
Output: "ctx pipe error: Bad address"
if instead i declare my program like this, i have no error and the programs continue
webserver.h
int pipefd[2];
webserver.c
int main(int argc, char *argv[]){
if(pipe(pipefd) == -1){
perror("ctx pipe error");
exit(EXIT_FAILURE);
}
...
...
}
Any ideas why i cant open the pipe inside a struct? I still havn't made any forks in the main program.
Thanks.
You're passing a null pointer to a function (system call) pipe() that doesn't accept null pointers. Don't do that!
ctx_t *ctx = {0};
That sets ctx to a null pointer, albeit somewhat verbosely (the braces are not necessary, though they're not harmful). You need to allocate the ctx_t structure somewhere before trying to use it.
Use:
cts_t ctx = { { 0, 0 } };
and:
if (pipe(ctx.pipefd) != 0)
…report error etc…
Using == -1 is also OK.

Issues with socket and server in C (Name or service not known error)

I am working on a client/server program that is supposed to take user input (two integers) and allow the user to calculate those to receive an answer.
When I run my program there seems to be an issue with either connecting to the client or opening the directory and I'm not sure what it could be. I'm entirely new to setting up servers and utilizing directories to read and write .txt files to.
Here are the parts of code that I think might be wrong and causing the issues I am facing, which is the program asks for a port number to connect to (2000 is what is recommended to use) so I enter that and then nothing happens.
// PURPOSE: To run the server by 'accept()'-ing client requests from
// 'listenFd' and doing them.
void doServer (int listenFd)
{
/* Application validity check: */
/* Server clients: */
pthread_t threadId;
pthread_attr_t threadAttr;
int threadCount = 0;
int *iPtr;
/* YOUR CODE HERE: */
while(1)
{
/* Accept connection to client: */
int connfd = accept(listenFd, NULL, NULL); //if I change this to getServerFileDescriptor (another function within the code, it will continuously loop through threadNumbers and do nothing as well).
/* Malloc memory for two integers: */
iPtr = (int *)calloc(2, sizeof(int));
/*Put file descriptor in the first space: */
iPtr[0] = listenFd; // or just listenFd not sure
/* put threadCount into the second space and increment: */
iPtr[1] = threadCount++;
/* Creates detached thread for handleClient and passes the address of iPtr */
pthread_attr_init(&threadAttr);
pthread_attr_setdetachstate(&threadAttr, PTHREAD_CREATE_DETACHED);
pthread_create(&threadId, &threadAttr, handleClient, (void*)iPtr);
pthread_join(threadId, NULL);
pthread_attr_destroy(&threadAttr);
}
}
void* handleClient(void* vPtr)
{
/* Read command: */
char buffer[BUFFER_LEN];
char command;
int fileNum;
char text[BUFFER_LEN];
int shouldContinue = 1;
int threadNum;
int fd;
/* Cast void* vPtr back to an int */
int *iPtr = (int *)vPtr;
/* Assign file descriptor to a local value named 'fd'*/
fd = iPtr[0];
/* Assign thread number to local value named 'threadNum'*/
threadNum = iPtr[1];
free(iPtr);
while (shouldContinue)
{
memset(buffer,'\0',BUFFER_LEN);
memset(text ,'\0',BUFFER_LEN);
read(fd,buffer,BUFFER_LEN);
printf("Thread %d received: %s\n",threadNum,buffer);
sscanf(buffer,"%c %d \"%[^\"]\"",&command,&fileNum,text);
/* YOUR CODE HERE: */
if(command == DIR_CMD_CHAR)
{
/* 1. Open the current directory (named "."). If an error occurs then just send STD_ERROR_MSG back to the client: */
DIR* dirPtr = opendir(".");
struct dirent* entryPtr;
/* If error occurs send STD_ERROR_MSG to client: */
if ((dirPtr = opendir (".")) == NULL) {
{
write(fd, STD_ERROR_MSG, sizeof(STD_ERROR_MSG));
//return(EXIT_FAILURE);
}
/* Read as many entries that will fit into BUFFER_LEN
put as many entries into the buffer and send the buffer to client
d_name=entryPtr into the bufffer using strcat_s,
make sure buffer starts empty
buffer[0]='\n';
add new line char using stringcat "\n"
make sure do not go over buffer lengh */
if (dirPtr)
{
while ((entryPtr = readdir(dirPtr)) != NULL)
{
buffer[0]='\0';
int i;
int sizebuf = sizeof(buffer);
for (i = 0; i < sizebuf; i++)
{
strcat(buffer,entryPtr->d_name);
strcat(buffer,"\n");
}
}
}
/* 3. Close directory */
closedir(dirPtr);
}
Here's how the correct output should look.
$ ./mathClient Machine name [localhost.localdomain]? (I just pressed enter)
Port number? 2000
What would you like to do:
(1) List files
(2) Read a math file
(3) Write a math file
(4) Calculate a math file
(5) Delete a math file
(0) Quit
Your choice? 1
Sending "l"
0.bc
Here are the instructions for the code that I am having trouble with.
Implementing doServer(int listenFd) (10 Points):
doServer() should have a loop in which it waits for a client to connect to listenFd. When a client does, it should:
malloc() enough memory for 2 integers
put the file descriptor from accept() in one of those spaces
put the value of threadCount in the other space, and increment threadCount
Make a detached thread to handle this new client. I called my function handleClient(), but you may call yours whatever. Pass the address of your malloc()-ed array.
The loop should then go back for another accept().
void* handleClient(void* vPtr) (10 Points):
(Or whatever you call your function that runs a thread for the client.)
The thread id and the file descriptor are passed, but they come in as a void* pointer.
Use another pointer to cast back to int*
Save the file descriptor and thread number in local vars
free() the memory
Print the thread number and do a loop like this:
// II.B. Read command:
char buffer[BUFFER_LEN];
char command;
int fileNum;
char text[BUFFER_LEN];
int shouldContinue = 1;
while (shouldContinue)
{
text[0] = '\0';
read(fd,buffer,BUFFER_LEN);
printf("Thread %d received: %s\n",threadNum,buffer);
sscanf(buffer,"%c %d \"%[^\"]\"",&command,&fileNum,text);
// YOUR CODE HERE
}
It read()s a line of text from the client into buffer[], and parses the line into a command character, fileNum integer, and quote-delineated text[] string. (The fileNum and text[] may or may not be given, depending upon the value of command.)
Then do the following operations based upon the value of command. Except for QUIT_CMD_CHAR I strongly recommend using a different function for each!
When the function ends just have it do:
printf("Thread %d quitting.\n",threadNum);
return(NULL);
command == DIR_CMD_CHAR (15 Points):
Open the current directory (named "."). If an error occurs then just send STD_ERROR_MSG back to the client.
Copy as many entries that will fit into a buffer of length BUFFER_LEN. Be sure to put a separating '\n' after each entry.
Close the directory.
Any help would be appreciated, if you need the full code I could send that to you if that would help.
EDIT: Here are two additional functions, one called getPortNum() and another called getServerFileDescriptor() which address receiving a port number and setting up sockets for the connection. Additionally I included the main() which utilizes these.
// PURPOSE: To decide a port number, either from the command line arguments
// 'argc' and 'argv[]', or by asking the user. Returns port number.
int getPortNum (int argc,
char* argv[]
)
{
// I. Application validity check:
// II. Get listening socket:
int portNum;
if (argc >= 2)
portNum = strtol(argv[1],NULL,0);
else
{
char buffer[BUFFER_LEN];
printf("Port number to monopolize? ");
fgets(buffer,BUFFER_LEN,stdin);
portNum = strtol(buffer,NULL,0);
}
// III. Finished:
return(portNum);
}
// PURPOSE: To attempt to create and return a file-descriptor for listening
// to the OS telling this server when a client process has connect()-ed
// to 'port'. Returns that file-descriptor, or 'ERROR_FD' on failure.
int getServerFileDescriptor
(int port
)
{
// I. Application validity check:
// II. Attempt to get socket file descriptor and bind it to 'port':
// II.A. Create a socket
int socketDescriptor = socket(AF_INET, // AF_INET domain
SOCK_STREAM, // Reliable TCP
0);
if (socketDescriptor < 0)
{
perror(THIS_PROGRAM_NAME);
return(ERROR_FD);
}
// II.B. Attempt to bind 'socketDescriptor' to 'port':
// II.B.1. We'll fill in this datastruct
struct sockaddr_in socketInfo;
// II.B.2. Fill socketInfo with 0's
memset(&socketInfo,'\0',sizeof(socketInfo));
// II.B.3. Use TCP/IP:
socketInfo.sin_family = AF_INET;
// II.B.4. Tell port in network endian with htons()
socketInfo.sin_port = htons(port);
// II.B.5. Allow machine to connect to this service
socketInfo.sin_addr.s_addr = INADDR_ANY;
// II.B.6. Try to bind socket with port and other specifications
int status = bind(socketDescriptor, // from socket()
(struct sockaddr*)&socketInfo,
sizeof(socketInfo)
);
if (status < 0)
{
perror(THIS_PROGRAM_NAME);
return(ERROR_FD);
}
// II.B.6. Set OS queue length:
listen(socketDescriptor,5);
// III. Finished:
return(socketDescriptor);
}
int main (int argc,
char* argv[]
)
{
// I. Application validity check:
// II. Do server:
int port = getPortNum(argc,argv);
int listenFd = getServerFileDescriptor(port);
int status = EXIT_FAILURE;
if (listenFd >= 0)
{
doServer(listenFd);
close(listenFd);
status = EXIT_SUCCESS;
}
// III. Finished:
return(status);

FIFO Pipelining Server only receiving certain amount

So I'm trying to implement a basic FIFO pipeline in C using mkfifo().
Here are my code classes so far:
main.c:
int main(int argc, char *argv[]) {
char *path = "/tmp/fifo";
pid_t pid;
setlinebuf(stdout);
unlink(path);
mkfifo(path, 0600);
pid = fork();
if (pid == 0) {
client(path);
} else {
server(path);
}
return(0);
}
client.c:
void client(char *path) {
char *input;
input = (char *)malloc(200 * sizeof(char));
read(STDIN_FILENO, input, 200);
struct Message message;
message = protocol(input); //protocol simply takes an input string and formats it
char number = message.server;
char* string;
string = message.string;
int fd;
fd = open(path, O_WRONLY);
write(fd, string, sizeof(string));
printf("Client send: %s\n", string);
close(fd);
return;
}
server.c:
void server(char *path) {
int fd;
char *input;
input = (char *)malloc(200 * sizeof(char));
fd = open(path, O_RDONLY);
read(fd, input, sizeof(input));
printf("Server receive: %s\n", input);
close(fd);
return;
}
Now the pipeline is working, but for some reason the server only receives part of the message. For example if we get the following string from the protocol: "HELLO WORLD" We get the following output:
Server receive: HELLO WO
Client send: HELLO WORLD
The server should receive the whole message, but it isn't. What am I doing wrong? Thank you for any help!
I notice you have skimped the usually essential checking of return values from open and read and write. If you had, you might have noticed the error in this line
write(fd, string, sizeof(string));
Because string is a pointer, you are sending 8 bytes (the size of the pointer). You should use strlen(string) or that +1, depending on whether the terminator needs to be sent.
write(fd, string, strlen(string));
You repeat the mistake in your recent unwise edit:
read(fd, input, sizeof(input));
You would be better sticking with the original #define and using that for both buffer allocation and read request size.

Program displays strange characters on the screen

I'm developing a client-server program, and this is my server_2 file, who will comunicate with the main server.
The program displays on the screen these lines when is running. I think that those lines after the mkfifo are causing this.
i�e|楬���h�.N=��.8��
i�H��h� ��h� �i���Ǭ��ǬjǬ�dǬ�#��i�P#h�Ǭ���h����h�jǬ��ǬP
Structures
typedef struct request req;
struct request
{
char str[256];
int client_pid;
int login; // In case of client, to identify if is logged
int whois; // To identify who is the client and the server
};
typedef struct answer ans;
struct answer
{
char str[256];
int server_pid;
int type;
int login;
int num_users;
};
Main:
#include "header.h"
int main(int argc, char *argv[])
{
int fifo_1, fifo_2;
struct request req;
struct answer ans;
if(argc == 2) // Check if the command was well prompted
{
if(strcasecmp(argv[1], "show") == 0 || strcasecmp(argv[1], "close") == 0)
{
if(fifo_2 = open("FIFO_SERV", O_WRONLY) == -1)
{
perror("[SERVER_2] Error: on the FIFO_SERVER opening!\n");
sleep(2);
exit(EXIT_FAILURE);
}
if(mkfifo("FIFO_SERV_2", 0777) == -1)
{
perror("[SERVER_2] Error: on the FIFO_SERVER_2 creation!\n");
sleep(2);
exit(EXIT_FAILURE);
}
strcpy(req.str, argv[1]); // Copy the argumento to the structure
write(fifo_2, &req, sizeof(req)); // Write a request to the server
strcpy(req.str,""); // Clean the string
fifo_1 = open("FIFO_SERV_2", O_RDONLY);
read(fifo_1, &ans, sizeof(ans)); //Read an answ
}
//close(fifo_1);
unlink("FIFO_SERVER_2");
sleep(2);
exit(EXIT_SUCCESS);
}
The precedence rules of operators = and == make the line
if(fifo_2 = open("FIFO_SERV", O_WRONLY) == -1)
equivalent to
if(fifo_2 = (open("FIFO_SERV", O_WRONLY) == -1))
which essentially assigns 0 to fifo_2 if open succeeds and 1 if open fails. The values 0 and 1 also happens to be the respective values of the standard input and output file descriptor in POSIX standard library implementations (see File descriptor on wikipedia), so later when you execute
write(fifo_2, &req, sizeof(req)); // Write a request to the server
you are either trying to write to standard input (undefined behavior), or to standard output depending on whether the file could be opened rather than to the server. To fix this, you can replace the open expression with:
if((fifo_2 = open("FIFO_SERV", O_WRONLY)) == -1)
Then, you may have to figure out why you can't open the file (since you are presumably writing to standard output, which means open failed).

Resources