Linux C/C++网络编程实战-陈硕-笔记18-第一个 Netcat 的实现

基于多线程阻塞 IO 实现的 netcat

Linux C/C++网络编程实战-陈硕-笔记18-第一个 Netcat 的实现_第1张图片

  • Thread-per-connection 适用于连接数目不太多,或者线程非常廉价的情况。

  • 使用多线程的方式来实现 netcat, 一个连接需要对应两个线程去处理,每个线程负责连接上的一个方向,即读 或 写。

代码

  • 下面程序中,主线程负责 从标准输入,写到 TCP Socket,另一个线程负责 从 TCP Socket 读, 写到标准输出。

    源码
    https://github.com/Anita-Mul/recipes/blob/master/tpc/bin/netcat.cc

  • netcat

    #include "Acceptor.h"
    #include "InetAddress.h"
    #include "TcpStream.h"
    
    #include 
    
    #include 
    #include 
    
    int write_n(int fd, const void* buf, int length)
    {
      int written = 0;
      while (written < length)
      {
        int nw = ::write(fd, static_cast<const char*>(buf) + written, length - written);
        if (nw > 0)
        {
          written += nw;
        }
        else if (nw == 0)
        {
          break;  // EOF
        }
        else if (errno != EINTR)
        {
          perror("write");
          break;
        }
      }
      return written;
    }
    
    void run(TcpStreamPtr stream)
    {
      // Caution: a bad example for closing connection
      // 读socket,写stdout
      std::thread thr([&stream] () {
        char buf[8192];
        int nr = 0;
        // 接收数据,recv返回0,则代表对端关闭。因此跳出循环,关闭连接
        while ( (nr = stream->receiveSome(buf, sizeof(buf))) > 0)
        {
          int nw = write_n(STDOUT_FILENO, buf, nr);
          if (nw < nr)
          {
            break;
          }
        }
        /* 这里采用exit的方式暴力的结束程序。因为,检测到对端半关闭连接时,
        * 本端主线程可能正处于read(stdin) 的阻塞状态,为了让主线程这边
        * 也能够退出,因此采用了exit 的方案。
        */
        ::exit(0);  // should somehow notify main thread instead
      });
    
      // 主线程:读stdin,写socket
      char buf[8192];
      int nr = 0;
      // 阻塞IO,这里read阻塞,直至读取到数据。
      // 退出条件为,读stdin时,返回0.则主线程关闭套接字写段,发送FIN
      while ( (nr = ::read(STDIN_FILENO, buf, sizeof(buf))) > 0)
      {	// 读取到数据后,马上发送到socket上
        int nw = stream->sendAll(buf, nr);
        if (nw < nr)
        {
          break;
        }
      }
      // 关闭写段,发送FIN
      stream->shutdownWrite();
      thr.join();
    }
    
    
    int main(int argc, const char* argv[])
    {
      if (argc < 3)
      {
        printf("Usage:\n  %s hostname port\n  %s -l port\n", argv[0], argv[0]);
        return 0;
      }
    
      int port = atoi(argv[2]);
      if (strcmp(argv[1], "-l") == 0)
      {
        std::unique_ptr<Acceptor> acceptor(new Acceptor(InetAddress(port)));
        TcpStreamPtr stream(acceptor->accept());
        if (stream)
        {
          acceptor.reset();  // stop listening
          run(std::move(stream));
        }
        else
        {
          perror("accept");
        }
      }
      else
      {
        InetAddress addr;
        const char* hostname = argv[1];
        if (InetAddress::resolve(hostname, port, &addr))
        {
          TcpStreamPtr stream(TcpStream::connect(addr));
          if (stream)
          {
            run(std::move(stream));
          }
          else
          {
            printf("Unable to connect %s\n", addr.toIpPort().c_str());
            perror("");
          }
        }
        else
        {
          printf("Unable to resolve %s\n", hostname);
        }
      }
    }
    

你可能感兴趣的:(Linux,linux,网络,c++)