EPOLLHUP/EPOLLRDHUP与read返回0的区别
文章参考:epoll触发事件的分析 、EPOLLHUP&EPOLLRDHUP 、man_page
相信很多时候,大家都是通过检测 read/recv 返回 0 来判断对端是否关闭了连接,如果返回 0,我们通常也会 close 该连接。这没有问题,但在很多场景下有个缺陷:FIN 报文和普通数据报文一样,也是需要在缓冲区中排队的,只有当 read 读取到 FIN 以后才会返回 0,而且 FIN 报文无法和数据同时被读取,也就是说,必须将数据 read 完毕后,再调用一次 read 才能读取到 FIN 并返回 0 。这也是为什么在网络读取时需要将 read 放在循环中的原因之一,不仅是为了将数据读取完整,也是为了能够读到 FIN 报文。
注意,FIN 报文虽然会排队,但当本端收到 FIN 后,内核网络栈会立刻回复 ACK,而不会管你是否 read 到这个 FIN 报文。
那么这个缺陷会引发什么问题呢?由于笔者现在也是网络编程初学者,没有太多实战经验,所以这里只提供本人猜想的两个情景:
在一个高并发网络场景下,服务器收到了对端发来的 FIN 报文(对端 close),但没有立 ...
互斥锁、条件变量与信号量的区别及其用法
参考:《操作系统导论》《操作系统之哲学原理》《Linux高性能服务器编程》
由于没有具体的应用场景,笔者之前一直对锁、条件变量和信号量感觉迷迷糊糊,总觉得它们很相似但又有所区别。这两天在写线程池时需要用到任务队列,主线程生产任务,工作线程则竞争地从队列中取出任务——也就是我们常说的“生产者/消费者问题”,接触到这个具体的场景后,笔者突然就明白了它们的区别。
互斥锁用来保证多线程/进程之间对共享资源的互斥访问,也就是保证同一时刻只有一个执行流在临界区中。
POSIX 的互斥锁操作主要有如下几个函数:
12345678910//初始化锁,将锁的各个字段都初始化为0int pthread_mutex_init(pthread_mutex_t* mutex, const pthread_mutexattr* attr);//销毁锁,释放系统资源int pthread_destroy(pthread_mutex_t* mutex);//上锁int pthread_lock(pthread_mutex_t* mutex);//解锁int pthread_unlock(pthread_mutex ...
网络字节序及其注意事项
网页上有许多关于字节序的讨论,这里就不多说了,只强调笔者认为最重要的两点:
字节序与 CPU 相关,或者说与计算机体系架构相关,而与操作系统无关。
字节序只针对多字节数据(如 int )才有意义,单字节数据(如 char)不用考虑字节序的问题。
笔者是这样理解上面两点的:
字节序就是指 CPU 中的寄存器对数据的解释方式 。32 位 CPU ,其寄存器大小为 4 字节,如果被设计为小端序,那么低地址的字节会被解释为低位数据,高地址的字节会被解释为高位数据:
从上图也能看出,当单字节数据不会被解释方式(大小端)影响,比如 0x11 仍然被解释为 0x11:
那么,如何知道自己主机的大小端呢?很简单:
12345int main() { int num = 0x11223344; char* p = # printf("%p\n",*p);}
如果输出 0x44,那么就是小端。有种常见的错误做法如下:
12345678int main(){ int i; char j; i = 0 ...
三握四挥异常分析
前置内容:全连接与半连接队列 、SYN泛洪
1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253//serverint main(){ int sock_listen = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP); int optval = 1; socklen_t optlen = sizeof(optval); setsockopt(sock_listen, SOL_SOCKET, SO_REUSEADDR, (void*)&optval, optlen); struct sockaddr_in addr; bzero(&addr, sizeof(addr)); addr.sin_family = AF_INET; addr.sin_port = htons(12345); addr.sin_addr.s_addr = ...
网络学习利器—hping3
网络学习利器—hping3
参考:hping3-tutorial 、端口扫描浅析、hping3-man
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384 -c --count packet count -i --interval wait (uX for X microseconds, for example -i u1000) --fast alias for -i u10000 (10 packets for second) --faster alias for -i u1000 (100 packets for second) --flood sent packets as fast as possible. Don't show ...
“包过滤工具—iptables”
防火墙是保护服务器和基础设施安全的重要工具。在 Linux 生态系统中,iptables 是使用很广泛的防火墙工具之一,它能够用来完成封包过滤、封包重定向和网络地址转换(NAT)等功能。 iptables 基于内核的包过滤框架 netfilter 。如果管理员或用户不了解这些系统的架构,那可能就无法创建出可靠的防火墙策略。
这里我们只简单地使用 iptables 的包过滤功能(后面讨论的命令也仅限于此) ,因为它能够很方便地模拟各种网络状况(TCP 三握四挥的异常处理、报文丢失等),非常有助于我们对网络理论知识的学习。其他功能的详细介绍可以参见iptables用法规则小结 。
命令格式:
ACCEPT 允许数据包通过
DROP 直接丢弃数据包,不给任何回应信息
REJECT 拒绝数据包通过,必要时会给数据发送端一个响应的信息
注意: 即使我们在输入链 INPUT 上 DROP 了指定报文,tcpdump/wireshark 仍然可以抓到该报文,因为报文的进入顺序是:Wire -> NIC -> tcpdump -> netfilter/iptables ; ...
常用socket选项
获取和设置socket选项
使用 getsockopt() 和 setsockopt 来获取和设置 socket 的各种选项:
123456789101112131415161718int getsockopt(int socket, int level, int option_name, void *restrict option_value, socklen_t *restrict option_len);//example int bufsize = 10000; socklen_t optlen = sizeof(bufsize); getsockopt(sock_listen, SOL_SOCKET, SO_RCVBUF, &bufsize, &optlen);//============================================================================int set ...
wireshark入门笔记
学习 wireshark 前需要先了解 tcpdump,许多情况下我们使用 tcpdump 抓包(更快速、更方便),然后使用 wireshark 进行分析 。可参见 tcpdump实操
环境:Ubuntu 16.04 LTS
安装 wireshark
1# sudo apt install wireshark-qt
启动 wireshark
1# wireshark
启动后显示如下界面:
发现没有我们想要的网卡。如何知道哪块网卡是我们想要的?打开命令行终端:
123456789101112131415161718# ifconfigens33 Link encap:Ethernet HWaddr 00:0c:29:48:09:a9 inet addr:192.168.248.128 Bcast:192.168.248.255 Mask:255.255.255.0 inet6 addr: fe80::555a:9e00:3f14:e7f/64 Scope:Link UP BROADCAST RUNNING M ...
tcpdump实操
tcpdump
tcpdump man
tcpdump 须在管理员权限下运行。
12#tcpdumplistening on ens33, link-type EN10MB (Ethernet), capture size 262144 bytes
输出结果表明 tcpdump 的监听网卡为 ens33 。
默认截断大小为 262144 字节(随版本而改变),超过该数字报文会被截断。
使用 ifconfig 查看其他网卡:
123456789101112131415161718#ifconfigens33 Link encap:Ethernet HWaddr 00:0c:29:48:09:a9 inet addr:192.168.248.128 Bcast:192.168.248.255 Mask:255.255.255.0 inet6 addr: fe80::555a:9e00:3f14:e7f/64 Scope:Link UP BROADCAST RUNNING MULTICAST MTU:1500 ...
高级I/O函数&网络数据读取的常见问题
参考文章:《UNIX网络编程》《UNIX环境高级编程》《TCP/IP编程》、socket阻塞模式
read
ssize_t 是有符号整型 long;size_t 是无符号整型 unsigned long
对于 read 和 write 这类 低速系统调用(即可能使进程永远阻塞下去的系统调用) ,当执行期间捕捉到信号,则该系统调用就会被中断不再执行,且返回 -1,并将 errno 设置为 -1。为什么呢?有这样一个常见的场景:网络中,由于不知道对端网络具体会发来多大的包,所以我们会让 read 尽可能多地接收数据,即把其第三个参数设置大些。那么问题来了,对端只发送了 1000 字节的数据,而你却指定本端的 read 读取 10000 字节,那怎么办?总不能一直阻塞下去吧?所以此时就需要用到信号,来中断 read 使其结束。
至于 read 在读取数据时是被什么信号中断的,笔者也没有查到相关资料,知晓的读者还请在评论区指点一二。
需要注意的是,TCP 套接字上的 IO 表现的行为不同于通常的磁盘 IO。 磁盘 IO 一般不会被信号打断而终止,因为磁盘 IO 与网络 IO 的区 ...