序言
基于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
- 1)从普通文件中read数据
[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 <stdio.h>
#define NDEBUG
#include <assert.h>
参考文章:
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
本文深入讲解了TCP网络编程中read和write函数的工作原理及其在不同场景下的应用,包括阻塞和非阻塞模式下的行为差异,并探讨了发送和接收缓冲区的概念及read/write调用的阻塞情况。
2473

被折叠的 条评论
为什么被折叠?



