netcat
- netcat 除了读取 socket 描述符,还要读取 stdin 和 stdout
- 并发模型
- thread-per-connection
- IO 复用和非阻塞 IO 配合
- 如何安全的关闭 TCP 连接
- 为什么 IO复用 必须和非阻塞 IO 一起使用
netcat 基本功能
- 信号发生器:发送数据。nc > /dev/zero、类似chargen服务
- 负载:接收数据。nc > /dev/null、类似discard服务
- 通过dd产生定量数据,通过nc发送测试网络带宽,dd /dev/zero | nc、类似ttcp服务
- 两台机器之间通过nc拷贝文件,nc < file, nc > file、类似scp服务
关于使用 nc 传输文件,示例如下:
注意:- 在使用nc传输文件后,校验一下文件的完整性,例如使用md5校验。
- netcat 版本不同,某些特性可能会存在差异。例如,该版本nc以EOF为断开连接的标志,而其他版本的nc可能需要识别Ctrl-C信号才断开连接(如果是这样的话,请试一下参数-q0 参数 或 -N 参数)。
如何正确关闭一个 TCP 连接
send()/write() 成功返回只意味着内核接收了数据,并准备在某些时候发送它们。内核接收数据后,还要把数据包发送到网卡,并在网络中各个网卡遍历,最终到达远程主机。远程主机的内核确认到数据,拥有该 socket 的进程从中读取数据,此时数据才真正到达应用程序。
也就是说,当最后一次 send 函数执行完之后,并不意味着对方已经接收到完整的数据了,如果此时我们需要关闭连接,应再确认一下对方是否已经将数据接收完毕。如果贸然的使用 close 关闭连接,可能会使得数据接收不完整。
代码
- 链接:https://github.com/Anita-Mul/recipes/blob/master/tpc/bin/sender.cc
- sender
void sender(const char* filename, TcpStreamPtr stream) { FILE* fp = fopen(filename, "rb"); if (!fp) return; printf("Sleeping 10 seconds.\n"); sleep(10); printf("Start sending file %s\n", filename); char buf[8192]; size_t nr = 0; while ( (nr = fread(buf, 1, sizeof buf, fp)) > 0) { stream->sendAll(buf, nr); } fclose(fp); printf("Finish sending file %s\n", filename); // Safe close connection printf("Shutdown write and read until EOF\n"); stream->shutdownWrite(); while ( (nr = stream->receiveSome(buf, sizeof buf)) > 0) { // do nothing } printf("All done.\n"); // TcpStream destructs here, close the TCP socket. }
- 其中 21 ~ 26 行是安全关闭套接字的方法。如果我们将这几行代码注释,模拟不安全的关闭套接字程序,然后进行测试,就有可能测出数据丢失的情况。
示例1:
- 在服务器上开启 sender 服务,发送一个 1k 大小的文件【正常的接收】
- 在客户端,使用 nc 连接服务端,接收文件
示例2
- 模拟数据未读取完,服务端发送的数据为 1M bytes
- 如果我们在客户端等待时,输入一些数据,可以模拟tcp发送数据不完整的情况。 (客户端nc 有用户输入的数据,发送到服务端后 sender 没有读取,使得 sender 在close() 的时候,没有进行 FIN 分解,而是进行了 RST 分解 直接断开了连接。)