linux c:这个代码有什么问题?行读取和缓冲读取

PYPlus 发布于 2013/07/28 10:54
阅读 528
收藏 0

我在做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)

加载中
0
Zirconi
Zirconi
UNP里面的这个readline确实没看太懂,所以我试验用的是CSAPP里面写的RIO
0
PYPlus
PYPlus

引用来自“Zirconi”的答案

UNP里面的这个readline确实没看太懂,所以我试验用的是CSAPP里面写的RIO
我现在碰到的问题是按行读取会有问题,但是HTTP协议是基于文本行的。所以我想问的是什么原因造成按行读取部分文件是会损坏
0
PYPlus
PYPlus
谁把我题目給改了
Zirconi
Zirconi
仔细看了一下问题,似乎没看太懂?意思是损坏的文件SHA1值和好的是一样的?确定?
0
PYPlus
PYPlus
回复 @Zirconi : 你再仔细看下,我说的是文件副本之间的sha1值是一样的,那当然说的是对一个文件进行多次拷贝生成的副本 和web上传上来的损坏的副本sha1值是一样的。web服务器是我用c语言写的。目前需要添加上传文件功能
0
中山野鬼
中山野鬼

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

返回顶部
顶部