Welcome to OStack Knowledge Sharing Community for programmer and developer-Open, Learning and Share
Welcome To Ask or Share your Answers For Others

Categories

0 votes
794 views
in Technique[技术] by (71.8m points)

c - Read line by line from a socket buffer

I want to write a function that read line by line from a socket buffer obtained from third parameter from read() function from unistd.h header.

I have wrote this:

int sgetline(int fd, char ** out)
{
    int buf_size = 128;
    int bytesloaded = 0;
    char buf[2];
    char * buffer = malloc(buf_size);
    char * newbuf;
    int size = 0;

    assert(NULL != buffer);

    while( read(fd, buf, 1) > 0 )
    {
        strcat(buffer, buf);
        buf[1] = '';
        bytesloaded += strlen(buf);
        size = size + buf_size;

        if(buf[0] == '
')
        {
            *out = buffer; 
            return bytesloaded;
        }

        if(bytesloaded >= size)
        {
            size = size + buf_size;
            newbuf = realloc(buffer, size);

            if(NULL != newbuf)
            {
                buffer = newbuf;
            }
            else 
            {
                printf("sgetline() allocation failed!
");
                exit(1);
            }
        }
    }

    *out = buffer;
    return bytesloaded;
}

but I have some problems with this function, for example, if the input is something like:

HTTP/1.1 301 Moved Permanently

Cache-Control:no-cache

Content-Length:0

Location
http://bing.com/




and I do

int sockfd = socket( ... );
//....
char* tbuf;
while(sgetline(sockfd, &tbuf) > 0)
{
    if(strcmp(tbuf,"

") == 0)
    {
       printf("End of Headers detected.
");
    }
}

the above C application does not output "End of Header detected.". Why is this, and how can I fix this?

See Question&Answers more detail:os

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
Welcome To Ask or Share your Answers For Others

1 Answer

0 votes
by (71.8m points)

It's not OK to read one byte at a time, because you are making too many system calls - better is to use a buffer, read a chunk and check if you got . After getting a line, the rest of the bytes read remains in the buffer, so you cannot mix read/recv with read_line. Another version of read n bytes using this kind of buffer can be write...

My version to read a line, and a little example to use it.

#include <stdio.h>
#include <errno.h>
#include <stdlib.h>
#include <sys/socket.h>
#include <unistd.h>
#include <netinet/in.h>
#include <netdb.h>
#include <arpa/inet.h>
#include <string.h>

#define CBSIZE 2048

typedef struct cbuf {
    char buf[CBSIZE];
    int fd;
    unsigned int rpos, wpos;
} cbuf_t;


int read_line(cbuf_t *cbuf, char *dst, unsigned int size)
{
    unsigned int i = 0;
    ssize_t n;
    while (i < size) {
        if (cbuf->rpos == cbuf->wpos) {
            size_t wpos = cbuf->wpos % CBSIZE;
            //if ((n = read(cbuf->fd, cbuf->buf + wpos, (CBSIZE - wpos))) < 0) {
            if((n = recv(cbuf->fd, cbuf->buf + wpos, (CBSIZE - wpos), 0)) < 0) {
                if (errno == EINTR)
                    continue;
                return -1;
            } else if (n == 0)
                return 0;
            cbuf->wpos += n;
        }
        dst[i++] = cbuf->buf[cbuf->rpos++ % CBSIZE];
        if (dst[i - 1] == '
')
            break;
    }
    if(i == size) {
         fprintf(stderr, "line too large: %d %d
", i, size);
         return -1;
    }

    dst[i] = 0;
    return i;
}

int main()
{
    cbuf_t *cbuf;
    char buf[512];
    struct sockaddr_in saddr;
    struct hostent *h;
    char *ip;
    char host[] = "www.google.com";

    if(!(h = gethostbyname(host))) {
        perror("gethostbyname");
        return NULL;
    }
    ip = inet_ntoa(*(struct in_addr*)h->h_addr);

    cbuf = calloc(1, sizeof(*cbuf));

    fprintf(stdout, "Connecting to ip: %s
", ip);
    if((cbuf->fd = socket(AF_INET, SOCK_STREAM, 0)) < 0) {
        perror("socket");
        return 1;
    }
    memset(&saddr, 0, sizeof(saddr));
    saddr.sin_family = AF_INET;
    saddr.sin_port = htons(80);
    inet_aton(ip, &saddr.sin_addr);
    if(connect(cbuf->fd, (struct sockaddr*)&saddr, sizeof(saddr)) < 0) {
        perror("connect");
        return 1;
    }

    snprintf(buf, sizeof(buf), "GET / HTTP/1.1
Host: %s
Connection: close

", host);
    write(cbuf->fd, buf, strlen(buf));
    while(read_line(cbuf, buf, sizeof(buf)) > 0) {
        // if it's an empty 
 on a line, header ends //
        if(buf[0]=='
' && buf[1] == '
') {
            printf("------------------------
");
        }
        printf("[%s]", buf);
    }
    close(cbuf->fd);
    free(cbuf);
    return 0;
}

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
Welcome to OStack Knowledge Sharing Community for programmer and developer-Open, Learning and Share
Click Here to Ask a Question

...