网络编程readn、writen和readline函数的编写

readn

在Linux中,read的声明为:

ssize_t read(int fd, void *buf, size_t count);

它的返回值有以下情形:

1.大于0,代表成功读取的字节数

2.等于0,代表读取到了EOF,一般是对方关闭了socket的写端或者直接close

3.小于0,出现错误。

我们编写一个readn函数,声明与read一致,但是,readn在未出错或者fd没有关闭的情况下,会读满count个字节

ssize_t readn(int fd, void *buf, size_t count)
{
    size_t nleft = count;  //剩余的字节数
    ssize_t nread; //用作返回值
    char *bufp = (char*)buf; //缓冲区的偏移量

    while(nleft > 0)
    {
        nread = read(fd, bufp, nleft);
        if(nread == -1)
        {
            if(errno == EINTR)
                continue;
            return -1; // ERROR
        }
        else if(nread == 0) //EOF
            break;

        nleft -= nread;
        bufp += nread;
    }

    return (count - nleft);
}

readn的返回值含义如下:

1.小于0,出错

2.等于0,对方关闭

3.大于0,但是小于count,对方关闭

4.count,代表读满count个字节

writen

write函数的声明如下:

ssize_t write(int fd, const void *buf, size_t count);

man手册中对write的返回值描述如下:

       On success, the number of bytes written is returned (zero indicates nothing was  writ‐
       ten).  On error, -1 is returned, and errno is set appropriately.

       If  count  is  zero and fd refers to a regular file, then write() may return a failure
       status if one of the errors below is detected.  If no errors are detected, 0  will  be
       returned  without  causing any other effect.  If count is zero and fd refers to a file
       other than a regular file, the results are not specified.

解释如下:

成功时,返回成功写入的字节数,否则返回-1,并设置相应的errno。

如果count为0,并且fd指向一个普通文件,那么当探测到错误时返回-1.如果没有错误发生,返回0,不会产生任何影响。

如果count为0,并且fd指向的不是普通文件,那么结果未定义。

我们不去追究write为0的情形。编写write如下:

ssize_t writen(int fd, const void *buf, size_t count)
{
    size_t nleft = count;
    ssize_t nwrite;
    const char *bufp = (const char*)buf;
    
    while(nleft > 0)
    {
        nwrite = write(fd, bufp, nleft);
        if(nwrite <= 0) // ERROR
        {
            if(nwrite == -1 && errno == EINTR)
                continue;
            return -1;
        }

        nleft -= nwrite;
        bufp += nwrite;
    }
    
    return count;
}

从代码中可以看出,writen要么写满count字节,要么失败

readline

在网络编程中,很多协议是基于文本行的,例如HTTP和FTP,还有telnet,他们的消息每行都是以 作为结束标志的。于是我们开发一个readline函数,声明如下:

ssize_t readline(int sockfd, void *usrbuf, size_t maxlen)

readline函数的语义是:

如果碰不到 ,那么读取maxlen-1个字节,最后一个位置补充 。

否则读取到 ,在后面加一个 。如果中间遇到EOF,直接返回0,而不是已经读取的字节数

我们先给出一种低效的实现:

ssize_t readline_slow(int fd, void *usrbuf, size_t maxlen)
{
    char *bufp = usrbuf;  //记录缓冲区当前位置
    ssize_t nread;
    size_t nleft = maxlen - 1;  //留一个位置给 ' '
    char c;
    while(nleft > 0)
    {
        if((nread = read(fd, &c, 1)) < 0)
        {
            if(errno == EINTR)
                continue;
            return -1;
        }else if(nread == 0) // EOF
        {
            break;
        }

        //普通字符
        *bufp++ = c;
        nleft--;

        if(c == '
')
            break;
    }
    *bufp = '