Java-Day-28( 网络相关概念 + InetAddress 类 + Socket + TCP 网络通信编程 + netstat 指令 )

发布时间 2023-06-09 00:08:30作者: 朱呀朱~

Java-Day-28

网络相关概念

网络通信

  • 两台设备之间通过网络实现数据传输 ( 将数据通过网络从一台设备传输到另一台设备 )
  • java.net 包下提供了一系列的类或接口,供程序员使用,完成网络通信

网络

  • 两台或多台设备通过一定物理设备连接起来构成了网络
  • 根据网络的覆盖范围不同,对网络进行分类:
    • 局域网:覆盖范围最小,仅仅覆盖一个教室或一个机房
    • 城域网:覆盖范围较大,可以覆盖一个城市
    • 广域网:覆盖范围最大,可以覆盖全国,甚至全球,万维网是广域网的代表

ip 地址

  • 用于唯一标识网络中的每台计算机 / 主机
  • 查看 ip 地址:ipconfig
  • ip 地址的表示形式:点分十进制
    • 如 IPV4:4 个字节 ( 乘以 8 得 32 位 ) 表示
    • 每一个十进制数的范围:0 ~255 ( 一个字节 )
  • ip 地址的组成 = 网络地址 + 主机地址,如:192.168.18.69 ( 仅 69 指主机地址 )
  • ilPv6 是互联网工程任务组设计的用于替代 IPv4 的下一代 IP 协议,其地址数量号称可以为全世界的每一粒沙子编上一个地址
    • 4 暂只考虑了主机等,但未来更多的网络设备会接入到网络中 ( 万物互联 ),如物联网,所以 4 不够用,就需要 6 来逐渐替代扩增
  • 由于 IPv4 最大的问题在于网络地址资源有限,严重制约了互联网的应用和发展。Ipv6 的使用,不仅能解决网络地址资源数量的问题,而且也解决了多种接入设备连入互联网的障碍
    • IPv6 是用 128 位表示一个地址 ( 除以 8 得 16 个字节 )
    • 例:2408 : 8207 : 1851 : 8670 : d52d : 7a14 : 362c : 4e8d ( IPv6 地址显示为 8 段 )
    • 16 ( 字节 ) 除以 8 ( 段 ) 得 2,即一段为两个字节,即 16 个 bits ( 位 )

ipv4 地址分类

image-20230606111721541

  • A 类:0 开头一个字节表示一个网络号,其余都是主机号 ( 可以引更多的主机:2 ^ 24 - 1 )
    • 0.0.0.0 ~ 127.255.255.255
  • B 类:1 0 开头两个字节表示一个网络号
    • 128.0.0.0 ~ 191.255.255.255
  • C 类:1 1 0 开头三个字节表示一个网络号
    • 192.0.0.0 ~ 233.255.255.255
  • D 类:1 1 1 0 开头
    • 224.0.0.0 ~ 239.255.255.255
  • E 类:1 1 1 1 0 开头
    • 240.0.0.0 ~ 247.255.255.255
  • 特殊的 127.0.0.1 表示本机地址

域名

  • 将 ip 地址映射成域名 ( HTTP 协议 )

  • 例:www.baidu.com

  • 为了方便记忆,解决记 ip 的困难

端口号

  • 用于标识计算机上某个特定的网络程序
    • 用 IP + 端口:找到特定服务 ( IP 指一个酒店,端口就是酒店不同规格房间,所对应的不同的房间号 )
  • 表示形式:以整数形式,端口范围 0 ~ 65535
    • 两个字节表示端口
      • 2 ^ 16 - 1 = 65536 - 1 = 65535
  • 0 ~ 1024 已经被占用 ( 开发时就不要使用了 )
    • 比如:ssh 22, ftp 21, smtp 25, http 80
  • 常见的网络程序端口号
    • tomcat:8080
    • mysql:3306
    • oracle:1521
    • sqlserver:1433

