类muduo学习收获篇2:tcp的一些硬菜
nc:
netcat 简称 nc,安全界叫它瑞士军刀。ncat 也会顺便介绍,弥补了 nc 的不足,被叫做 21 世纪的瑞士军刀。nc 的基本功能如下:
telnet / 获取系统 banner 信息
传输文本信息
传输文件和目录
加密传输文件
端口扫描
远程控制 / 正方向 shell
流媒体服务器
远程克隆硬盘
参数如下
参数 |
说明 |
-C |
类似-L选项,一直不断连接[1.13版本新加的功能] |
-d |
后台执行 |
-e prog |
程序重定向,一旦连接,就执行 [危险!!] |
-g gateway |
源路由跳数,最大值为8(source-routing hop point[s],up to 8) |
-G num |
源路由指针:4,8,12,... (source-routing pointer: 4,8,12,...) |
-h |
帮助信息 |
-i secs |
延时的间隔 |
-l |
监听模式,用于入站连接 |
-n |
指定数字的IP地址,不能用hostname |
-o file |
记录16进制的传输 |
-p port |
本地端口号 |
-r |
任意指定本地及远程端口 |
-s addr |
本地源地址 |
-u |
UDP模式,[netcat-1.15可以:远程nc -ulp port -e cmd.exe,本地nc -u ip port连接,得到一个shell.] |
-v |
详细输出——用两个-v可得到更详细的内容 |
-w secs |
指定超时的时间 |
-z |
将输入输出关掉——用于扫描时 |
用法例子:
https://www.oschina.net/translate/linux-netcat-command
1.端口扫描
2.Chat Server
3.文件传输
4.目录传输
5.加密你通过网络发送的数据
6.流视频
7.克隆一个设备
8.打开一个shell
tcp连接的正确关闭方式:
tcp数据发送不完整的情况:
当服务端send()发送完数据就立即close fd,若输入缓冲区内有数据,则close()会触发协议发送RST给客户端,导致客户端过早地断开连接,从而客户端接收到的数据不完整(服务端开一个while,每次读一个文件8k再send给客户端,最后发送的若干8k数据在客户端关闭后就送不到了)
第一次发送文件时,客户端不往服务端写数据,则客户端收到的文件大小是正常的1235399k字节
第二次发送文件时,客户端往服务端写1234567890这串数字,导致了客户端收到RST,提早关闭了连接,只收到1212416k
字节的数据,差了22983k字节。
正确的做法:
服务端send完数据后只关闭写端,保留读端(半关闭状态)->客户端读完了数据(read=0)且无数据要发送 就close -> 服务端read=0 从而close。
注意:若出现客户端恶意或者有bug 一直不close连接,那么服务端可能会一直阻塞或者返回ERRNO=EAGAIN,不满足read=0的情况,就不会close,因此一般要加上一个超时时间,在关闭写端后若干秒内没有收到read=0也强行关闭连接。
对于TCP non-blocking socket, recv返回值== -1,但是errno == EAGAIN, 此时表示在执行recv时相应的socket buffer中没有数据,应该继续recv。
见另一篇文章的 优雅关闭连接
服务器tcp端口设置三连:
tcp NoDelay :关闭tcp的 Nagle算法
ignore sigpipe: 防止某个客户端意外断开连接时,服务器还在往他写数据,会收到RST和sigpipe,导致服务器进程退出的bug
当服务器close一个连接时,若client端接着发数据。根据TCP协议的规定,会收到一个RST响应,client再往这个服务器发送数据时,系统会发出一个SIGPIPE信号给进程,告诉进程这个连接已经断开了,不要再写了。
socket reuseaddr :打开端口复用:目的是在服务器挂掉后能够立即重启,监听跟刚才同一个端口,否则会显示端口正在被监听,一般要等2分钟才能再次监听
为什么IO多路复用要搭配非阻塞IO
- select、poll_wait、epoll_wait返回可读≠read去读的时候能读到(因为select和read是两个独立的系统调用)。如果不用非阻塞,程序会永远卡在read上。以上情况可能出现在多进程同时监听一个socket,只有一个进程可以accept,别的都会block(惊群效应)。
- 假如socket的读缓冲区已经有足够多的数据,需要read多次才能读完,如果是非阻塞可以在循环里读取,不用担心阻塞在read上,等到errno被置为EWOULDBLOCK的时候break,安全返回select。但如果是阻塞IO,只敢读取一次,因为如果读取没有数据的fd,read会阻塞,无法返回select,这样就只能期待着多次从select返回,每次只读一次,效率低下。而且,如果是ET模式,还会造成数据无人处理,导致饥饿。
为什么non-blocking网络编程中应用层buffer是必须的
首先,multiplex的核心思想是——用一个线程去同时对多个socket连接服务,(传统的模式是一个thread/process只对一个connection服务),而想要做到这一点,thread/process就不能阻塞在某一个socket的read或write上,就要用到非阻塞IO,原因见上。(应该阻塞在epoll_wait的调用上)
那么现在假设你要向一个socket发送100kb的数据,但是write调用中,操作系统只接受了80kb的数据,原因可能是受制于TCP的流量控制等等,现在你有两个选择 :
1. 等——你可以while这个write调用,但你不知道要等多久,这取决于对方什么时候收到之前的报文并且滑动窗口,而且这样也浪费了处理别的socket的时间。
2. 把剩下的20kb存起来,下次再发,具体一点就是把这20kb保存在这个TCPconnection的output buffer里,并且注册POLLOUT事件,这样select下次返回的时候就还会来发送这20kb的数据,也不会影响别的socket的监听。
- 若20kb发送之前,又有数据要write,则应该append到缓冲区尾部,否则可能造成乱码
- POLLOUT可写事件到来是由操作系统的发送缓冲区有空触发的,同理EPOLLIN事件是操作系统接收缓冲区有数据触发的
- 若缓冲区为empty,则应该停止关心POLLOUT事件,否则可能会busy loop(但是epoll的ET模式下不会再次提醒,就没有这个问题)
至于为什么需要inputbuffer, 那是因为TCP是一个没有边界的字节流协议,不可能一个数据报就是一个请求。
非阻塞IO这里涉及到一个问题,考虑这样一个简单场景:
1. 有一个echo服务器,他的任务就是简单的将客户端发来的数据存到机子的缓冲区里,等到read=0的时候就将缓冲区的数据write回客户端。 现在有一个恶意客户端,往服务器不停地写写写,这样服务器的缓冲区就会一直变大(无限增大或者到达上限后开始溢出丢包),导致服务器程序占用的内存不断增大。这时候应该在程序中设置一个缓冲区大小上限,达到上限时就停止收包,赶紧清空缓冲区。
2. 有个代理服务器,他和服务器之间的带宽是1Gb, 客户端通过普通家用宽带(假设8Mb)像代理服务器请求一个大文件,那么代理服务器很快就从服务器里拿到了整个文件,但是他发给客户端的速度太慢了,出现这种情况:
应该要设置一个高水位和低水位,高于高水位时停止发送,低于低水位时快速发送