什么时候会read到EOF?

本文深入探讨了TCP连接中使用close和shutdown函数的区别、作用及影响,并详细解释了SO_LINGER选项、引用计数机制、以及在多进程环境下它们的相互作用。通过实例分析,阐述了关闭TCP连接时的不同策略及其对网络通信的影响。
收到FIN报文时,read()会读到EOF.

没有设置SO_LINGER的情况下,close(sock)、shutdown(sock,SHUT_WR)或shutdown(sock,SHUT_RDWR)会导致FIN发送。


http://bbs.chinaunix.net/thread-1786785-1-1.html


延展阅读:


更多关于close和shutdown的说明:
1. 如果有多个进程共享一个套接字,close每被调用一次,计数减1,直到计数为0时,也就是所用进程都调用了close,套接字将被释放。
2. 在多进程中如果一个进程中shutdown(sfd, SHUT_RDWR)后其它的进程将无法进行通信. 如果一个进程close(sfd)将不会影响到其它进程. 得自己理解引用计数的用法了. 有Kernel编程知识的更好理解了.
3. 只要TCP栈的读缓冲里还有未读取(read)数据,则调用close时会直接向对端发送RST。
4. shutdown与socket描述符没有关系,即使调用shutdown(fd, SHUT_RDWR)也不会关闭fd,最终还需close(fd)。
5. 可以认为shutdown(fd, SHUT_RD)是空操作,因为shutdown后还可以继续从该socket读取数据,这点也许还需要进一步证实。
6. 在已发送FIN包后write该socket描述符会引发EPIPE/SIGPIPE。
7. 当有多个socket描述符指向同一socket对象时,调用close时首先会递减该对象的引用计数,计数为0时才会发送FIN包结束TCP连接。shutdown不同,只要以SHUT_WR/SHUT_RDWR方式调用即发送FIN包。
8. SO_LINGER与close,当SO_LINGER选项开启但超时值为0时,调用close直接发送RST(这样可以避免进入TIME_WAIT状态,但破坏了TCP协议的正常工作方式),SO_LINGER对shutdown无影响。
9. TCP连接上出现RST与随后可能的TIME_WAIT状态没有直接关系,主动发FIN包方必然会进入TIME_WAIT状态,除非不发送FIN而直接以发送RST结束连接。
参见:


`libevent` 中的 `read callback` 是在 **有可读数据到达时触发** 的,而不是在无数据可读时触发。 这是基于事件驱动 I/O 模型的基本机制:当底层 socket 或文件描述符上有新数据可读时,操作系统通过 I/O 多路复用(如 `epoll`, `kqueue`, `select` 等)通知 libevent,libevent 随即调用你注册的 `read callback` 函数。 --- ## ✅ 触发 read callback 的条件 | 条件 | 是否触发 read callback | |------|------------------------| | socket 上有新数据到达 | ✅ 是 | | 数据被部分读取,缓冲区中仍有剩余数据 | ✅ 是(如果设置了 EV_READ 并未禁用) | | TCP 连接关闭(收到 FIN) | ❌ 否,但会触发 eventcb 中的 BEV_EVENT_EOF | | 初始连接建立后没有数据 | ❌ 否 | --- ## 📌 原理说明 当你调用: ```c bufferevent_enable(bev, EV_READ); ``` libevent 会监听该 `bufferevent` 对应 socket 的可读事件。一旦系统检测到该 socket 可读(即接收缓冲区中有数据),就会调用你设置的 `read_callback` 函数。 你可以在这个回调中使用 `bufferevent_get_input()` 获取当前输入缓冲区的数据,并对其进行处理(比如解析协议、转发等)。 --- ## 🔧 示例代码 下面是一个完整的示例,演示了 `read callback` 的行为: ```c #include <event2/bufferevent.h> #include <event2/event.h> #include <event2/buffer.h> #include <stdio.h> // Read callback 函数 void read_callback(struct bufferevent *bev, void *ctx) { struct evbuffer *input = bufferevent_get_input(bev); struct evbuffer *output = bufferevent_get_output(bev); char buf[1024]; // 从输入缓冲区读取数据并打印 size_t n = evbuffer_remove(input, buf, sizeof(buf)); printf("Read %zu bytes: %.*s\n", n, (int)n, buf); // 将数据写入输出缓冲区(实现回声服务器) evbuffer_add(output, buf, n); } // Event callback 函数(用于处理连接关闭、错误等) void event_callback(struct bufferevent *bev, short events, void *ctx) { if (events & BEV_EVENT_EOF) { printf("Connection closed.\n"); } else if (events & BEV_EVENT_ERROR) { perror("Socket error"); } bufferevent_free(bev); } int main() { struct event_base *base = event_base_new(); struct bufferevent *bev = bufferevent_socket_new(base, -1, BEV_OPT_CLOSE_ON_FREE); // 设置回调函数 bufferevent_setcb(bev, read_callback, NULL, event_callback, NULL); bufferevent_enable(bev, EV_READ); // 启用读事件 // 模拟一个客户端连接,实际中需要 accept 或 connect printf("Starting event loop...\n"); event_base_dispatch(base); // 开始事件循环 return 0; } ``` --- ## 🧠 解释 - `read_callback()` 在每次 socket 有数据可读时被调用。 - `evbuffer_remove()` 从输入缓冲区取出数据,如果你没有完全取空,下一次仍然可能再次触发 `read_callback`。 - 如果你想控制触发频率,可以结合: - `bufferevent_disable(bev, EV_READ)` 手动暂停读事件; - 使用水位线控制触发时机。 --- ## ⚙️ 控制 read callback 行为的方法 ### 1. 设置高低水位线(Watermark) ```c bufferevent_setwatermark(bev, EV_READ, 1024, 0); // 至少收到 1KB 数据才触发 read callback ``` ### 2. 手动启用/禁用读事件 ```c bufferevent_disable(bev, EV_READ); // 暂停监听读事件 bufferevent_enable(bev, EV_READ); // 恢复监听读事件 ``` --- ## ✅ 总结 - `read callback` 是在 **socket 上有新数据可读时触发**。 - 它不是“一次性”的,只要缓冲区中还有未读完的数据,并且监听状态未被关闭,就可能再次触发。 - 可以通过设置水位线或手动启停来优化其行为。 --- ###
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值