网络通信基础细节记录(持续更新)

1, 利用iptables在OUTPUT方向丢弃特定端口上的报文之后,再使用tcpdump是无法抓取该端口出方向的包的。原因是libpcap是从网卡驱动中获取报文的,而这些报文在没有送到网卡驱动之前,就已经在协议栈中被netfilter丢包了。

2, connect()调用block模式下,当发送的syn报文完全丢包时,超时等待的时间是很长的。UNP中对此的说明是"a TCP connect can take a long time to time out (typically 75 seconds)",在不同系统中实测结果有21s(windows10 build1702) 、63s(Linux cubieboard2 3.4.79)和127s(Linux ubuntu 4.4.0-87-generic)。因而默认的超时时长是完全不适用的,实际必须额外在应用层主动控制超时(比如使用poll())。并且在Eventloop中严禁使用此类长时间阻塞的调用,否则对整体的IO处理造成致命的性能影响。

3, windows10 build1702下发起non-blocking模式的connect()之后,再WSAPoll()检查socket是否可写(POLLOUT),若链接失败(无论发送的SYN报文丢失,还是对端直接返回RST),实际上始终无任何事件返回。因而在windows下使用WSAPoll()检查connect()结果,超时参数一定不能为-1,否则WSAPoll()调用将持续阻塞,结果还不如blocking模式的connect()。windows此处的实现还是比较坑的。
而类似在linux系统下(测试过Linux cubieboard2 3.4.79、Linux ubuntu 4.4.0-87-generic)发起non-blocking模式的connect()之后,再poll()检查fd是否可写(POLLOUT),若链接失败(无论发送的SYN报文丢失而超时,还是对端直接返回RST),返回的事件为 (POLLHUP | POLLERR | POLLOUT),poll()调用不会始终阻塞。

4,准确的获取UDP收包时的本地地址:若UDP socket是bind到 INADDR_ANY 上,那么收包之后,直接调用 getsockname() 是返回 INADDR_ANY 的地址,无法确定是收包网口的IP。此时可以先临时的connect()到所收报文的地址(即recvfrom()返回的地址)上,让socket句柄处于connected状态,再调用 getsockname() 即可返回刚才报文收包网口地址。之后再将socket句柄connect到sin_family/sin6_family为AF_UNSPEC的地址上,恢复到原来的未连接状态即可。这样做副作用是,当短暂处于connected状态时,该UDP socket句柄,只能接受所connect到的地址端点发来的报文,其他地址的报文在协议栈将被丢弃,因而若这段时间内,有其他地址的端点向其发包,可能会导致应用层收不到。

posted @ 2017-12-18 17:54  lanyuliuyun  阅读(285)  评论(0编辑  收藏  举报