17-如何正确使用TCP

从本节开始,实现一个简单的netcat(网络工具中的瑞士军刀)

 

why netcat?

image-20230212175325452

 

netcat 基本功能简介

  • 信号发生器:发送数据。nc > /dev/zero、类似chargen服务

  • 负载:接收数据。nc > /dev/null、类似discard服务

  • 通过dd产生定量数据,通过nc发送测试网络带宽,dd /dev/zero | nc、类似ttcp服务

  • 两台机器之间通过nc拷贝文件,nc < file, nc > file、类似scp服务

 

how/when 关闭TCP连接

  • 为什么出现TCP不可靠的错觉

    • 发送所有数据,紧接着close,最后的一些字节丢失

  • 出现错误的情况:send() + close()

    • 当发送缓冲区还有数据,而调用close()会发送RST,强行使得TCP连接断开

    • SO_LINGER无助于解决这一问题,不要使用SO_LINGER(???)

  • sender关闭连接的正确步骤:send() + shutdown(WR) + read->0 + close()

    • 这里的shutdown(WR)会导致receiver的read返回0

  • receiver关闭连接的正确步骤:read()->0 + 如果没有数据要发送就调用close()

    • 这里的close(),会导致sender的read返回0

  • 或者在应用层设计更好的协议。因为此处的协议有权限(恶意客户端,一直不close,导致服务端不能关闭连接。解决方法:为每一个连接超时器,超时不活跃连接,直接close。)

 

一个传输文件的例子

代码出处:recipes/tpc/bin at master · chenshuo/recipes (github.com)

 

这个例子是为例复现TCP”不可靠“的场景。

 

打开文件(filename),然后每次读一字节数据到buf,再将buf的数据发送给reciever,直至read返回0。再调用stream->shutdownWrite(),其实就是shutdown(SHUT_WR)。然后循环read,直至read返回0,才调用close(代码中是通过TcpStream的析构来close)。

 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.
 }

 

测试步骤

  • 服务端:先在将服务端运行起来:将ttcp文件从端口1234发出

image-20230212183145167

  • 客户端:在同一台主机上运行netcat当作客户端,并通过wc计算接收数据的大小,无论客户端期间是否发送数据,接收的数据都与原来服务端发送的文件的大小一致。

image-20230212183302769

 

  • 下面复现错误,将sender函数的重复read直至返回0注释,也即是将安全关闭连接的操作去除。

image-20230212183623813

  • 重新编译,按照上面的步骤运行。服务端:

image-20230212185026358

  • 客户端:可见第二次的大小与原来文件的大小不一致。

image-20230212185321458

原因:

服务端 sender 将文件发送完之后,close(fd)关闭连接。而由于客户端通过nc发送的数据还没有读,因此会发送RST报文丢弃数据立刻终止连接,而不是发送FIN报文等待所有数据都发送完才正常断开连接(发送RST,还是FIN 取决于close()时,local tcp buffer中是否还有未读完的数据)。

在客户端收到 RST 包时,丢弃所有数据,然后立刻断开连接。

 

参考链接:(224条消息) 【网络编程实践】2.3.2 TCP连接错误关闭示例_我叫RT的博客-CSDN博客

posted @ 2023-04-29 15:27  DavidJIAN  阅读(31)  评论(0)    收藏  举报