类muduo学习收获篇2:tcp的一些硬菜

nc:

netcat 简称 nc,安全界叫它瑞士军刀。ncat 也会顺便介绍,弥补了 nc 的不足,被叫做 21 世纪的瑞士军刀。nc 的基本功能如下:

telnet / 获取系统 banner 信息
传输文本信息
传输文件和目录
加密传输文件
端口扫描
远程控制 / 正方向 shell
流媒体服务器
远程克隆硬盘

参数如下
类muduo学习收获篇2:tcp的一些硬菜

参数

说明

-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数据在客户端关闭后就送不到了)

类muduo学习收获篇2:tcp的一些硬菜

第一次发送文件时,客户端不往服务端写数据,则客户端收到的文件大小是正常的1235399k字节

第二次发送文件时,客户端往服务端写1234567890这串数字,导致了客户端收到RST,提早关闭了连接,只收到1212416k

字节的数据,差了22983k字节。

 

正确的做法:

类muduo学习收获篇2:tcp的一些硬菜

服务端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)像代理服务器请求一个大文件,那么代理服务器很快就从服务器里拿到了整个文件,但是他发给客户端的速度太慢了,出现这种情况:

类muduo学习收获篇2:tcp的一些硬菜

应该要设置一个高水位和低水位,高于高水位时停止发送,低于低水位时快速发送