网络编程-read()/write()/assert()函数

序言

基于TCP的网络编程中,使用read/recv接收数据,使用write/send发送数据。sendto/recvfrom也可用于TCP数据传输


1. read函数

  • [1] 头文件

    • unistd.h
  • [2] 函数原型

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

    • 功能:从描述符fd所指向的文件中读取count个字符到buf所指向的缓存区中

    • 参数:

      • fd:文件描述符
      • buf:存放读取数据的地址
      • count:所要读取的字符
  • [3] 函数返回值:分几种情况

    • 1)从普通文件中read数据
      • 成功:返回读到的字节数
      • 已到文件尾或无数据可读:返回0
      • 出错:返回-1
      • 若在读到指定字节数之前读到文件末尾,例如,若在到达文件末端之前还有30个字节,而要求读100个字节,则read返回30,下一次再调用read时,它将返回0(文件末端)
    • 2)从设备文件中read数据

      • 以终端这一设备来讲,通常一次读一行,读到换行符返回
      • 调用read时睡眠等待,直到终端输入换行符才从read一定字符返回,剩下的数据仍保存在内核终端设备缓冲区
    • 3)read用于套接字

      • 若对方结束了连接,返回0
      • 读取过程中信号中断:如果已读取了一部分数据则返回已读取数据,没有读取返回-1且errno为EINTR
  • [4] 补充:socket read中的阻塞和非阻塞

    • 阻塞socket:read一直阻塞等待有数据到来返回,不管数据量多少返回其实际读取的字符数

    • 非阻塞socket:不管socket接收缓冲区是否有数据都会立即返回,有数据返回实际读取的字符长度,没有数据返回-1,并errno为EWOULDBLOCK或者EAGAIN (表示应该设置为阻塞的)


2. write函数

  • [1] 头文件

    • unistd.h
  • [2] 函数原型

    • ssize_t write(int fd, void *buf, size_t length);

    • 功能:把length个字节从buf所指缓冲区中写到描述符fd所指文件中

  • [3] 函数返回值

    • 成功:返回写入的字符数

    • 失败:返回-1

    • 阻塞和非阻塞返回值

      • 阻塞:与read不同(返回实际读取到的字符数),write只有在缓冲区足以放下整个buffer时才返回

      • 非阻塞:返回能够放下的字节数,之后调用则返回-1,并errno = EAGAIN或EWOULDBLOCK


3. 发送和接收缓冲区 && read/write调用阻塞

  • write成功

    • write成功返回,只是buf中的数据被复制到了kernel中的TCP发送缓冲区。至于数据什么时候被发往网络,什么时候被对方主机接收,什么时候被对方进程读取,系统调用层面不会给予任何保证和通知
  • 发送端和接收端缓存

    • 对于每个socket都有自己的发送和接收缓冲区,不管TCP还是UDP

    • send buffer:已经发送到网络的数据依然需要暂存在send buffer中,只有收到对方的ack后,kernel才从buffer中清除这一部分数据,为后续发送数据腾出空间

    • receive buffer:接收端将收到的数据暂存在receive buffer中,自动进行确认。但如果socket所在的进程不及时将数据从receive buffer中取出,最终导致receive buffer填满,由于TCP的滑动窗口拥塞控制,接收端会阻止发送端向其发送数据

  • write和read调用阻塞

    • write调用阻塞:一般来说,由于接收端进程从socket读数据的速度跟不上发送端进程向socket写数据的速度,最终导致发送端write调用阻塞

    • read调用阻塞:从socket的receive buffer拷贝数据到应用程序的buffer中,read调用阻塞,通常是发送端的数据没有到达


4. assert()函数

  • [1] 头文件

    • assert.h
  • [2] 实现

    • 在C标准库中,assert是用来实现的,而不是函数

    • 表达式:int assert(int expression);

    • 功能:先计算表达式 expression ,如果其值为假为0,那么它先向stderr打印一条出错信息,然后通过调用 abort() 来终止程序运行

  • [3] 用法总结

    • 在函数开始时检验传入参数的合理性
    int resetBufferSize(int nNewSize)
    {
      assert(nNewSize >= 0);
      assert(nNewSize <= MAX_BUFFER_SIZE);
      ...
    }
    • 一次只检验一个条件,如果检验多个,断言失败无法直观判断是哪个条件出错

    • 不能使用改变环境的语句

      assert(i++ < 100)
      先断言i是否小于100,再++

      如果出错,比如i在执行之前就为100,那么i++这条命令就没有执行

  • [4] assert的缺点及避免

    • 频繁调用会极大影响程序的性能,增加额外的开销

    • 避免:在调试结束后,在assert.h头文件之前添加 #define NDEBUG 来禁止 assert 调用

#include 

#define NDEBUG
#include 



参考文章
http://www.cnblogs.com/xiehongfeng100/p/4619451.html
http://www.cnblogs.com/Dscxy/p/4103833.html
http://www.cnblogs.com/ggzss/archive/2011/08/18/2145017.html

2017.11.03

你可能感兴趣的:(网络编程)