Jump to content

inane beginner's question

- - - - -

This topic has been archived. This means that you cannot reply to this topic.
8 replies to this topic

#1
junket

junket

    Newbie

  • Members
  • Pip
  • 5 posts
Hi,

I've been doing some C on linux for an OS course.
We had an exercise on sockets. I got the basic question done, where a fork child holds one end of a socket, receives a message from the parent, prints them to screen, then sends a message back to the parent, which prints the message to screen, on an infinite loop.
There was an extra question though, where the child sends different messages to the parent, depending on the message it receives from it. That was all it said. My attempted solution is below.
It is crude in many ways, I am sure. The main problem though is that when the parent prints the messages it receives from the child, it runs over from one message into the next.

i.e. the two messages it receives in turn are "so is this" and "neither is this"
but it eventually starts printing out something like "so is this neith"

So my central, basic question is how do I get the parent to read only the sufficient amount from the socket, once strings are concatenated into it (which seems to be the case). Or rather, is there some way when writing to the socket to punctuate the messages sent to it so that the reader knows when to stop reading?

I think one of the problems is synchronization - ideally, there should only be one message to read in the socket at a time, but this is not how it has panned out.

Should I use flags to make sure that only one string is written into each end of the socket at one time?

Thanks for your help.

Here's the code: (there are probably a few unneccessary quirks in it due to various vain attemps to solve the problem)

#include <fcntl.h>
#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/socket.h>

main(){
        int pid;
    int socket[2];
    int n = 1;
    int m;
    int p;
        char buffer1[20];
        char buffer2[16];
    socketpair(AF_UNIX, SOCK_STREAM, 0, socket);

    pid = fork();
    
    while(n==1){ 
        
        if (pid > 0){
    
            close(socket[1]);
            printf("Parent Process writing \n");
            write(socket[0], "This is not a pipe\n", 20);
            sleep(5);
            printf("Parent Process writing \n");
            write(socket[0], "This is a socket\n", 18);            
            sleep(5);
            p = read(socket[0], buffer2, 16);
            printf("Parent process reading\n");
            write(1, buffer2, p);
                        sleep(5);
        }        
    
        
        
        if (pid == 0){
        
            
            close(socket[0]);

            
            m = read(socket[1], buffer1, 20);
            
            printf("Child Process reading\n");
            
            
            write(1, buffer1, 20);
            
            sleep(5);
            

            
            if (m > 19){
                
                printf("Child process writing\n");            
                
                write(socket[1], "Neither is this\n",16);
            
            }
            

            else{
                
                printf("Child process writing\n");
                
                write(socket[1], "So is this\n",11);
            
            }
            
            sleep(5);

        
        }
    
    }

}

