从套接字缓冲区逐行读取

好吧,我需要帮助:我想写一个函数,从一个从unistd.h头的read()函数的第三个参数获得的套接字缓冲区中逐行read()

我写了这个:

 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." 。 为什么? 怎么解决这个问题? 非常感谢提高此function的技巧。 提前致谢。 🙂

你为自己制造的东西比他们需要的东西更难。 你真的不需要做strcats来获得你在当前位置添加的每次读取时读取的单个字符。

但是你的错误是例程一看到\ n就返回,所以它返回的字符串永远不会包含第一个\ n之后的任何内容。

一次读取一个字节是不行的,因为你正在进行太多的系统调用 – 更好的是使用缓冲区,读取一个块并检查你是否得到了\ n。 获取一行后,读取的其余字节仍保留在缓冲区中,因此您无法将read / recv与read_line混合使用。 使用这种缓冲区的另一个读n字节版本可以写…

我的版本读取一行,并使用它的一个小例子。

 #include  #include  #include  #include  #include  #include  #include  #include  #include  #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; } 

请尝试此实现:

 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);