writen和readn解决流式套接字socket上的部分读和部分写的情况

调用readn和writen的原因

在Socket编程中,调用readn和writen的主要原因是确保在网络通信过程中数据的可靠性和完整性。这是因为在网络通信中,数据传输往往是分片的,也就是说,发送方可能会将一个大的数据包分成多个小的数据包进行发送,而接收方可能需要多次接收这些小的数据包并组装成完整的数据包。

如果在接收数据时,使用传统的read函数,可能会存在以下问题:

  1. 读取的数据可能不完整,因为数据可能会被分成多个小包发送,而read函数一次只能读取一个小包。

  2. 读取的数据可能包含多个数据包,因为多个小包可能会在底层网络协议中合并成一个大的数据包,而read函数一次只能读取一个数据包。

  3. 读取的数据可能包含错误数据,因为在网络传输过程中,数据可能会出现丢失、重复、乱序等问题。

类似地,如果使用传统的write函数发送数据,也可能存在类似的问题。

为了解决这些问题,可以使用readn和writen函数。这两个函数都是自定义的函数,通常会在底层调用read和write函数,但是会进行额外的处理,确保数据的完整性和可靠性。具体而言,readn函数会反复调用read函数,直到读取到指定长度的数据;而writen函数会反复调用write函数,直到写入指定长度的数据。这样就可以避免上述问题的出现,使得数据的传输更加可靠。

writen和readn函数

ssize_t readn(int fd,void*buffer,size_t count);
ssize_t writen(int fd,void*buffer,size_t count);

readn()参数含义:
fd:表示要读取数据的文件描述符,可以是打开文件的文件描述符,也可以是网络套接字的文件描述符。
buf:表示一个缓冲区,用于存放读取的数据。
count:表示要读取的字节数。

writen()参数含义如下:
fd:表示文件描述符,指向需要写入数据的文件或者套接字。
buf:表示指向缓冲区的指针,指向需要写入的数据。
count:表示需要写入的数据长度,以字节为单位。

函数readn()和writen()的参数与read()和write()相同,但是这两个函数使用的循环来重新启用这些系统调用,确保了请求的字节数总是能够全部得到运输(除非出现错误或者在read()中检测到了文件结尾符。

write函数的返回值是:
如果返回值大于等于0,表示成功写入了指定字节数的数据。
如果返回值等于0,表示写入结束,即写入的数据长度为0。
如果返回值小于0,表示写入失败。此时,可以通过查看errno变量来获取具体的错误原因。常见的错误原因包括:EINTR(写入被信号中断)、EAGAIN(写入被阻塞)等。
read函数的返回值是:
返回一个正整数时,它表示已经成功读取了指定数量的字节。
如果返回值为0,则表示到达文件末尾或者网络连接已经关闭。
返回-1时,表示读取出错,此时可以通过errno全局变量获取错误码。常见的错误码包括EAGAIN/EWOULDBLOCK(表示读取操作会阻塞)、EINTR(表示读取操作被信号中断)、EBADF(表示文件描述符无效)、EINVAL(表示参数无效)等。

readn()和writen()的实现

//从文件描述符fd中读取n个字节的数据
ssize_t Readn(int fd,void*buffer,size_t n)
{
    ssize_t numRead;//本次读取的数据
    ssize_t totRead;//已经成功读取的数据
    char* buf;//缓冲区指针

    buf=(char*)buffer;
    //循环读取数据,直到读取到n个字节的数据
    for(totRead=0;totRead<n;)
    {
        numRead=read(fd,buf,n-totRead);//从fd中读取数据
        if(numRead==0)//如果读取到文件末尾,返回已读取的字节数
        {
            return totRead;
        }
        if(numRead==-1)//如果读取出错
        {
            if(errno==EINTR)//如果是由于中断导致的错误,继续读取
            {
                continue;
            }
            else//其他错误。返回-1
            return -1;
        }
        totRead+=numRead;//更新已读取的字节数
        buf+=numRead;//更新缓冲区指针
    }
    return totRead;//返回已读取的字节数
}

//从内存缓冲区buf中向文件描述符fd所代表的文件中写入n个字节序
ssize_t Writen(int fd, const void *buffer, size_t n)
{
    ssize_t numWritten; //本次写入的字节数
    size_t totWritten;  //记录已经成功写入的字节数
    const char *buf;
    buf = (char *)buffer; //指向缓冲区的下一个未写入的位置
    //用循环保证写入n个字节的数据
    for (totWritten = 0; sWritten < n;)
    {
        numWritten = write(fd, buf, n - totWritten);

        if (numWritten <= 0)
        { 
            if (numWritten == -1 && errno == EINTR)//为了在信号中断的情况下,能够重新尝试写入
                continue; 
            else
                return -1; 
        }
        //已经成功写入的字节数增加了numWritten
        totWritten += numWritten;
        //指针buf向后移动numWritten个字节
        buf += numWritten;
    }
    return totWritten; 
}
  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 3
    评论
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值