2

So I programmed a multi threaded web server, here is one function from the program. This function takes output file descriptor (fd), content type, pointer to data to be served (*buf) and size of the data (numbytes). It always gets stuck at 5775 bytes! I've tried using write() instead of send(), but no avail! I tried to send whole buf at a time, and even tried to transfer it in chunks, but wget shows that it gets stck at 5775 bytes! Here is the code:

int return_result(int fd, char *content_type, char *buf, int numbytes)
{
    char out_buf[BUF_SIZE], numb[6];
    int buf_len, total = 0, buf_size;
    long int i = 0;
    sprintf(numb, "%d", numbytes);
    strcpy(out_buf, "HTTP/1.1 200 OK \nContent-Type: ");
    strcat(out_buf, content_type);
    strcat(out_buf, "\nContent-Length: ");
    strcat(out_buf, numb);
    strcat(out_buf, "\nConnection: Close\n  \n");
    printf("\nSending HTTP Header\n %d bytes sent!",
           send(fd, out_buf, strlen(out_buf), 0));
    char *start = NULL, *str = NULL, *temp = NULL;
    start = buf;
    printf("\n Start Pointer Val = %ld", &start);
    while (start != NULL) {
        printf("\n While Loop");
        if (i + 2048 * sizeof(char) < numbytes) {
            printf("\n If 1");
            str = (char *)malloc(sizeof(char) * 2048);
            memcpy(str, start, sizeof(char) * 2048);
            i = i + 2048 * sizeof(char);
            buf_size = send(fd, str, 2048, 0);
            free(str);
            printf("\n Sent %d bytes total : %d", buf_size, total =
                   total + buf_size);

            temp = start + sizeof(char) * 2048;
            start = temp;

        } else {

            i = numbytes - i * sizeof(char);
            if (i > 0) {
                printf("\n If 2");
                printf("\n Value of i %d", i);
                str = (char *)malloc(sizeof(char) * i);
                memcpy(str, start, sizeof(char) * i);
                printf("Total bytes finally sent:%d", total =
                       total + send(fd, str, i, 0));
                if (total == numbytes) {
                    printf("\nTransfer Complete!");
                }
                free(str);

            }
            start = NULL;
        }
    }
    printf("out of loop!");
    return 0;
}
2
  • Are you actually getting the "Total bytes finally sent:.." message matching what you expect? If so it may be a buffering/flushing issue. Commented Dec 16, 2011 at 1:13
  • Yes, infact I'm send()ing all the bytes from server side, but wget receives only 5775! Commented Dec 16, 2011 at 1:16

2 Answers 2

2

I'd like to suggest replacing your code with the following writen() function from Advanced Programming in the Unix Environment, 2nd edition:

ssize_t             /* Write "n" bytes to a descriptor  */
writen(int fd, const void *ptr, size_t n)
{
        size_t          nleft;
        ssize_t         nwritten;

        nleft = n;
        while (nleft > 0) {
                if ((nwritten = write(fd, ptr, nleft)) < 0) {
                        if (nleft == n)
                                return(-1); /* error, return -1 */
                        else
                                break;      /* error, return amount written so far */
                } else if (nwritten == 0) {
                        break;
                }
                nleft -= nwritten;
                ptr   += nwritten;
        }
        return(n - nleft);      /* return >= 0 */
}

This code is already debugged and known working, and further allows write(2) to write PIPE_BUF bytes at a go for better speed when things are working well.

send(2) should block if it cannot send all the data you have requested, though. I think more interesting would be debugging the version with plain send(2) without any of the surrounding efforts to break things into blocks.

Better than both write(2) and send(2) would be sendfile(2) -- open the file, pass the descriptor and socket to sendfile(2), and let the kernel handle it all for you, using zero-copy mechanisms if possible.

One last point: HTTP uses CRLF, not plain carriage returns. Each \n should be replaced with \r\n.

Sign up to request clarification or add additional context in comments.

1 Comment

Thanks a lot for your inputs! I added your code, but wget still gets stuck on 5775 bytes! I ssh'ed into different machines, and turns out I get same problem everywhere! Any thoughts?
1

Try something like this (printf() statements omitted for clarity):

int send_buf(in fd, void *buf, int numbytes)
{
    char *start = (char*) buf;
    while (numbytes > 0)
    {
        int sent = send(fd, start, numbytes, 0);
        if (sent <= 0)
        {
            if ((sent == -1) && (errno == EAGAIN))
            {
                fd_set wfds;
                FD_ZERO(&wfds);
                FD_SET(fd, &wfds);
                if (select(fd + 1, NULL, &wfds, NULL, NULL) == 1)
                    continue;
            }
            return -1;
        }

        start += sent;
        numbytes -= sent;
    }

    return 0;
}

int return_result(int fd, char *content_type, void *buf, int numbytes) 
{ 
    char out_buf[BUF_SIZE], 

    int len = sprintf(out_buf,
        "HTTP/1.1 200 OK\r\n"
        "Content-Type: %s\r\n"
        "Content-Length: %d\r\n"
        "Connection: Close\r\n"
        "\r\n",
        content_type,
        numb); 

    if (send_buf(fd, out_buf, len) != 0)
        return -1;

    if (send_buf(fd, buf, numbytes) != 0)
        return -1;

    return 0; 
}

4 Comments

Perfect! Thank you very much! What was the problem with my solution?
When you were sending a particular block of data, whether it was your formatted HTTP headers or a chunk of the input data buffer, you were calling send() only once per block. That is not guaranteed to send the entire block in one go. In fact, it very likely won't in many cases. send() returns how many bytes were actually accepted. You have to keep calling send() until the enire block has been sen. In my example, I do exactly that, with an extra call to select() for sockets operating in non-blocking mode (you did not say what you are using).
Another bug in your code was this line: temp = start + sizeof(char) * 2048;. That should have been this instead: temp = start + (buf_size * sizeof(char));, which can be simplified to this: temp = start + buf_size; using pointer arithmetic, which can then be further simplified to this: start += buf_size; like I use in my code.
Thanks for your detailed response! I totally get it now!

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.