网络通信协议

  • 协议 ( tcp / ip )

    • 如 QQ 信息聊天时,A 给 B 发信息,就是发送数据,而在网络编程中数据的组织形式,就是协议
    • 然后 B 回复数据给 A,就也是按照规定好的协议方式
    • QQ 程序负责将协议的内容取出呈现出来
  • TCP / IP ( Transmission Control Protocol / Internet Protocol ),中文译名为传输控制协议 / 因特网互联协议,又名网络通讯协议,这个协议是 Internet 最基本的协议、Internet 国际互联网络的基础,简单来说,就是由网络层的 IP 协议和传输层的 TCP 协议组成的。

  • 简单一叙:

    • 用户数据 —( 应用程序 )—> 加上了 App 首部 —( TCP )—> 加上了 TCP 首部 —( IP )—> 加上了 IP 首部 —( 以太网驱动程序 )—> 加上以太网首部与尾部 ——> 打包完成进入以太网发送出
    • 接收时就是倒过来一步步拆包直到仅用户数据
  • TCP / IP 协议包括两种模型:

    • OSI 模型 ( 理论模型 ):应用层 —— 表示层 —— 会话层 —— 传输层 —— 网络层 —— 数据链路层 —— 物理层
    • TCP / IP 模型 ( 实际所用 ):应用层 —— 传输层 —— 网络层 —— 物理 + 数据链路层
      • 都与上面简单一叙有对应:应用程序 ( HTTP 等协议 ) —— TCP 层 ( TCP 协议用最多,所以有的就称 TCP 层 ) —— IP 层 ( IP 协议用最多,就叫 IP 层了 ) —— Link 协议

TCP 和 UDP

  • TCP 协议:传输控制协议 —— 慢但可靠
    • 使用 TCP 协议前,须先建立 TCP 连接,形成传输数据通道
    • 传输前,采用 " 三次握手 " 方式,是可靠的
      • A 发送给连接请求给 B
      • B 回复确认信息给 A 并向 A 也发生连接请求
      • A 收到后,回复 B 确认信息
      • ...... 发送正式信息 ......
    • TCP 协议进行通信的两个应用进程:客户端、服务端
    • 在连接种可进行大数据量的传输
    • 传输完毕,需释放已建立的连接 ( 否则无法接收或发送信息给其他 ),效率低
  • UDP 协议:—— 快但不可靠
    • 将数据、源、目的封装成数据包,不需要建立连接
    • 每个数据报的大小限制在 64 K 内
    • 因无需链接,故是不可靠的
      • A 直接通知 B 后就紧接着走了
    • 发送数据结束时无需释放资源 ( 因为不是面向连接的 ),速度快

InetAddress 类

  • 相关方法

    • 获取本机 InetAddress 对象:getLocalHost
    • 根据指定主机名 / 域名获取 ip 地址对象:getByName
    • 获取 InetAddress 对象的主机名:getHostName
    • 获取 InetAddress 对象的地址:getHostAddress
    public class Test_java {
        public static void main(String[] args) throws UnknownHostException {
    //        获取本机的 InetAddress 对象
            InetAddress localHost = InetAddress.getLocalHost();
            System.out.println("计算机名/IP —— " + localHost); // 计算机名/IP —— DESKTOP-TO5JTRS/10.111.4.162
    
    //        根据指定主机名获取 InetAddress 对象
            InetAddress host1 = InetAddress.getByName("DESKTOP-TO5JTRS");
            System.out.println("host1 = " + host1); // DESKTOP-TO5JTRS/10.111.4.162
    
    //        根据域名返回 InetAddress 对象
            InetAddress host2 = InetAddress.getByName("www.baidu.com");
            System.out.println("host2 (域名/IP) = " + host2); // host2 = www.baidu.com/110.242.68.3
    
    //        通过 InetAddress 对象,获取对应的地址
            String hostAddress = host2.getHostAddress(); // (百度的IP) 会变化
            System.out.println("host2对应的IP = " + hostAddress);
    
    //        通过 InetAddress 对象,获取对应的主机名/域名
            String hostName = host2.getHostName();
            System.out.println("host2对应的主机名/域名 = " + hostName); // www.baidu.com (这个不会变的)
        }
    }
    

Socket

  • 套接字 ( Socket ) 开发网络应用程序被广泛采用,以至于成为事实上的标准

  • 通信的两端都要有 Socket,是两台机器间通信的端点

  • 网络通信其实就是 Socket 间的通信

  • Socket 允许程序把网络连接当成一个流,数据在两个 Socket 间通过 IO 传输

  • 一般主动发起通信的应用程序属客户端,等待通信请求的为服务端

  • 两种编程方式:TCP 编程和 UDP 编程

  • 简单理解:

    • 主机 A ( 客户端 — 发起连接 ) 的 socket ——— 数据通道 ———> 主机 B ( 服务端 — 接收请求连接 ) 的 socket
      • A 调用 socket.getOutputStream 输出流写到数据通道,由 B 调用 socket.getInputStream 输入流读取通道的数据,将数据读到了程序中去
      • 读写操作反之亦可 ( in 为读进,out 为写出 )

