0

I am trying to create a server and client program that sends a string from client to server where the server executes that string and sends the output back to the client. I am doing this in linux and I am very confused why my program isnt working the least bit. Here is the code.

**Client**
int main()
{
    //Code to use unix socket here

    if (connect(s, (struct sockaddr *)&remote, len) == -1) {
        perror("connect");
        exit(1);
    }

    printf("Connected.\n");

    while(printf("> "), fgets(str, MAX, stdin), !feof(stdin)) {
        if (send(s, str, strlen(str), 0) == -1) {
            perror("send");
            exit(1);
        }
    }
    done=0;
    do {
        if(t=recv(s, str, MAX, 0)<0)
            perror("recv failed at client side!\n");
        str[t] = '\0';
        if(strcmp(str, "ENDOFTRANS")==0)
        {
            printf("\nRead ENDOFTRANS. Breaking loop.\n");
            done=1;
        }
        printf("Server > %s", str);
    } while(!done);
}

And then the server code is:

**Server**

#define MAX 1000
int main(void)
{
//Unix socket code

//This process is now a daemon.
daemon();


//Listens for client connections, up to 5 clients can queue up at the same time.
if (listen(s, 5) == -1) {
    perror("listen");
    exit(1);
}

for(;;) {
    int done, n, status;

    printf("Waiting for a connection...\n");
    t = sizeof(remote);

    if ((newsock= accept(s, (struct sockaddr *)&remote, &t)) == -1) {
            perror("accept");
            exit(1);
    }

            printf("Connected.\n");

    done = 0;
    do {
        switch(fork())
        {
            case -1:  //ERROR
            perror("Could not fork.\n");
            break;

            case 0:  //CHILD

            //Accept string from client.
            //Edit: Why am I getting an error here? says: Invalid argument.
            if(n = recv(newsock, str, MAX, 0)) {
                 perror("Recv error at server side.\n");
                 exit(1);
            }
            str[n]='\0';
            if (n <= 0) {
                if (n < 0)
                    perror("recv");
                done = 1;
            }
            printf("String=>%s<",str);

            //Redirect socket to STDOUT & STDERR.
            test = close(WRITE);        assert(test==0);
            test = dup(newsock);        assert(test==WRITE);
            test = close(ERROR);        assert(test==0);
            test = dup(newsock);        assert(test==ERROR);



            if (!done)
            {
                if (str==something)
                {
                    //execute command
                }

                else {
                    //Fork and execvp the command
                }

                //Sends End of Transaction character.
                ENDTHETRANS();
                exit(0);
            }
            break;

            default:  //PARENT
                //Parent keeps accepting further clients.
                wait(&status);
                if ((newsock= accept(s, (struct sockaddr *)&remote, &t)) == -1) {
                    perror("accept");
                    exit(1);
                }

                printf("Connected.\n");
                done=1;
                break;

        }
    } while (!done);
}
close(s);
}

Im relatively new to programming in general and from my understanding the client code is good except that when it recieves the text back from the server it only recieves the text in small bits (2 rows at a time). I have to keep pressing enter on client promt to get the rest of the input. I have tried so many things that by this point I dont even know what I am doing wrong anymore.

Firstly, in the server code, after it recieves the string from the client I have a printf("String=>%s<",str); that outputs the string. However when the server prints the output as String=>ls -l the < key at the end gets eaten up somehow. It shouldnt be doing that right?

Any help much appreciated. Please bare in mind that I am a beginner and have only used pipes as inter process communcation before. Now I wanna make my first unix socket program.

Thanks in advance.

Arman Iqbal
  • 725
  • 2
  • 8
  • 14
  • You client code won't even compile due to unbalanced `{}` -- try posting an [sscce](http://sscce.org) – Chris Dodd Feb 23 '14 at 18:42
  • Closely read the man-pages for recv()/send() and learn that those two functions do not necessarily receive/send as much bytes as they were told to, but few. So looping around such calls counting until all data or a terminator had been received/sent is a good idea, not to say an essential necessity. – alk Feb 23 '14 at 21:43
  • Thanks for the answer @alk. I do not understand why it wouldnt send as many bytes as I specified it to. But I will take your word for it =). So you are saying that wether a long or short string I am sending, I would add an "end of transaction" (character) at the end of each `send()`, and I should ask the other end to look for said character? Take a look at the one place in my code where I am calling the `ENDTHETRANS()` function. Is that the solution you are refering to? (but everytime i use recv?) – Arman Iqbal Feb 23 '14 at 22:13
  • The end-of-transaction indicator is a good idea, when transitting data via a stream (what a TCP socket is) but it does not keep you from tracking the number of bytes that **really** had sent/received. And this you can only do by inspecting the return value of the send*/recv* calls. – alk Feb 23 '14 at 22:30

1 Answers1

2

The usual problem in cases such as this is not realizing that SOCK_STREAM sockets don't preserve message boundaries. So data sent with a send call might be split up and received in multiple recvs, or it might be coalesced and multiple sends end up in a single recv. Most importantly, when a kernel send buffer fills up, a send call might write partial data (sending only some of the requested data) and return a short return value. You need to test for this and resend the rest of the data.

Another problem that often shows up is issues with line endings (particularly when talking between linux and windows). There may be extra carriage return characters (\r) in the either the client or server that confuse the other side. These tend to result in apparently missing or truncated output when printed.

edit

The line

if(t=recv(s, str, MAX, 0)<0)

is equivalent to

if(t = (recv(s, str, MAX, 0)<0))

that is, it sets t to 0 or 1 depending on whether there was an error or not. As with most errors of this type, turning on warnings will give you some indication about it.

Chris Dodd
  • 119,907
  • 13
  • 134
  • 226
  • What do you suggest I do? I was told that in the client code, if i put the `recv()` inside a `while loop` it should grab everything from the socket. However it isnt doing that. Im using the `done` variable to keep track of when the ENDOFTRANSACTION is read and change done to 1. – Arman Iqbal Feb 23 '14 at 21:36
  • @ArmanIqbal: See my comment in the question. You currently appear to have the receive loop nested in the send loop, but it's unclear as the indentation is all messed up and there's a missing `}` somewhere. – Chris Dodd Feb 23 '14 at 23:09
  • I fixed that. I messed up while copy pasting the code here. However I now an getting an error when the server code calls `recv()`. At line `n = recv(newsock, str, MAX, 0);` – Arman Iqbal Feb 23 '14 at 23:13
  • I am 4 months into my uni programming course. I have no idea how to provide an sscce. First time I am hearing about it. Not entirely sure how its done even after googling. – Arman Iqbal Feb 24 '14 at 09:27