网络编程

:::tips

概述:

  • 实现不同计算机上进行数据传输的程序

    三要素

  • 确定发给哪个接收端(IP地址)

  • 确定发给接收端中的哪个应用程序(端口号)
  • 确定网络中传输数据的规则(协议)

    IP地址介绍

  • 查看本机IP地址

    • 在DOS命令行输入: ipconfig
  • 检查网络是否连通
    • 在DOS命令行输入: ping IP地址
  • 特殊IP地址

    • 127.0.0.1:是回送地址也称本地回环地址,可以代表本机的IP地址,一般用来测试使用 :::

      协议介绍

      :::tips

      协议:计算机网络中,连接和通信的规则被称为网络通信协议

  • UDP协议: 用户数据报协议(User Datagram Protocol)

    • 不需要连接
    • 速度快
    • 有大小限制
    • 一次最多发送64K
    • 易丢失数据
  • TCP协议: 传输控制协议 (Transmission Control Protocol)

    • 需要连接
    • 速度慢
    • 没有大小限制
    • 不易丢失数据 :::

      UDP通信程序

      :::tips

      InetAddress的使用

      InetAddress:此类表示Internet协议(IP)地址
      常用方法
  • static InetAddress getLocalhost() 静态方法,获取本机的IP地址对象

  • static InetAddress getByName(String host) 静态方法,通过主机名或IP地址得到InetAddress对象
  • String getHostName() 获取此IP地址的主机名
  • String getHostAddress() 返回IP地址字符串

    DatagramPacket发送数据的构造方法:
  • DatagramPacket(byte[] buf, int offset, int length, InetAddress address, int port)

    • buf :要发送的字节数据
    • offset:从数组的哪个位置开始发送
    • length:发送数据的长度
    • address:接收端的IP
    • port:接收端的端口号
      DatagramPacket接收数据的构造方法:
  • DatagramPacket(byte[] buf, int length)

    • buf: 用来保存接收到的数据
    • length: 指定接收数据的长度 :::

      1. /*
      2. 目标:编写UDP发送端
      3. 发送数据的步骤:
      4. 1.创建发送端。 DatagramSocket s = new DatagramSocket();
      5. 2.创建数据包,并指定IP(cmd -> ipconfig)和端口。DatagramPacket
      6. 3.发送数据。send(DatagramPacket p)方法
      7. 4.关闭资源。
      8. DatagramPacket发送数据的构造方法:
      9. DatagramPacket(byte[] buf, int offset, int length, InetAddress address, int port)
      10. buf :要发送的字节数据
      11. offset:从数组的哪个位置开始发送
      12. length:发送数据的长度
      13. address:接收端的IP
      14. port:接收端的端口号
      15. */
      16. //数据发送端
      17. public class UDPSender {
      18. public static void main(String[] args) throws Exception{
      19. //创建发送端(快递员)
      20. DatagramSocket socket = new DatagramSocket();
      21. //创建数据包,并指定IP和端口(快递)
      22. byte[] arr = "hello".getBytes();//数据
      23. InetAddress ip = InetAddress.getByName("192.168.88.49");//IP
      24. int port = 8888;//端口
      25. DatagramPacket packet = new DatagramPacket(arr, 0, arr.length, ip, port);
      26. //发送数据(快递员发快递)
      27. socket.send(packet);
      28. //关闭资源
      29. socket.close();
      30. System.out.println("发送端发送完毕");
      31. }
      32. }
      1. /*
      2. 目标:编写UDP接收端
      3. 接收数据的步骤:
      4. 1.创建接收端。 DatagramSocket s = new DatagramSocket(int port);
      5. 2.创建空的数据包,用于装数据。DatagramPacket
      6. 3.接收数据。receive(DatagramPacket p)方法
      7. 4.解析数据。(从数组中获取有效内容)
      8. 5.关闭资源。close
      9. DatagramPacket接收数据的构造方法:
      10. DatagramPacket(byte[] buf, int length)
      11. buf: 用来保存接收到的数据
      12. length: 指定接收数据的长度
      13. */
      14. //数据接收端
      15. public class UDPReceiver {
      16. public static void main(String[] args) throws Exception{
      17. System.out.println("接收端返回");
      18. //创建接收端
      19. DatagramSocket socket = new DatagramSocket(8888);
      20. //创建空的数据包,用于装数据
      21. byte[] arr = new byte[1024];
      22. DatagramPacket packet = new DatagramPacket(arr, arr.length);
      23. //接收数据
      24. socket.receive(packet);
      25. //从packet中获取数据的长度
      26. int length = packet.getLength();
      27. //解析数据(从数组中获取有效内容)
      28. System.out.println(new String(arr, 0, length));
      29. socket.close();
      30. }
      31. }

      TCP通信程序

      :::tips

      原理

  • TCP通信协议是一种可靠的网络协议,它在通信的两端各建立一个Socket对象

  • 通信之前要保证连接已经建立(TCP三次握手)
  • 通过Socket产生IO流来进行网络通信

    通信流程

  • TCP协议通信是需要连接的,建立连接后以流的形式传输 ::: ```java /* 目标:编写TCP客户端程序

  1. 创建客户端Socke对象,与指定服务端连接 Socket(String host, int port)
    1. host :服务端IP地址
    2. port :服务端端口号
  2. 得到输出流写数据 OutputStream getOutputStream()
  3. 得到输入流读取数据 InputStream getInputStream()
  4. 关闭资源 void close() */ public class TCPClinet { public static void main(String[] args) throws Exception{

     System.out.println("客户端启动成功");
     //创建TCP客户端,指定目的地的IP和端口
     Socket socket = new Socket("127.0.0.1", 8887);
     //往服务端发送数据
     OutputStream output = socket.getOutputStream();
     output.write("dddd".getBytes());
     //发出输出结束
     socket.shutdownOutput();
     //接收服务端和回复信息(得到输入流读取数据)
     InputStream input = socket.getInputStream();
     byte[] arr = new byte[1024];
    
     int len = input.read(arr);
     System.out.println(new String(arr, 0, len));
    
     //关闭资源
     input.close();
     output.close();
     socket.close();
    

    } } java /* 目标:编写TCP服务端程序

  5. 创建TCP服务端ServerSocket对象。 ServerSocket(int port)
  6. 监听客户端的连接,连接成功返回一个Socket对象。 Socket accept() 3.得到输入流读取数据。 InputStream getInputStream() 4.得到输出流写数据。 OutputStream getOutputStream() 5.关闭资源 void close() */ public class TCPServer { public static void main(String[] args) throws Exception{

     System.out.println("服务端启动");
     //创建服务端
     ServerSocket sSok = new ServerSocket(8887);
     //和客户端建立连接(TCP通讯的连接)
     Socket socket = sSok.accept();
     System.out.println(socket);
     //接收客户端信息(读取数据)
     InputStream input = socket.getInputStream();
     byte[] arr = new byte[1024];
     int len;
     while ((len = input.read(arr)) != -1) {
         System.out.println(new String(arr, 0, len));
     }
     //回复客户端(写出数据)
     OutputStream output = socket.getOutputStream();
     output.write("dddd".getBytes());
     //关闭资源
     output.close();
     input.close();
     //sSok.close();//为了持续化接收输出通常不关,
    

    } } java / 编写文件上传的客户端 1.创建Socket客户端 2.创建输入流读取文件 3.从Socket获取输出流,循环读写数据 4.从Socket获取输入流,获取服务端的回应。 5.关闭资源 / public class UploadClient1 { public static void main(String[] args) throws Exception{

     System.out.println("客户端启动");
     //1.创建Socket客户端
     Socket socket = new Socket("127.0.0.1",8888);
     //2.创建输入流读取文件
     FileInputStream fis = new FileInputStream("D:\\MyTest\\test.jpg");
     //3.从Socket获取输出流
     OutputStream out = socket.getOutputStream();
    
     //循环读写数据
     byte[] array = new byte[1024*8];
     int len;
     while ((len=fis.read(array))!=-1){
         out.write(array,0,len);
     }
    
     //给出输出结束的标记
     socket.shutdownOutput();
    
     //4.从Socket获取输入流,获取服务端的回应。
     InputStream in = socket.getInputStream();
     len = in.read(array);
     System.out.println("服务端回应:"+new String(array,0,len));
    
     //5.关闭资源
     in.close();
     out.close();
     fis.close();
     socket.close();
    

    } }

/ 编写文件上传的服务端 1.创建ServerSocket服务端 while(true){ 2.监听客户端连接 3.获取Socket输入流,用于接收客户端文件 4.创建文件输出流,循环读写数据 5.获取Socket输出流,向客户端写出上传结果 6.关闭资源 } / public class UploadServer { public static void main(String[] args) throws Exception{ //1.创建ServerSocket服务端 System.out.println(“服务端启动”); ServerSocket ss = new ServerSocket(8888);

    //线程池,提高效率
    ExecutorService pool = Executors.newFixedThreadPool(20);

    while (true) {
        //2.监听客户端连接
        //等待客户端的连接,一个客户端对应一个socket
        Socket socket = ss.accept();
        System.out.println(socket);
       /* UploadThread upload = new UploadThread(socket);
        Thread t = new Thread(upload);
        t.start();*/
        UploadThread upload = new UploadThread(socket);
        pool.submit(upload);
    }
}

}

//线程 public class UploadThread implements Runnable{ private Socket socket; public UploadThread(Socket socket) { this.socket = socket; } @Override public void run() { InputStream in = null; FileOutputStream fos = null; OutputStream out = null; try { //3.获取Socket输入流,用于接收客户端文件 in = socket.getInputStream(); //4.创建文件输出流,保存用户的图片到服务器中 //UUID通用唯一识别码 String filename = UUID.randomUUID().toString()+ “.jpg”; fos = new FileOutputStream(“t_day12\upload\“+filename); byte[] array = new byte[1024*8]; int len; while ((len=in.read(array))!=-1){ fos.write(array,0,len); } //5.获取Socket输出流,向客户端写出上传结果 out = socket.getOutputStream(); out.write(“上传成功!”.getBytes()); System.out.println(“传输完毕”); }catch (Exception e){ System.out.println(“传输失败:”+e.toString()); }finally { //6.关闭资源 try { out.close(); fos.close(); in.close(); socket.close(); } catch (Exception e) { System.out.println(“传输失败:”+e.toString()); } } } }

<a name="xqsgP"></a>
### NIO
:::tips
<a name="Iq7Ug"></a>
#### NIO概述

- JDK1.4以前:InputStream/OutputStream称为BIO(Blocking IO)    阻塞式IO  BIO
- JDK1.4推出了一套新的IO体系称为NIO (New IO/ Not Blocking IO)   非阻塞式IO 
<a name="Bc58u"></a>
#### 阻塞和非阻塞都是处理IO数据的方式

- 阻塞:如果没有数据就一直等待
- 非阻塞:如果没有数据,不会一直等待,可以做其他事情
<a name="pzhL2"></a>
#### BIO模型(单向流)

- BIO是同步阻塞模型,一个客户端连接对应一个处理线程
- 在BIO中,accept和read方法都是阻塞操作
- 如果没有连接请求,accept方法阻塞
- 如果无数据可读取,read方法阻塞。
<a name="GaesO"></a>
#### NIO(双向通道)

- Buffer缓存
   - 用于存储数据,底层基于数组实现,针对8种基本类型提供了对应的缓冲区类
   - Buffer相当于之前BIO的byte[],但效率更高,功能更强大。
- Channel通道
   - 用于进行数据传输,面向缓冲区进行操作,支持双向传输
   - 数据可以从Channel读取到Buffer中,也可以从Buffer写到Channel中
- Selector选择器
   - Selector也叫多路复用器,使用一个线程高效地管理多个Channel
   - 当向一个Selector中注册Channel后,Selector内部就可以自动不断地查询这些注册的Channel是否有已就绪的 I/O 事件
   - 当某个Channel上面发生了读或写事件,这个Channel就会被Selector监听到,然后通过SelectionKeys可以获取就绪Channel的集合,进行后续的I/O操作。
:::
<a name="JD5MO"></a>
### commons-io工具包
:::tips
<a name="ewFGz"></a>
#### 概述

- commons-io是apache开源基金组织提供的一组有关IO操作的类库,可以提高IO功能开发的效率
- commons-io工具包提供了很多有关io操作的类。有两个主要的类FileUtils, IOUtils
<a name="E8muq"></a>
#### FileUtils主要方法

- String readFileToString(File file, String encoding)    读取文件中的数据, 返回字符串
- void copyFile(File srcFile, File destFile)    复制文件
- void copyDirectoryToDirectory(File srcDir, File destDir)    复制文件夹
- void deleteDirectory(File directory)     删除文件夹
:::
```java
/*
1.FileUtils操作文件或文件夹
    拷贝文件:copyFile(File srcFile, File destFile)
    拷贝文件夹:copyDirectoryToDirectory(File srcDir, File destDir)
        文件夹里面的子文件夹也会一起拷贝
    删除文件夹: deleteDirectory(File directory)
        注意:整个文件夹都会直接删除,不管里面是否有内容
    获取文件或文件夹大小:long sizeOf(File file)
2.FileUtils读取文件
    读取文件内容为字符串:String readFileToString(File file, String encoding)
    将文件内容按行读取成字符串集合:List<String> readLines(File file, String encoding)*/
public class Demo {
    public static void main(String[] args) throws Exception{
        FileUtils.copyFile(
                new File("E:\\File\\hello.txt"), new File("Day12UDPTCP\\A\\hello.txt")
        );

        FileUtils.copyDirectoryToDirectory(
                new File("E:\\File"), new File("Day12UDPTCP\\A")
        );

        FileUtils.deleteDirectory(new File("Day12UDPTCP\\A\\File"));

        long l = FileUtils.sizeOf(new File("E:\\File"));
        System.out.println(l);

        //readFileToString(文件对象,编码)
        String s = FileUtils.readFileToString(
                new File("Day12UDPTCP\\A\\gbk_file.txt"), "GBK"
        );
        System.out.println(s);

        //List<String> readLines(文件对象,编码)
        List<String> list = FileUtils.readLines(
                new File("Day12UDPTCP\\A\\gbk_file.txt"), "GBK"
        );
        list.forEach(System.out::println);
    }
}