2012-03-22 2 views
2

Ну, мне нужно помочь: Я хочу написать функцию, которая читается по строкам из буфера сокета, полученного из третьего параметра из read(), из заголовка unistd.h.чтение строки за строкой из буфера сокета

Я написал:

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] = '\0'; 
     bytesloaded += strlen(buf); 
     size = size + buf_size; 

     if(buf[0] == '\n') 
     { 
      *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!\n"); 
       exit(1); 
      } 
     } 
    } 

    *out = buffer; 
    return bytesloaded; 
} 

, но у меня есть некоторые проблемы с этой функцией, например, если вход что-то вроде:

HTTP/1.1 301 Moved Permanently\r\n 
Cache-Control:no-cache\r\n 
Content-Length:0\r\n 
Location\r\nhttp://bing.com/\r\n 
\r\n\r\n 

и я

int sockfd = socket(...); 
//.... 
char* tbuf; 
while(sgetline(sockfd, &tbuf) > 0) 
{ 
    if(strcmp(tbuf,"\r\n\r\n") == 0) 
    { 
     printf("End of Headers detected.\n"); 
    } 
} 

вышеуказанное приложение C не выводит "End of Header detected.". Зачем? как это исправить? советы по улучшению этих функций очень ценятся. Заранее спасибо. :)

+0

Как получить значок «Известный вопрос» с ** 0 ** upvotes haha. – Jack

+0

См. Реализацию [TLPI readline] (http://man7.org/tlpi/code/online/dist/sockets/read_line.c.html). – mmoya

ответ

2

Вы делаете вещи более трудными для себя, чем они должны быть. Вам действительно не нужно делать strcats, чтобы получить один символ, который вы читаете, при каждом чтении, добавленном в текущую позицию.

Но ваша ошибка заключается в том, что процедура возвращается, как только она видит \ n, поэтому возвращаемая строка никогда не может содержать ничего, следующего за первым \ n.

3

Попробуйте эту реализацию вместо:

int sgetline(int fd, char ** out) 
{ 
    int buf_size = 0; 
    int in_buf = 0; 
    int ret; 
    char ch; 
    char * buffer = NULL; 
    char * new_buffer; 

    do 
    { 
     // read a single byte 
     ret = read(fd, &ch, 1); 
     if (ret < 1) 
     { 
      // error or disconnect 
      free(buffer); 
      return -1; 
     } 

     // has end of line been reached? 
     if (ch == '\n') 
      break; // yes 

     // is more memory needed? 
     if ((buf_size == 0) || (in_buf == buf_size)) 
     { 
      buf_size += 128; 
      new_buffer = realloc(buffer, buf_size); 

      if (!new_buffer) 
      { 
       free(buffer); 
       return -1; 
      } 

      buffer = new_buffer; 
     } 

     buffer[in_buf] = ch; 
     ++in_buf; 
    } 
    while (true); 

    // if the line was terminated by "\r\n", ignore the 
    // "\r". the "\n" is not in the buffer 
    if ((in_buf > 0) && (buffer[in_buf-1] == '\r')) 
     --in_buf; 

    // is more memory needed? 
    if ((buf_size == 0) || (in_buf == buf_size)) 
    { 
     ++buf_size; 
     new_buffer = realloc(buffer, buf_size); 

     if (!new_buffer) 
     { 
      free(buffer); 
      return -1; 
     } 

     buffer = new_buffer; 
    } 

    // add a null terminator 
    buffer[in_buf] = '\0'; 

    *out = buffer; // complete line 

    return in_buf; // number of chars in the line, not counting the line break and null terminator 
} 

int sockfd = socket(...);   
//....   
char* tbuf;   
int ret; 

// keep reading until end of headers is detected. 
// headers are terminated by a 0-length line 
do 
{ 
    // read a single line 
    ret = sgetline(sockfd, &tbuf); 
    if (ret < 0) 
     break; // error/disconnect 

    // is it a 0-length line? 
    if (ret == 0) 
    { 
     printf("End of Headers detected.\n");   
     free(tbuf); 
     break; 
    } 

    // tbuf contains a header line, use as needed... 

    free(tbuf); 
} 
while (true); 
+0

Выглядит так, как будто бы первое появление «\ r \ n» ломается, а не первое появление «\ r \ n \ r \ n». Если мне что-то не хватает, то это слишком умно для отсутствия комментариев. – DRVic

+0

Функция считывает только 1 строку и 1 строку. Он должен выйти, когда он встречает символ '\ n', и поддерживает как разрывы строк' \ r \ n', так и '\ n' (HTTP говорит, что использовать' \ r \ n' только, но некоторые третьи- сторонние реализации используют '\ n' вместо). Заголовки HTTP заканчиваются пустой строкой. Функция возвращает количество символов в возвращенной строке, не считая разрыва строки. Код вызывает функцию в цикле до тех пор, пока не встретится линия с длиной 0 строк. –

+0

Я добавил комментарии. Вы не проверяете '\ r \ n \ r \ n' вместе, вы проверяете строку, которая является просто' \ r \ n' сама по себе. –

3

Это не нормально, чтобы прочитать один байт в то время, потому что вы делаете слишком много системных вызовов - лучше использовать буфер, прочитайте кусок и проверьте, есть ли у вас \ n. После получения строки остальная часть прочитанных байтов остается в буфере, поэтому вы не можете смешивать read/recv с read_line. Другая версия чтения n байтов с использованием такого типа буфера может быть записана ...

Моя версия, чтобы прочитать строку и небольшой пример ее использования.

#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] == '\n') 
      break; 
    } 
    if(i == size) { 
     fprintf(stderr, "line too large: %d %d\n", 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\n", 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\r\nHost: %s\r\nConnection: close\r\n\r\n", host); 
    write(cbuf->fd, buf, strlen(buf)); 
    while(read_line(cbuf, buf, sizeof(buf)) > 0) { 
     // if it's an empty \r\n on a line, header ends // 
     if(buf[0]=='\r' && buf[1] == '\n') { 
      printf("------------------------\n"); 
     } 
     printf("[%s]", buf); 
    } 
    close(cbuf->fd); 
    free(cbuf); 
    return 0; 
} 
+0

Обратите внимание, что очень часто читать один байт за раз – JSON

Смежные вопросы