Named pipes for client-server simulation in 2 terminals - c

I know the title doesn't explain the problem precisely and I apologize.
I've been writing a program in C of named pipes and the goal of it is to open 2 windows of the terminal, each running either the "Server" or the "Client" file.
After the terminals connected to each other via one named pipe, the Client can then send a string to the Server, the server would create a thread that prints the string it had received, reverses it, and then send it back to the Client via another named pipe. Finally, the Client should print the message it had received back from the Server. The client should be able to keep sending strings until the file is exited or the string "exit" is sent.
What my issue is and what I think causes it:
Everything works fine when the user enters a single word in the string, but when it sends a sentence with spaces in it, the Client's fscanf, that is meant to read from "toClient" named pipe, slices the sentence and it can only receive one word at a time.
So then the next time the client sends a message, it will read the second word of the previous message because it was sliced out and stayed in the named pipe.
I tried to use a while loop to keep reading from "toClient" until all the individual words have been taken out and it works like I expected it to, but after the first message the connection hangs and the client can't send new messages. I think it hangs because the while loop doesn't reach EOF for some reason, maybe because both the Client and Server still have the named pipe open.
Server:
/*function that reverses a string*/
char* revstr(char *str1)
{
int i, len, temp;
len = strlen(str1);
for (i = 0; i < len/2; i++)
{
temp = str1[i];
str1[i] = str1[len - i - 1];
str1[len - i - 1] = temp;
}
return str1;
}
/*function that is run by a thread to handle a client's message*/
void *threadFunc(void *arg){
char *string = (char*)arg;
printf("Received from client: %s\n", string);//print message received to terminal
/*make a connection to "toClient" named pipe that in order to send back the reversed string*/
FILE *fdw;
if (!(fdw = fopen("toClient", "w"))) {
perror("cannot open fifo file for w") ;
exit(EXIT_FAILURE) ;
}
/*irrelevant*/
if(strcmp(string,"exit")==0)
{
fprintf(fdw, " Done\n") ;
fflush(fdw) ;
printf("Shutting down...\n");
exit(0) ;
}
char *string2 = revstr(string);//string2 is the reversed string
fprintf(fdw, " %s\n", string2) ;//send string2 into the named pipe labeled "toClient"
fflush(fdw) ;
printf("Sent message back to client...\n");
}
int main()
{
char s[STR_LEN];
FILE *fdr;
if (mkfifo("toServer", 0777) == -1 && errno != EEXIST) {
perror("cannot create fifo1 file") ;
exit(EXIT_FAILURE) ;
}
if (mkfifo("toClient", 0777) == -1 && errno != EEXIST) {
perror("cannot create fifo2 file") ;
exit(EXIT_FAILURE) ;
}
printf("Waiting for client...\n");
if (!(fdr = fopen("toServer", "r"))) {
perror("cannot open fifo file for r") ;
exit(EXIT_FAILURE) ;
}
printf("Client found, waiting for message...\n");
/*this block waits for a message from the client, then creates a thread to handle it*/
while ( fscanf(fdr, " %s", s) != EOF){
int retcode;
pthread_t t1;
retcode = pthread_create(&t1,NULL,&threadFunc,(void *)(s));
if(retcode!=0)
printf("Create thread failed with error %d\n", retcode);
pthread_join(t1,NULL);
}
printf("Client disconnected\n") ;
return EXIT_SUCCESS ;
}
Client:
{
char s[STR_LEN];
FILE *fdw;
FILE *fdr;
if (mkfifo("toServer", 0777) == -1 && errno != EEXIST) {
perror("cannot create fifo file") ;
exit(EXIT_FAILURE) ;
}
if (mknod("toClient", 0777,0) == -1 && errno != EEXIST) {
perror("cannot create fifo file") ;
exit(EXIT_FAILURE) ;
}
if (!(fdw = fopen("toServer", "w"))) {
perror("cannot open fifo file for w") ;
exit(EXIT_FAILURE) ;
}
puts("Connected to server, enter a message:\n");
/*the user now enters a message into the terminal*/
while ( fgets(s, STR_LEN, stdin) != NULL) {
printf("Sent: %s",s);//print the message to terminal
fprintf(fdw, " %s\n", s) ;//send the message into the named pipe labeled "toServer"
fflush(fdw) ;
/*connect to the server to receive it's response using the named pipe labeled "toClient"*/
if (!(fdr = fopen("toClient", "r"))) {
perror("cannot open fifo file for r") ;
exit(EXIT_FAILURE) ;
}
/*this is where my problem is - this block is essentially meant to read from the named pipe "toClient"*/
if/*while*/ ( fscanf(fdr, " %s", s) != EOF )
{
printf("Received from server: %s\n", s);//print the response received to the terminal
/*Irrelevant*/
if(strcmp(s,"Done")==0)
exit(0);
}
}
return EXIT_SUCCESS ;
}
Terminals when using if on fscanf:
Terminals when using while on fscanf:
I know this thread is really long and the problem is probably due to bad code design but I have no idea what to do after almost 8 hours of trying and being relatively inexperienced with the subject .(It's part of an OS course I'm taking in uni)