TCP 网络通信编程

  • 基于客户端 — 服务端的网络通信

  • 底层使用的是 TCP / IP 协议

  • 应用场景举例:客户端发送数据,服务端接收并显示

  • 基于 Socket 的 TCP 编程

    • 即 Server 服务端与 Client 客户端建立连接后,便如上 " 简单理解 " 部分所述,完成相互的信息传输操作后,都勿忘了要 Socket.close() 关闭

应用案例一 ( 使用字节流 + 单向信息 ) :

  • 编写一个服务器端和一个客户端

  • 服务器端在 9999 端口监听

    • 当没有客户端连接 9999 端口时,程序会阻塞,等待连接
    • 连接后,生成一个 Socket 输入流读取数据
  • 客户端连接到服务器端,发送 " hello server, I'm Client ",然后退出

    • 连接服务端 ( ip,端口 ),连接上后,生成一个 Socket,通过输出流写入数据到数据通道
  • 服务器端接收到客户端发送的信息,输出,并退出

  • 代码编写 — 客户端:

    /**
     * 客户端
     */
    public class SocketTCP01Client {
        public static void main(String[] args) throws IOException {
    //        通过 ip/端口 连接服务端 (这里仿照有一个本地的9999接口)
            Socket socket = new Socket(InetAddress.getLocalHost(), 9999); // 因为演示是在一个电脑上,所以就获取本机,连接本机的9999端口
    //        如果连接成功就返回 Socket 对象
    //        如果是远程服务器,就写 "ip/端口"
            System.out.println("客户端 socket 返回 = " + socket.getClass());
    
    //        连接后,生成的Socket通过get方法获取与socket对象关联的输出流对象
            OutputStream outputStream = socket.getOutputStream();
    
    ////        OutputStream 是抽象类,这里的运行类型实际上是SocketOutputStream,不过向上用OutputStream接收了
    //        System.out.println("socket.getOutputStream的运行类型 :" + outputStream.getClass());
    
    //        通过输出流,写入数据到数据通道
            outputStream.write("hello server, I'm Client".getBytes());
    //        别忘了关闭流对象和socket
            outputStream.close();
            socket.close();
            System.out.println("信息已发送,客户端已退出......");
        }
    }
    
  • 代码编写 — 服务端:

    /**
     * 服务端
     */
    public class SocketTCP01Server {
        public static void main(String[] args) throws IOException {
    //        服务器端为了应对多个客户端的连接,所以用一个serverSocket来accept返回出多个socket
            ServerSocket serverSocket = new ServerSocket(9999);
    //        细节:要求在本机没有其他服务在监听9999端口
    
            System.out.println("服务端在9999端口监听,等待连接中......");
    //        当有客户端连接时,就返回一个对象:socket
            Socket socket = serverSocket.accept(); // 没有连接的话,运行就阻塞在这,不往下走了
            System.out.println("服务器端 socket = " + socket.getClass());
    
    //        通过 socket.getInputStream 读取客户端写入到数据通道的数据
            InputStream inputStream = socket.getInputStream();
    //        IO 读取 (字节流返回的都是数字,字符流返回的空判断才是用null)
            System.out.println("读取Client通过数据通道写入的数据并呈现~~");
            byte[] buf = new byte[1024];
            int readLen = 0;
            while ((readLen = inputStream.read(buf)) != -1) {
                System.out.println(new String(buf, 0, readLen)); // 根据读取到的实际长度,显示内容
            }
    //        勿忘关闭流和socket
            inputStream.close();
            socket.close();
            serverSocket.close(); // 以防资源的浪费、占用
    
        }
    }
    

