我在做web 上传,HTTP协议是基于文本行的。所以我原先的做法是写了一个不带缓冲的readline去每次读取HTTP数据包,效率低下的同时,部分文件上传还会损坏。之后更改为带缓冲的readline 将数据存入缓冲区之后再在缓冲区中readline。发现还是会有同样的问题。于是我测试了用带缓冲和不带缓冲的readline在本机上进行拷贝文件测试发现部分文件损坏,但文件副本之间的sha1值都相同。(部分文件损坏是指通过web上传会损坏的本地拷贝照样会损坏且哈希值是一样的)。之后用readn函数即从文件描述符中读取n个字符的函数发现拷贝是成功的。为什么会这样?
我之后测试了《unix网络编程》提供的带缓冲和不带缓冲的readline函数在本机上测试。通过web上传会损坏的文件拷贝照样会损坏。下面的readline代码来自《unix网络编程》:下面的readline拷贝文件会损坏
static int read_cnt; static char *read_ptr; static char read_buf[MAXLINE]; static ssize_t my_read(int fd,char *ptr) { if(read_cnt <= 0) { again: if((read_cnt = read(fd,read_buf,sizeof(read_buf))) < 0) { if (errno == EINTR) goto again; return (-1); }else if(read_cnt == 0) return (0); read_ptr = read_buf; } read_cnt --; *ptr = *read_ptr ++; return 1; } ssize_t r_readline(int fd,void *vptr,size_t maxlen) { ssize_t n,rc; char c,*ptr; ptr = vptr; for ( n = 1;n < maxlen;n++) { if((rc = my_read(fd,&c)) == 1) { *ptr ++ = c; if (c == '\n') break; } else if(rc == 0) { *ptr =0; return (n-1); } else return (-1); } *ptr = 0; return (n); }
readn函数
ssize_t readn(int fd,void *vptr,size_t n) { size_t nleft; ssize_t nread; char *ptr; ptr = vptr; nleft = n; while(nleft > 0) { if ((nread = read(fd,ptr,nleft)) < 0) { if (errno == EINTR) { nread = 0; } else return -1; } else if(nread ==0) break; nleft -= nread; ptr += nread; } return (n - nleft); }
谁能解释下为什么readline会失败?HTTP协议分析必须readline,真让人纠结 @中山野鬼 @osc所有人
环境:Linux pyplus-PC.lan 3.9.6-200.fc18.x86_64 #1 SMP Thu Jun 13 18:56:55 UTC 2013 x86_64 x86_64 x86_64 GNU/Linux
gcc:gcc (GCC) 4.7.2 20121109 (Red Hat 4.7.2-8)
UNP里面的这个readline确实没看太懂,所以我试验用的是CSAPP里面写的RIO######
my_read 不要这么读。而是一次读取尽可能多的数据。如果一定要行才结束,就检测新读的数据是否存在\n。
你这样读,my_read数据,如果在已经读了一些数据后,而出现errno,会导致你前面读取的数据丢弃。核心的原因是,你一次r_readline会触发多次my_read,而当最后一次my_read出现-1的情况,会把你前面几次正确读的数据给丢弃掉。
你的代码有几个地方书写有问题。
我不知道你这个代码是抄的还是自己写,自己写的都还好,抄的就有点郁闷了。
另外最近我在写书中,也反复提到goto的用法,主要是鼓励用,但上面的情况不该用。下面我给个随手写的代码,没测试过。你可以试一下,至少逻辑主线按照你的思路,有点小区别。
static int read_cnt = 0; static read_buf[MAXLINE]; static char *read_ptr = read_buf; static int get_read_buf(int fd){ int er = 0; read_ptr = read_buf; while ((read_cnt = read(fd,read_buf,sizeof(read_buf)) < 0){ er = errno; if (er != EINTR) { return 1; } } return 0 ; } static ssize_t my_read(int fd,char *ptr){ if (read_cnt <= 0){ if (get_read_buf(fd)){ return 0; } } if (read_cnt > 0){ *ptr = *read_ptr ++; read_cnt--; return 1; } return 0; } ssizt_t r_readline(int fd ,void *vptr,size_t maxlen){ int i = 0; while (my_read(fd,vptr+i)){ if (vptr[i] == '\n'){ i++; break; } i++; if (i >= maxlen - 1) break; } ptr[i] = 0; return i-1; }
你对比下中间不一样的地方。 另外,如果你非要用char c这个空间的话,建议你如下申请和使用。
char c[1];
myread(fd,c);
好处多多,比较容易把各种新手的错误浮现出来。
另外这年头不流行*p++这种写法了。。上面保留了一个,我自己写,绝对不会这么做。
另外补充,好的代码殊路同归,差的代码千奇百怪。说句狂的话,我3分钟看不懂的代码逻辑,基本是shit。即便linux内核,任何一个函数内部的逻辑你是容易看懂的,看不懂的是数据的来龙去脉和整体的目的性,但那些不属于代码逻辑。
版权声明:本文内容由阿里云实名注册用户自发贡献,版权归原作者所有,阿里云开发者社区不拥有其著作权,亦不承担相应法律责任。具体规则请查看《阿里云开发者社区用户服务协议》和《阿里云开发者社区知识产权保护指引》。如果您发现本社区中有涉嫌抄袭的内容,填写侵权投诉表单进行举报,一经查实,本社区将立刻删除涉嫌侵权内容。