Well, this is how scanf with %s specifier works. If you want to read whole line using scanf then use %[^\n]s, which means - read until newline character. Anyways it would be better to use fgets.

Related

C socket: FD_ISSET return true with no data waiting

As a school project, I'm implementing a IRC server and I've been stuck on this one problem for the day.
My server use select to choose which client is sending data, it then read one command from this user (commands are \r\n separated), parse it and execute it before passing to the next user.
A user can send multiple command at once like so :
"command1\r\ncommand2\r\n"
If this happen, i want the first command to be executed and the second to stay in the stream so that it can be read at the next select() call. (this way, each user only execute one comamnd per "turn").
To do this, I have a FILE *stream per client that stay open as long as it is connected.
This work perfectly (if I send the double comamnd specified above, the two commands are executed one after the other).
The problem is after that after that, select() continue to tell me that there is something to read in the socket (FD_ISSET of the fd return 1), so my receive function is called for that fd and the getline() in it fail (without setting errno) and it loop like this forever.
I don't understand why the select still consider that there is something to read in the socket and why the getline is failing instead of blocking.
Any idea ?
edit: I'm not allowed to use non blocking socket or fork() for this project
The "main" loop:
while (g_run_server)
{
fd_max = g_socket_fd;
FD_ZERO(fds);
FD_SET(g_socket_fd, fds);
tmp = users;
while (tmp)
{
FD_SET(tmp->fd, fds);
fd_max = (tmp->fd > fd_max ? tmp->fd : fd_max);
tmp = tmp->next;
}
if (select(fd_max + 1, &fds, NULL, NULL, NULL) < 0)
break;
if (FD_ISSET(g_socket_fd, &fds))
accept_new_user(&hdl, &users);
handle_clients(&hdl, &fds);
}
The functions to handle and read client input :
static bool recv_and_execute(t_handle *hdl)
{
char *raw;
size_t len;
len = 0;
raw = NULL;
if (!hdl->sender->stream &&
(hdl->sender->stream = fdopen(dup(hdl->sender->fd), "r")) == NULL)
return (false);
if (getline(&raw, &len, hdl->sender->stream) != -1)
{
printf("Received \"%s\"\n", raw);
parse_cmd(hdl, raw);
exec_cmd(hdl);
}
else
printf("Getline failed %s\n", strerror(errno));
free(raw);
return (true);
}
int handle_clients(t_handle *hdl, fd_set *fds)
{
t_user *tmp;
tmp = *hdl->users;
while (tmp)
{
if (FD_ISSET(tmp->fd, fds))
{
printf("fd %d is ready to be read\n", tmp->fd);
hdl->sender = tmp;
recv_and_execute(hdl);
FD_CLR(tmp->fd, fds);
tmp = tmp->next;
if (hdl->sender->status == DEAD)
del_user(hdl->users, hdl->sender);
}
else
tmp = tmp->next;
}
return (0);
}
And this is the output when I connect one client and send "USER foo\r\nUSER no bo dy :wa\r\n" :
fd 4 is ready to be read
Received "NICK foo
"
[DEBUG] Executing "NICK" with params "foo" "(null)" "(null)" "(null)"
[INFO] Nickname successfully changed.
fd 4 is ready to be read
Received "USER no bo dy :wa
"
[DEBUG] Executing "USER" with params "no" "bo" "dy" ":wa"
[INFO] User registered.
fd 4 is ready to be read
Getline failed Success
fd 4 is ready to be read
Getline failed Success
fd 4 is ready to be read
Getline failed Success
fd 4 is ready to be read
Getline failed Success
fd 4 is ready to be read
Getline failed Success
continue like this....
Edit : I edited my receive function based on the comment of alk:
static bool recv_and_execute(t_handle *hdl)
{
char *raw;
size_t len;
ssize_t nread;
len = 0;
raw = NULL;
if (!hdl->sender->stream &&
(hdl->sender->stream = fdopen(dup(hdl->sender->fd), "r")) == NULL)
return (false);
errno = 0;
if ((nread = getline(&raw, &len, hdl->sender->stream)) > 0)
{
printf("Received \"%s\"\n", raw);
parse_cmd(hdl, raw);
exec_cmd(hdl);
}
else if (errno != 0)
printf("getline failed %s\n", strerror(errno));
else {
printf("EOF reached\n");
fclose(hdl->sender->stream);
hdl->sender->stream = NULL;
}
printf("nread = %zd\n", nread);
free(raw);
return (true);
}
So this time, when EOF is reach (getline return -1), I close the stream and set it to NULL to be reopened the next time select find data on the socket fd. But even when I close the stream, select still detect that there is data available and the inifinite loop continue :/
fd 4 is ready to be read
Received "NICK foo^M
"
nread = 10
fd 4 is ready to be read
Received "USER no bo dy :wa^M
"
nread = 19
fd 4 is ready to be read
EOF reached
nread = -1
fd 4 is ready to be read
EOF reached
nread = -1
fd 4 is ready to be read
EOF reached
nread = -1
and so on...
I'm pretty sure you're using the select wrong. I show you a simple code example on how to use it (I don't handle many errors) and you can edit it as you need.
/*
* If you read at man select bugs you can see
* that select could return that someone is
* ready but it isn't true
*/
int fd_c;
fd_set rdset;
fd_set set; /*That's the mask you'll use when new requests arrive*/
FD_ZERO(&set); /*Clears the mask*/
FD_SET(g_socket_fd,&set); /*Set the listening socket as ready*/
while(g_run_server){
/*
* YOU MUST INITIALIZATE IT EVERY LOOP
* read # man select
*/
rdset = set;
if(select((fd_num_max+1),(&rdset),NULL,NULL,NULL) < 0){
perror("Failed on select\n");
exit(EXIT_FAILURE);
}
/*You go through the ready clients in the rdset*/
for(fd=0;fd<=fd_num_max;fd++) {
if(FD_ISSET(fd,&rdset)) { /*If the bit is set*/
if(fd == fd_skt) { /*If it's a new client*/
/*You can handle the new client here or delegete it to someone*/
fd_c=accept(fd_skt,NULL,0); /*File descriptor of new client*/
FD_SET(fd_c,&set);
if(fd_c > fd_num_max) fd_num_max = fd_c;
}else { /*If it's a request from an existing client*/
/*You can handle the new request here or delegete it to someone*/
FD_SET(fd,&set);
}
}
}
}
You should also modify static bool recv_and_execute(t_handle *hdl) that way:
errno = 0;
if ((nread = getline(&raw, &len, hdl->sender->stream)) != -1){
printf("Received \"%s\"\n", raw);
parse_cmd(hdl, raw);
exec_cmd(hdl);
}else{
if( errno == 0){
printf("EOF reached\n");
fclose(hdl->sender->stream);
hdl->sender->stream = NULL;
}else{
printf("getline failed %s\n", strerror(errno));
exit(EXIT_FAILURE); /*You must handle it in some way, exiting or doing something*/
}
}

read system call on socket stuck [C language] - failed to send binary file over socket

I have client server architecture wherein I transfer a binary file from server to client.
Server Code:
FILE *outFile = fopen("/tmp/localpkg.deb","wb");
if( outFile == NULL )
{
RPTLOG(1, "Error opening file for writing.\n");
return -1;
}
FILE *readFilefp = fopen(file_name, "rb");
if ( readFilefp == NULL)
{
RPTLOG(1, "Error opening file.\n");
return -1;
}
char fileBuffer[1024];
bzero(fileBuffer, 1024);
while(1)
{
size_t readSize = fread(fileBuffer, sizeof(char), sizeof(fileBuffer) -1, readFilefp);
if(readSize == 0)
{
RPTLOG(1, "No more contents are left in file.\n");
n = write(sockfd, "\r\n", 2);
break;
}
n = write(sockfd, fileBuffer,readSize);
fwrite(&fileBuffer, sizeof(char), readSize, outFile);
RPTLOG(1, "Data[%d] written to sock=%s.\n", n, fileBuffer);
bzero(fileBuffer, 1024);
}
fclose(readFilefp);
fclose(outFile);
char *endfile_var =(char*) malloc(2048);
bzero(endfile_var,100);
strcpy(endfile_var,"ENDFILE\r\n");
n = write(sockfd, endfile_var, 9 );
RPTLOG(1, "ENDFILE text sent to client[%s] NumBytes sent=%d\n", endfile_var, n );
Client code:
FILE *fp = fopen(localfile, "wb");
if( fp == NULL )
{
RPTLOG(1, "Not enough permissions to write on disk, exiting....\n");
break;
}
memset(buf, 0, 2048);
//Started receiving installation package from server
while ((ret = read(sock, buf, 2047)) > 0) //Stcuk point: read blocks over here when it is about to receive last few lines of
{ //binary file from server
buf[ret] = '\0';
if ( ret == 1 )
{
RPTLOG(1, "Caught character where ret = %d\n", ret);
if ( buf[0] == '\n' )
continue;
}
if (strstr(buf, "ENDFILE") != 0 )
{
RPTLOG(1, "Endfile detected\n");
break;
}
else
{
fwrite(buf, 1, ret, fp);
}
memset(buf, 0, 2048);
}
if( ret == 0)
{
RPTLOG(4, "Connection closed from server = %d \n", ret );
}
else if( ret < 0)
{
RPTLOG(4, "Read error on client socket= %d \n", ret );
}
fclose(fp);
My Problem:
When server is about to send last few lines of binary file, client stuck in read call [stuck point in client code posted above]. Here client program is designed in such a way that when it receives "ENDFILE" line from server it will assume that file contents have been ended from server side and come out of while loop.
So kindly suggest the solution to receive binary file from server successfully.
[Special Note:] Client code has been wrapped up in a build and installed at customer end. So I have to make changes in server side code only. Also as indicated in server code above, for debugging purpose I have written content sent on socket to '/tmp/localpkg.deb' file as well. And that file contains all the contents which were written on socket at server side.
[Special Note 2:] When I try to send plain text file, I can send it successfully. Problem comes in only while sending binary file.
Thanks in advance.
The reason why your code fails is simple enough. You are reading a chunk of binary data into your buf, and than you call strstr on this buf. strstr goes until first \0 byte when looking for given token. Obviously, binary data will have a lot of those, so search stops after first one encountered and returns 'nothing was found'.
As a result, your code never exits the read loop, and keeps waiting for more data to arrive from the server - which will never send it.
The proper solution is, of course, implement a proper protocol for sending binary data. However, if this is impossible due to the listed constraints, a semi-workable solution would be to to replace strstr function with a custom one, which will go over provided buffer ignoring the the nul-terminators (using size of the buffer instead) and looks for the token provided. It will still break if your binary data has the token in it, but there is nothing you can do about it.

How to write and read at the same file using "popen" in C

I'm using Intel Edison and SensorTag. In order to get temperature data via BLE, there are a bunch of commands. When I define popen as:
popen(command,"w");
code works fine most of the times. (Crashes other times due to delay issues I assume as I don't control the responses.)
However, when I want to control the command/console responses (such as step into next line when bluetooth connection is established and if not try to connect again etc.), I cannot read the responses. My "data" variable is not changed.
I also tried other modes of "popen" but they give run-time errors.
Here is the code I'm using:
#include <stdlib.h>
#include <stdio.h>
#include <unistd.h>
int endsWith (char* base, char* str) {
int blen = strlen(base);
int slen = strlen(str);
return (blen >= slen) && (0 == strcmp(base + blen - slen, str));
}
FILE* get_popen(char* command, int close, int block) {
FILE *pf;
char data[512];
// Setup our pipe for reading and execute our command.
pf = popen(command,"w");
// Error handling
if (block == 1) {
// Get the data from the process execution
char* result;
do {
result=fgets(data, 512 , stderr);
if (result != NULL) {
printf("Data is [%s]\n", data);
}
} while (result != NULL);
// the data is now in 'data'
}
if (close != 0) {
if (pclose(pf) != 0)
fprintf(stderr," Error: Failed to close command stream \n");
}
return pf;
}
FILE* command_cont_exe(FILE* pf, char* command, int close, int block) {
char data[512];
// Error handling
if (pf == NULL) {
// print error
return NULL;
}
fwrite(command, 1, strlen(command), pf);
fwrite("\r\n", 1, 2, pf);
if (block == 1) {
// Get the data from the process execution
char* result;
do {
result=fgets(data, 512 , stderr);
if (result != NULL) {
printf("Data is [%s]\n", data);
}
} while (result != NULL);//
}
// the data is now in 'data'
if (close != 0) {
if (pclose(pf) != 0)
fprintf(stderr," Error: Failed to close command stream \n");
}
return pf;
}
int main()
{
char command[50];
sprintf(command, "rfkill unblock bluetooth");
get_popen(command, 1, 0);
printf("Working...(rfkill)\n");
sleep(2);
sprintf(command, "bluetoothctl 2>&1");
FILE* pf = get_popen(command, 0, 1);
printf("Working...(BT CTRL)\n");
sleep(3);
sprintf(command, "agent KeyboardDisplay");
command_cont_exe(pf, command, 0, 1);
printf("Working...(Agent)\n");
sleep(3);
//Main continues...
You cannot do this with popen, but can build a program using fork, exec and pipe. The last opens two file descriptors, which are related: the parent's connection to a pipe, and the child's connection. To make a two-way connection to a child process, you must use two calls to pipe.
The file-descriptors opened by pipe are not buffered, so you would use read and write to communicate with the child (rather than fgets and fprintf).
For examples and discussion, see
Does one end of a pipe have both read and write fd?
Read / Write through a pipe in C
UNIX pipe() : Example Programs
pipe(7) - Linux man page
6.2.2 Creating Pipes in C
Unfortunately, you can use popen() in one direction only. To get a bidirectional communication, you need to create two anonymous pipes with pipe() for stdin and stdout and assign them to the file handles 0 and 1 with dup2().
See http://tldp.org/LDP/lpg/node11.html for more details.

Reading and writing(at back) simultaneously in a fifo

I'm trying to copy the contents of a file1 into other file2 through fifo. The first four characters I want to write back in the fifo (during reading, not earlier when writing contents from file1 to fifo) and then copy it in the file2 also. But the first four characters don't get appended at back but they get inserted randomly in the middle. My code is
int main( int argc, char* argv[] ) {
int fdes,fdes1;
pid_t pid;
ssize_t numRead;
char readBuff[1];
char writeBuff[1];
int readCounter;
int c=0;
umask(0);
if (mkfifo("ajjp.e",0666) == -1 /*make the fifo*/
&& errno != EEXIST)
{}
if( argc < 3 ) {
printf( "Atleast need 2 params " );
exit(1);
}
int to_copy = open( argv[1], 0 );/* file from which contents are to be copied */
int oo = creat(argv[2], 0666);/* file created where we've to write contents*/
if ( to_copy == -1 ) {
printf( "Opening file failed " );
exit(1);
}
if ( (pid = fork()) < 0) /* child process is made*/
perror("fork error");
/* in parent process,I'm cwriting contents of file1 to fifo character by character */
else if(pid>0)
{
fdes = open("ajjp.e", O_WRONLY);
while( (readCounter = read( to_copy, readBuff, sizeof( readBuff ) ) > 0 ) ) {
write( fdes, readBuff, sizeof( readBuff ) );
}
close(to_copy);
}
/* now, in child process, I opened its read end then I'm reading contents from fifo and writing it to file2(i.e copy_to here) but for first four characters( c< 5 here), I'm writing them to fifo also by opening its write end. */
else
{
fdes1 = open("ajjp.e", O_RDONLY);
fdes = open("ajjp.e", O_WRONLY);
if (signal(SIGPIPE, SIG_IGN) == SIG_ERR)
printf("signal");
int copy_to = open( argv[2], 0666);/* opened the file where we've to write*/
if ( copy_to == -1 ) {
printf( "Opening file failed " );
exit(1);
}
for(;;) {
c++;
numRead = read(fdes1, readBuff, sizeof(readBuff));/* reading from read end of fifo*/
if (numRead == 0)
break;
/* write to the file2*/
if (write(copy_to, readBuff, numRead) != numRead)
{}
/* for first 4 characters, I am rewriting to the back of fifo*/
if(c<5)
{
write(fdes,readBuff,sizeof(readBuff));
}
/*after writing those 4 characters, write end I've closed*/
if(c==5)
close(fdes);
}
close(fdes);
close(fdes1);
}//end else
return 0;
}
Now, if on terminal, I run
$ ./a.out a.txt b.txt
I want to copy from a.txt to b.txt, b.txt contains a.txt plus first 4 characters inserted randomly between characters.
You've got some logic problems and synchronization problems. Your goal isn't too clear, but it seems like you want to copy a file, say "Hello World" and in the copy, it should have "Hello World" but also have "Hell" sprinkled in. So, maybe "HelHleo llWorld"?
Computers are fast and can buffer and do quite a lot at once. Your child process may not even execute until after the parent is completely done because it takes a bit of time to start a new process. As such you are probably getting "Hello WorldHell". That is, the parent totally copies the file to the FIFO before the child even starts reading. You need to look into some synchronization methods. For example, using the tools you have, you could make another fifo. Have the parent wait until it can read from it. Have the child write to it when it is loaded as a way to tell the parent it is ready to go. Making your file really really large might also give the child time to start or adding sleep statements after each character.
It would be easier to speculate if you had provided your input file as well as your output(the wrong output). But, basically, you have a multi-process/multi-threading problem where you want them to operate together but they really end up running one at a time. Slow down the parent or make it wait for the child.

select() not detecting incoming data

Objective: N nodes (running on different machines) should communicate with each other by establishing TCP connections with each other. Sending and receiving messages are done by 2 threads created by the process. Initially the main process connects all nodes with each other, creates the 2 threads and gives it a list of file descriptors which can be used by threads to send and receive data. The below structure is filled by the main process and passed to the threads.
typedef struct
{
char hostName[MAXIMUM_CHARACTERS_IN_HOSTNAME]; /* Host name of the node */
char portNumber[MAXIMUM_PORT_LENGTH]; /* Port number of the node */
char nodeId[MAXIMUM_NODE_ID_LENGTH]; /* Node ID of the node */
int socketFd; /* Socket file descriptor */
int socketReady; /* Flag to indicate if socket information is filled */
}SNodeInformation;
PS: socketFd is the socket descriptor received by either accept() or by socket() depending on how the connection was established (Either listening to connections from a node or connecting to a node).
An array of SNodeInformation of size MAX_NUM_OF_NODES is used.
The send thread goes through the nodeInformation and sends a message "Hello" to all nodes as except itself show below.
void *sendMessageThread(void *pNodeInformation) {
int i;
int ownNodeId;
int bytesSent = 0;
char ownHostName[MAXIMUM_CHARACTERS_IN_HOSTNAME];
SNodeInformation *nodeInformation = (SNodeInformation *) pNodeInformation;
SNodeInformation *iterNodeInformation;
printf("SendMessageThread: Send thread created\n");
if(gethostname(ownHostName, MAXIMUM_CHARACTERS_IN_HOSTNAME) != 0) {
perror("Error: sendMessageThread, gethostname failed\n");
exit(1);
}
for(i=0, iterNodeInformation=nodeInformation ; i<MAXIMUM_NUMBER_OF_NODES ; i++, iterNodeInformation++) {
if(strcmp((const char*) iterNodeInformation->hostName, (const char*) ownHostName) != 0) {
/* Send message to all nodes except yourself */
bytesSent = send(iterNodeInformation->socketFd, "Hello", 6, 0);
if(bytesSent == -1) {
printf("Error: sendMessageThread, sending failed, code: %s FD %d\n", strerror(errno), iterNodeInformation->socketFd);
}
}
}
pthread_exit(NULL);
}
The receive thread goes through the nodeInformation, sets up a file descriptor set and uses select to wait for incoming data as show below.
void *receiveMessageThread(void *pNodeInformation)
{
int i;
int fileDescriptorMax = -1;
int doneReceiving = 0;
int numberOfBytesReceived = 0;
int receiveCount = 0;
fd_set readFileDescriptorList;
char inMessage[6];
SNodeInformation *nodeInformation = (SNodeInformation *) pNodeInformation;
SNodeInformation *iterNodeInformation;
printf("ReceiveMessageThread: Receive thread created\n");
/* Initialize the read file descriptor */
FD_ZERO(&readFileDescriptorList);
for(i=0, iterNodeInformation=nodeInformation ; i<MAXIMUM_NUMBER_OF_NODES ; i++, iterNodeInformation++) {
FD_SET(iterNodeInformation->socketFd, &readFileDescriptorList);
if(iterNodeInformation->socketFd > fileDescriptorMax) {
fileDescriptorMax = iterNodeInformation->socketFd;
}
}
printf("ReceiveMessageThread: fileDescriptorMax:%d\n", fileDescriptorMax);
while(!doneReceiving) {
if (select(fileDescriptorMax+1, &readFileDescriptorList, NULL, NULL, NULL) == -1) {
perror("Error receiveMessageThread, select failed \n");
return -1;
}
for(i=0 ; i<fileDescriptorMax ; i++) {
if (FD_ISSET(i, &readFileDescriptorList)) {
/* Check if any FD was set */
printf("ReceiveThread: FD set %d\n", i);
/* Receive data from one of the nodes */
if ((numberOfBytesReceived = recv(i, &inMessage, 6, 0)) <= 0) {
/* Got error or connection closed by client */
if (numberOfBytesReceived == 0) {
/* Connection closed */
printf("Info: receiveMessageThread, node %d hung up\n", i);
}
else {
perror("Error: receiveMessageThread, recv FAILED\n");
}
close(i);
/* Remove from Master file descriptor set */
FD_CLR(i, &readFileDescriptorList);
doneReceiving = 1;
}
else {
/* Valid data from a node */
inMessage[6] = '\0';
if(++receiveCount == MAXIMUM_NUMBER_OF_NODES-1) {
doneReceiving = 1;
}
printf("ReceiveThread: %s received, count: %d\n", inMessage, rece iveCount);
}
}
}
}
pthread_exit(NULL);
}
Expected Output: I tried with just 2 processes, P1 (Started first) and P2 running on machine1 and another on machine2. Both the processes in the machines should first connect and then the threads should send and receive the message "Hello" and exit.
Observed Output: The P1 is able to send the message and P2 (receiver thread) is able to receive the message "Hello". But P1 (receiver thread) is not able to get the message from P2 (Sending thread). Application code is the same in both the machines but every time, the process started first does not get the message from the other process. I added a print to just check if some file descriptor was set, but I don't see it for P1 but only for the P2. The send in the receiving process is not failing, it returns with 6. I checked the maximum value of file descriptors, its correct.
If I start P2 first and then P1 then I can see that P1 receives the message from P2 and exists while P2 waits infinitely for the message from P1.
I am not sure if the problem is because of incorrect use of socket descriptors or because of threads ?
Two issues:
1 The loop testing for a file descriptor being set, does not include all file descriptors put into the set. (This programming error is expected to be the reason for the malfunction described in the OP.)
2 The sets of file descriptors passed to select() are modified by select(), so the set need to be re-initialized before for select() again. (The programming error would only be notable if from more than one socket data sall be received.)
Please see the following mod/s to the OP's code:
void *receiveMessageThread(void *pNodeInformation)
{
...
printf("ReceiveMessageThread: Receive thread created\n");
while(!doneReceiving) {
/* Initialize the read-set of file descriptors */
/* Issue 2 fixed from here ... */
FD_ZERO(&readFileDescriptorList);
for(i=0, iterNodeInformation=nodeInformation ; i<MAXIMUM_NUMBER_OF_NODES ; i++, iterNodeInformation++) {
FD_SET(iterNodeInformation->socketFd, &readFileDescriptorList);
if (iterNodeInformation->socketFd > fileDescriptorMax) {
fileDescriptorMax = iterNodeInformation->socketFd;
}
}
/* ... up to here. */
printf("ReceiveMessageThread: fileDescriptorMax:%d\n", fileDescriptorMax);
if (select(fileDescriptorMax+1, &readFileDescriptorList, NULL, NULL, NULL) == -1) {
perror("Error receiveMessageThread, select failed \n");
return -1;
}
for(i=0 ; i <= fileDescriptorMax ; i++) { /* Issue 1 fixed here. */
...

Resources