应用案例二 ( 使用字节流 + 双向信息 ) :

  • 编写一个服务器端和一个客户端

  • 服务器端在 9999 端口监听

  • 客户端连接到服务器端,发送 " hello server, I'm Client ",并接收服务器端回发的 " hello client, baga ",然后再退出

  • 服务器端接收到客户端发送的信息,输出,并发送 " hello client, baga " 再退出

  • 代码编写 — 客户端:

    /**
     * 客户端
     */
    public class SocketTCP02Client {
        public static void main(String[] args) throws IOException {
    //        通过 ip/端口 连接服务端 (这里仿照有一个本地的9999接口)
            Socket socket = new Socket(InetAddress.getLocalHost(), 9999); // 因为演示是在一个电脑上,所以就获取本机,连接本机的9999端口
    //        如果连接成功就返回 Socket 对象
    //        如果是远程服务器,就写 "ip/端口"
            System.out.println("客户端 socket 返回 = " + socket.getClass());
    
    //        连接后,生成的Socket通过get方法获取与socket对象关联的输出流对象
            OutputStream outputStream = socket.getOutputStream();
    
    ////        OutputStream 是抽象类,这里的运行类型实际上是SocketOutputStream,不过向上用OutputStream接收了
    //        System.out.println("socket.getOutputStream的运行类型 :" + outputStream.getClass());
    
    //        通过输出流,写入数据到数据通道
            outputStream.write("hello server, I'm Client".getBytes());
    
    //        设置结束标记
            socket.shutdownOutput();
    
    //        获取相关联的输入流读取数据(字节)并显示
            InputStream inputStream = socket.getInputStream();
            byte[] buf = new byte[1024];
            int readLen = 0;
            while ((readLen = inputStream.read(buf)) != -1) {
                System.out.println(new String(buf, 0, readLen));
            }
    
    //        别忘了关闭流对象和socket
            outputStream.close();
            inputStream.close();
            socket.close();
            System.out.println("信息已发送,客户端已退出......");
        }
    }
    
  • 代码编写 — 服务端:

    /**
     * 服务端
     */
    public class SocketTCP02Server {
        public static void main(String[] args) throws IOException {
    //        服务器端为了应对多个客户端的连接,所以用一个serverSocket来accept返回出多个socket
            ServerSocket serverSocket = new ServerSocket(9999);
    //        细节:要求在本机没有其他服务在监听9999端口
    
            System.out.println("服务端在9999端口监听,等待连接中......");
    //        当有客户端连接时,就返回一个对象:socket
            Socket socket = serverSocket.accept(); // 没有连接的话,运行就阻塞在这,不往下走了
            System.out.println("服务器端 socket = " + socket.getClass());
    
    //        通过 socket.getInputStream 读取客户端写入到数据通道的数据
            InputStream inputStream = socket.getInputStream();
    //        IO 读取 (字节流返回的都是数字,字符流返回的空判断才是用null)
            System.out.println("读取Client通过数据通道写入的数据并呈现~~");
            byte[] buf = new byte[1024];
            int readLen = 0;
            while ((readLen = inputStream.read(buf)) != -1) {
                System.out.println(new String(buf, 0, readLen)); // 根据读取到的实际长度,显示内容
            }
            OutputStream outputStream = socket.getOutputStream();
            outputStream.write("server: hello client, baga".getBytes());
    //        设置结束标记
            socket.shutdownOutput();
    
    //        勿忘关闭流和socket
            inputStream.close();
            outputStream.close();
            socket.close();
            serverSocket.close(); // 以防资源的浪费、占用
    
        }
    }
    
  • 注意:因为有来回的对话,不像案例一发完就退,所以要在 socket 设置结束标记 ( 即:socket.shutdownOutput() ) ,表示已完成了信息的发送

