我在做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内核,任何一个函数内部的逻辑你是容易看懂的,看不懂的是数据的来龙去脉和整体的目的性,但那些不属于代码逻辑。