Edited by ZekeDragon, 25 March 2010 - 03:20 AM.
Please use [code] tags (the # button) when posting code.


#2
dargueta

dargueta

    Writes binary right handed and hex left handed

  • Moderators
  • 4,717 posts
Anal-retentive note: please edit your post to use the code tags. Highlight your code and click on the # button. It makes reading your code easier.

Anyway, I would suggest sending the length of the string (including the terminating null if you're sending it as well) in an integer, followed by the string. Make sure you use the htonl/ntohl functions. Since different machines may have different ways of representing integers you're going to want to convert it to the standard network representation before sending it, so the receiving machine can convert it back to its native representation. So you're going to use something like this:

Sending end:
#include <stdint.h>
#include <arpa/inet.h>

...

char *message = "Hello, World!";
uint32_t length = strlen(message);
uint32_t nwlength = htonl(length);

send(sockid, &nwlength, sizeof(uint32_t), your flags here);
send(sockid, message, length, your flags here);
Receiving end:
#include <stdint.h>
#include <arpa/inet.h>

...

uint32_t nwlength, length;
char *message;

recv(sockid, &nwlength, sizeof(uint32_t), your flags);
length = ntohl(nwlength);

message = calloc(length,sizeof(char));
/* null checks and whatever */
recv(sockid, message, length, your flags);

...blah...
free(message);

Edited by dargueta, 24 March 2010 - 06:41 PM.
Typo

sudo rm -rf /

#3
junket

junket

    Newbie

  • Members
  • Pip
  • 5 posts
Thank you Dargueta, and sorry about the code format, or lack thereof.

There are only a couple of things I don't understand.
Firstly, a "terminating null" - we haven't done that. How do I send that? How do I append a NULL to a string?
Secondly, in the send/receive command, it includes flags as a final parameter. Are these flags in the sense of synchronization/semaphores, all that lark?
Sorry for the simplicity of the questions. you've been very helpful. I will use send/receive in future.

#4
dargueta

dargueta

    Writes binary right handed and hex left handed

  • Moderators
  • 4,717 posts
The terminating null is appended automatically to string constants or any string read by the *scanf functions. For example:
char *message = "Hello, CodeCall!";

/* compiler sticks a '\0' at the end behind your back.
this is the same as */

char message[] = {'H','e','l','l','o', ',', ' ', 'C', 'o', 'd', 'e', 'C', 'a', 'l', 'l', '!', [B]'\0'[/B]};

The terminating null in C/C++ indicates the end of the string in all strings. (Not std::string, that's something else altogether.) You don't have to add it yourself. Anyway, strlen() does not include the terminating null in its count, so if it returns a length of 20 bytes, there's actually 21 bytes in the string, but you never really directly manipulate the terminating null so you don't care about it.

And yes, the flags are about synchronization, not semaphores. If you look at the man pages for send() and recv() you'll see a list of all the flags at the end and descriptions of what they do. If you don't want any of them, just leave the flags field as 0.

Edited by dargueta, 25 March 2010 - 10:51 AM.
Clarification

sudo rm -rf /

#5
junket

junket

    Newbie

  • Members
  • Pip
  • 5 posts
Thanks a million.
That's plenty for me to be getting on with.
Will experiment with the flags in send/receive.
Thanks again!

#6
dargueta

dargueta

    Writes binary right handed and hex left handed

  • Moderators
  • 4,717 posts
No problem. Any questions, I'll be floating around here from time to time. I'm on spring break. :)
sudo rm -rf /

#7
junket

junket

    Newbie

  • Members
  • Pip
  • 5 posts
Hi again,
I've been trying to implement the code. Have quite a bit of debugging to get through though, and I have to admit that I am struggling .
I also still cannot resolve my central problem: namely, when the parent prints the messages it receives from the child, it runs over from one message into the next, and vice versa. :crying:
Is there any simple way for the reader/receiver to distinguish between one string in the socket and the next?
It still comes out like:
"This is a socket
Th...(other prints)...is a ...(etc)"
I have tried using calloc to allocate the size of output but it doesn't seem to work for me.
Thanks!

#8
junket

junket

    Newbie

  • Members
  • Pip
  • 5 posts
This is my current version of the code. I have not used the htonl(etc) commands for the moment as I am just using the socket on the one machine, and I want to hone in on the specific problem.
I have tried send, and recording the length of the string, but the result is exactly the same - the print-to-screen statements overlap the messages read in through the socket.
Thanks for your help

#include <fcntl.h>

#include <stdio.h>

#include <stdlib.h>


#include <sys/types.h>

#include <sys/socket.h>


main(){


	int pid;

	int socket[2];

	int n = 1;

	int m;

	int p;


	char *buffer1;

	char *buffer2;


	char *pmessage1 = "This is not a pipe\n";

	char *pmessage2 = "This is a socket\n";

	int plength;

	int prlength;	

	

	char *cmessage1 = "Neither is this\n";

	char *cmessage2 = "So is this\n";

	int clength;

	int crlength;


	socketpair(AF_UNIX, SOCK_STREAM, 0, socket);


	pid = fork();

	

	while(n==1){ 

		if (pid > 0){

			char buffer2[16];

			close(socket[1]);

			printf("Parent Process writing \n");


			plength = strlen(pmessage1);

			write(socket[0], plength, sizeof(plength));

			write(socket[0], pmessage1, plength);

			sleep(5);

			

			printf("Parent Process writing \n");

			plength = strlen(pmessage2);

			write(socket[0], plength, sizeof(plength));

			write(socket[0], pmessage2, plength);			

			sleep(5);

			printf("Parent process reading\n");

			read(socket[0], prlength, sizeof(int));

			buffer2 = calloc(prlength,sizeof(char));


			read(socket[0], buffer2, prlength); 	

			write(1, buffer2, prlength);		


			/*do{

				buffer[p] = NULL;

				p = read(socket[0], buffer2, 5);

				printf("%s", buffer2);

			} while (p==5);*/


			


			sleep(5);

		}		

	

		if (pid == 0){

		

			close(socket[0]);

			read(socket[1], clength, sizeof(int));

			buffer1 = calloc(crlength, sizeof(char)); 

			read(socket[1], buffer1, crlength);

			printf("Child Process reading\n");

			

			write(1, buffer1, crlength);

			sleep(5);

			


			if (crlength > 19){

				printf("Child process writing\n");			

				clength = strlen(cmessage1);

				write(socket[1], clength, sizeof(clength));

				write(socket[1], cmessage1, clength);	

			}

			else{

				printf("Child process writing\n");

				clength = strlen(cmessage2);

				write(socket[1], clength, sizeof(clength));

				write(socket[1], cmessage2, clength);

			}

			sleep(5);


		}

	}

}


#9
dargueta

dargueta

    Writes binary right handed and hex left handed

  • Moderators
  • 4,717 posts
OOOH. You're writing to the same terminal, aren't you? I thought you had two separate programs running. This is not a good idea. Anyway, I strongly suggest you use send() and recv() for two reasons:
1) You're supposed to.
2) Your problem with overlapping is partly due to the fact that read() just gets whatever's in the buffer regardless of how long the message is. If you want to use read() you're going to have to do this annoying loop thing, or you can use recv() and specify the MSG_WAITALL flag that will force recv() to wait until all the data you requested is read.
sudo rm -rf /