应用案例三 ( 使用字符流,使在发送文本的时候更方便些 ) :

  • 既然要用字符流,就 OutputStream —> Writer,InputStream —> Reader

  • 编写一个服务器端和一个客户端

  • 服务器端在 9999 端口监听

  • 客户端连接到服务器端,发送 " hello server, I'm Client ",并接收服务器端回发的 " hello client, baga ",然后再退出

  • 服务器端接收到客户端发送的信息,输出,并发送 " hello client, baga " 再退出

  • 代码编写 — 客户端:

    public class SocketTCP03Client {
        public static void main(String[] args) throws IOException {
            Socket socket = new Socket(InetAddress.getLocalHost(), 9999); // 因为演示是在一个电脑上,所以就获取本机,连接本机的9999端口
    
            System.out.println("客户端 socket 返回 = " + socket.getClass());
    
            OutputStream outputStream = socket.getOutputStream();
    //        outputStream.write("hello server, I'm Client".getBytes()); ———> 字节流所用
    //        使用字符流:
            BufferedWriter bufferedWriter = new BufferedWriter(new OutputStreamWriter(outputStream)); // OutputStreamWriter 转换流
            bufferedWriter.write("hello server, I'm Client (字符流)");
            bufferedWriter.newLine(); // 插入一个换行符,表示写入的内容结束(注意:要求对方用readLine()来读!!!!)
            bufferedWriter.flush(); // 如果使用的字符流,需要手动刷新,否则数据不会写入数据通道
    
            InputStream inputStream = socket.getInputStream();
    //        byte[] buf = new byte[1024];
    //        int readLen = 0;
    //        while ((readLen = inputStream.read(buf)) != -1) {
    //            System.out.println(new String(buf, 0, readLen));
    //        }
    //        字符流方式:
            BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(inputStream)); // InputStreamReader 转换为字符流
            String s = bufferedReader.readLine();
            System.out.println(s); // 输出
    
            outputStream.close();
            inputStream.close();
            socket.close();
            System.out.println("信息已发送,客户端已退出......");
        }
    }
    
  • 代码编写 — 服务端:

    public class SocketTCP03Server {
        public static void main(String[] args) throws IOException {
            ServerSocket serverSocket = new ServerSocket(9999);
    
            System.out.println("服务端在9999端口监听,等待连接中......");
            Socket socket = serverSocket.accept();
            System.out.println("服务器端 socket = " + socket.getClass());
    
            InputStream inputStream = socket.getInputStream();
            System.out.println("读取Client通过数据通道写入的数据并呈现~~");
    //        byte[] buf = new byte[1024];
    //        int readLen = 0;
    //        while ((readLen = inputStream.read(buf)) != -1) {
    //            System.out.println(new String(buf, 0, readLen));
    //        }
    //        字符流方式:
            BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(inputStream));
            String s = bufferedReader.readLine();
            System.out.println(s);
    
            OutputStream outputStream = socket.getOutputStream();
    //        outputStream.write("server: hello client, baga".getBytes());
    //        socket.shutdownOutput();
    //        字符流方式:
            BufferedWriter bufferedWriter = new BufferedWriter(new OutputStreamWriter(outputStream));
            bufferedWriter.write("hello client, baga (字符流)");
            bufferedWriter.newLine(); // 写入一个换行符表示写入/回复内容的结束
            bufferedWriter.flush(); // 需要手动刷新flush
    
    //        inputStream.close(); // 关外层流
            bufferedReader.close();
    
    //        outputStream.close();
            bufferedWriter.close();
            socket.close();
            serverSocket.close();
    
        }
    }
    

应用案例四 ( 图片 + 信息 )

  • 编写一个服务器端和一个客户端

  • 服务器端在 8888 端口监听

  • 客户端连接到服务器端,发送一张图片 e:\\student.jpg

  • 服务器端接收到客户端发送的图片,保存到 src 下,发送 " 收到图片 " 再退出

  • 客户端接收到服务端发送的 " 收到图片 " 再退出

  • 要求该程序使用 StreamUtils.java

    • 一个辅助工具类

      /**
       * 此类用于演示关于流的读写方法
       */
      public class StreamUtils {
          /**
           * 功能:将输入流转换成byte[],即可以把文件的内容读入到byte[]
           */
          public static byte[] streamToByteArray(InputStream is) throws Exception {
              ByteArrayOutputStream bos = new ByteArrayOutputStream(); //创建输出流对象
              byte[] b = new byte[1024]; // 字节数组
              int len;
              while((len = is.read(b)) != -1) { // 循环读取
                  bos.write(b, 0, len); // 把读取到的数据写入bos
              }
              byte[] array = bos.toByteArray(); // 然后将bos转成字节数组(就是将图片转成程序内文件字节数组的过程)
              bos.close();
              return array;
          }
      
          public static String streamToString(InputStream is) throws Exception {
              /**
               * 功能:输入流数据直接转成字符串
               */
              BufferedReader reader = new BufferedReader(new InputStreamReader(is));
              StringBuilder builder = new StringBuilder();
              String line;
              while ((line = reader.readLine()) != null) {
                  builder.append(line + "\r\n");
              }
              return builder.toString();
          }
      }
      
  • 使用 BufferedInputStream 和 BufferedOutputStream

  • 思路:

    • 将客户端的图片通过网络上传到服务器,服务器回复消息
    • 客户端处,磁盘上的图片输入读到程序中,先放进一个字节数组 ( 图片是二进制 ),数据通过 socket 入输出流里 ——> 服务端输入流接收、读文件数据 ( 字节 ),然后输出到服务器的某目录下
    • 服务端将信息 socket 输出,在客户端 socket 输入流,获取信息在程序里
  • 代码编写 — 服务端:

    public class SocketTCP04Server {
        public static void main(String[] args) throws Exception {
    //        1. 服务端在本机监听 8888 端口
            ServerSocket serverSocket = new ServerSocket(8888);
    //        2. 等待连接
            System.out.println("服务端8888端口监听等待......");
            Socket socket = serverSocket.accept();
    //        3. 读取客户端发送的数据
    //        通过socket得到一个输入流
            BufferedInputStream bis = new BufferedInputStream(socket.getInputStream());
            byte[] bytes = StreamUtils.streamToByteArray(bis);
    //        4. 将得到的bytes数组(输出流)写入到指定的路径,就得到一个文件了
            String desFilePath = "src\\main\\webapp\\img\\student.jpg";
            BufferedOutputStream bos = new BufferedOutputStream(new FileOutputStream(desFilePath));
            bos.write(bytes);
            bos.close();
    
    //        BufferedOutputStream字节流对图片,BufferedWriter字符流对文本
    
    //        5. 向客户端回复 "收到图片"
    //        通过socket获取到输出流(字符)
            BufferedWriter bufferedWriter = new BufferedWriter(new OutputStreamWriter(socket.getOutputStream()));
            bufferedWriter.write("收到图片");
            bufferedWriter.flush(); // 把内容刷新到数据通道
            socket.shutdownOutput(); // 设置写入结束标记(否则读的时候不知道什么时候结束),此处用shut而不是换行符newLine
    
    //        关闭其他资源
            bis.close();
            socket.close();
            serverSocket.close();
            bufferedWriter.close();
        }
    }
    
  • 代码编写 — 客户端:

    public class SocketTCP04Client {
        public static void main(String[] args) throws Exception {
    //        1. 客户端连接服务端,得Socket对象
            Socket socket = new Socket(InetAddress.getLocalHost(), 8888);
    //        2. 创建读取磁盘文件的输入流
            String filePath = "e:\\student.jpg";
            BufferedInputStream bis = new BufferedInputStream(new FileInputStream(filePath));
    
            byte[] bytes = StreamUtils.streamToByteArray(bis); // 这里的bytes就是存的文件内容了
    
    //        3. 通过socket获取到输出流,将bytes数据发送给服务端
            BufferedOutputStream bos = new BufferedOutputStream(socket.getOutputStream());
            bos.write(bytes); // 将文件对应的字节数组写入到数据通道
            bis.close();
            socket.shutdownOutput(); // 设置写入数据的结束标记
    
    //        采用了工具类方法,所以可直接用InputStream读取成字符串,否则还要readLen字节数组循环读取(new String...)
    
    //        接收从服务端回复的消息
            InputStream inputStream = socket.getInputStream();
    //        使用工具类的方法,直接将inputStream读取到的内容转成字符串
            String s = StreamUtils.streamToString(inputStream);
            System.out.println(s);
    
    //        关闭相关流
            bos.close();
            socket.close();
            inputStream.close();
        }
    }
    

netstat 指令 ( TCP )

  • netstat -an:可以查看当前主机网络情况,包括端口监听情况和网络连接情况

  • netstat -an|more:可以分页显示 ( 回车显示下一条,空格显示下一页 )

    • 可以输入 ctrl + c 直接退出
  • 要求在 dos 控制台下执行

  • 说明:

    • Listening 表示某个端口在监听
    • 如果有一个外部程序 ( 客户端 ) 连接到该端口,就会显示一条连接信息
  • netstat -anb:要找到命令提示符 ( 开始菜单的 Windows 系统里 ) 以管理员身份运行才行

    • 同理后面加上:|more 就可以分页显示

    • 运行先前案例代码的 Server 部分后就会发现多了一条:

      image-20230608221901159

  • TCP 网络通讯不为人知的秘密

    • 当客户端连接到服务端后,实际上客户端也是通过一个端口和服务器端进行通讯的,这个端口是 TCP / IP 来分配的 ( 是不确定的,随机的 )

    • 验证:在运行 Client 后紧接着 netstat -an | more,就会发现有一个和 8888 对应的随机端口 ( 例如:60258 )

    • 如:TCP 192.168.12.1:8888 192.168.12.1:60258

      ​ TCP 192.168.12.1:60258 192.168.12.1:8888

      • 因为服务端和客户端在编码时都放在了同一台主机:本机上,所以有两个来回
      • 实际上服务端只能看到第一条,客户端只能看到第二条