# 学习目标

  • 能够说出 TCP 协议下两个常用类名称
  • 能够编写 TCP 协议下字符串数据传输程序
  • 能够理解 TCP 协议下文件上传案例
  • 能够理解 TCP 协议下 BS 案例

# 网络编程概念

# 网络编程概念

什么是网络编程编:写在不同计算机上进行数据传输的程序。
网络编程场景:网络应用程序、即时通信、网游对战、金融证券、国际贸易、邮件等等。

常见的软件架构有如下 2 种形式:Client-Server(CS) 、 Browser/Server(BS)Client-Server(CS): 客户端-服务端模式
C/S
B/S

# 网络编程三要素-IP 地址

你女朋友叫你去酒店找她学英语,你需要知道哪些信息?

  • 酒店的地址
  • 房间号
  • 交流用的语言

我们要想在不同计算机之间传输数据必须具备 3 个条件,称为网络编程三要素:

  • IP 地址
  • 端口号
  • 协议

IP 地址概念:互联网协议地址(Internet Protocol Address),俗称 IP。IP 地址用来给网络中的计算机进行编号。
IP 地址作用:通过 IP 地址可以找到网络中的某台电脑,IP 地址就相当于生活中的家庭住址。

IPv4:是一个 32 位的二进制数,通常被分为 4 个字节,表示成 a.b.c.d 的形式,例如 192.168.65.100。其中 a、b、c、d 都是 0~255 之间的十进制整数,那么最多可以表示 42 亿个。

IPv6:由于互联网的蓬勃发展,IP 地址的需求量愈来愈大,但是网络地址资源有限,使得 IP 的分配越发紧张。有资料显示,全球 IPv4 地址在 2011 年 2 月分配完毕。为了扩大地址空间,拟通过 IPv6 重新定义地址空间,采用 128 位地址长度,分成 8 组以十六进制数显示,表示成 ABCD:EF01:2345:6789:ABCD:EF01:2345:6789,号称可以为全世界的每一粒沙子编上一个网址,这样就解决了网络地址资源数量不够的问题。

查看本机 IP 地址,在 DOS 命令行输入: ipconfig

检查网络是否连通在 DOS 命令行输入: ping IP 地址/域名

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

# 网络编程三要素-端口

端口介绍:我们一台电脑上会安装很多的应用程序,内网通,微信,QQ。如何找到计算机中的某个程序?应用程序在设备中唯一的标识。

端口作用:通过端口号可以找到电脑上的某个程序

用两个字节表示的整数,它的取值范围是 0~65535。其中 0~1023 之间的端口号用于一些知名的网络服务或者应用。我们自己使用 1024 以上的端口号就可以了。

# 网络编程三要素-协议

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

网络通信协议有两套参考模型:

  • OSI 参考模型:世界互联协议标准,全球通信规范,单模型过于理想化,未能在因特网上进行广泛推广。
  • TCP/IP 参考模型(或 TCP/IP 协议):事实上的国际标准。
    OSI 参考模型 TCP/IP 参考模型 TCP/IP 参考模型各层对应协议 面向哪些
    应用层 应用层 HTTP、FTP、Telnet、 DNS… 一般是应用程序需要关注的。
    如浏览器,邮箱。程序员一般在这一层开发
    表示层
    会话层
    传输层 传输层 TCP、UDP、… 选择传输使用的 TCP , UDP 协议
    网络层 网络层 IP、ICMP、ARP… 封装自己的 IP,对方的 IP 等信息
    数据链路层 物理+数据链路层 硬件设备。010100101010100101010… 转换成二进制利用物理设备传输
    物理层

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

  • 不需要连接
  • 速度快
  • 有大小限制一次最多发送 64K
  • 易丢失数据

UDP 协议通信场景(速度要求高,数据完整性要求不高):

  • 直播
  • 语音通话
  • 视频会话

TCP 协议特点: 传输控制协议 (Transmission Control Protocol)

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

TCP 协议通信场景(速度要求不高,数据完整性要求高):

  • 下载扫
  • 码支付
  • 金融等数据通信

# UDP 通信程序

# 介绍 UDP 通信流程

UDP 协议通信是不需要连接的,相当于我们生活中的寄快递,直接寄出即可。
DatagramPacket: 表示数据包 数据的目的地 IP 地址 数据目的地端口号 DatagramPacket(byte[] buf, int offset, int length, InetAddress address, int port) 创建发送的数据包 DatagramPacket(byte[] buf, int length) 创建接收的数据包

发送端:1.创建发送端 2.创建一个数据包 3.发送数据包
接收端:1.创建接收端 2.创建空的数据包 3.接收数据

# 编写 UDP 通信程序

InetAddress 的使用:为了方便我们对 IP 地址的获取和操作,Java 提供了一个类 InetAddress 供我们使用 InetAddress:此类表示 Internet 协议(IP)地址

方法名 说明
static InetAddress getLocalhost() 获取本机的 IP 地址对象
public static InetAddress getByName(String host) 确定主机名称的 IP 地址。主机名称可以是机器名称,也可以是 IP 地址
public String getHostName() 获取此 IP 地址的主机名
public String getHostAddress() 返回文本显示中的 IP 地址字符串

DatagramSocket 的作用:表示 UDP 的发送端和接收端 send()发送数据,receive()接收数据
DatagramPacket 的作用表示数据包

发送端:

package com.itheima.demo02UDP_了解;

import java.io.IOException;
import java.net.*;

/*
目标:编写UDP的发送端

DatagramPacket(byte buf[], int offset, int length, InetAddress address, int port)
    byte buf[]: 要发送的数据的字节数组
    int offset: 从数组哪个位置开始发
    int length: 发送多少数据
    InetAddress address: 接收方的IP地址
    int port: 接收方的端口

    InetAddress.getLocalHost(): 获取本机的IP
    InetAddress.getByName("192.168.75.96"): 别人的IP地址

注意:UDP协议不需要连接,没有接收端也不会报错,数据丢失啦
 */
public class UDPSender {
    public static void main(String[] args) throws IOException {
        System.out.println("发送端启动!");
        // 1.创建发送端
        DatagramSocket socket = new DatagramSocket();

        // 2.创建数据包
        byte[] bytes = "你好UDP".getBytes();
        // DatagramPacket packet = new DatagramPacket(bytes, 0, bytes.length, InetAddress.getLocalHost(), 9527);
        DatagramPacket packet = new DatagramPacket(bytes, 0, bytes.length, InetAddress.getByName("192.168.73.252"), 9527); // 接收方需要关闭防火墙

        // 3.发送数据
        socket.send(packet);

        socket.close();
    }
}

接收端:

package com.itheima.demo02UDP_了解;

import java.io.IOException;
import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.net.SocketException;

/*
目标:编写UDP的接收端

    DatagramPacket(byte buf[], int length)
        创建空的数据包
        byte buf[]: 用来保存数据的数组
        int length: 数组一次可以接收多少数据

注意:要先运行接收端

 */
public class UDPReceiver {
    public static void main(String[] args) throws IOException {
        System.out.println("接收端启动!");
        // 1.创建接收端, 会绑定本机的9527端口,别人通过9527端口就能找到这个接收端
        DatagramSocket socket = new DatagramSocket(9527);

        // 2.创建空的数据包
        byte[] buf = new byte[1024];
        DatagramPacket packet = new DatagramPacket(buf, buf.length);

        // 3.接收数据, 接收到的数据放到包中
        socket.receive(packet);
        int length = packet.getLength(); // 获取接收数据的长度
        System.out.println("接收端收到: " + new String(buf, 0, length));

        socket.close();
    }
}

# TCP 通信程序

# 介绍 TCP 通信流程

TCP 协议通信是需要连接的,建立连接后以流的形式传输,相当于打电话。
image.png

# 代码实现 TCP 客户端

实现 TCP 客户端步骤:

  1. 创建客户端
  2. 得到输出流写数据
  3. 得到输入流读取数据
  4. 关闭资源
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.Socket;

/*
目标: 编写TCP客户端

Socket(String host, int port) 创建客户端, 会自动连接服务端
    String host: 服务端的IP
    int port: 服务端的端口

注意:
    TCP协议先启动客户端,没有服务端,会出现连接异常
    TCP程序一定要先运行服务端
 */
public class TCPClient {
    public static void main(String[] args) throws IOException {
        System.out.println("客户端启动啦!");
        // 1.创建客户端, 创建好的客户端会自动连接 "127.0.0.1" 这台电脑的 10086端口
        Socket socket = new Socket("127.0.0.1", 10086);

        // 2.得到输出流写数据
        OutputStream out = socket.getOutputStream();
        out.write("你好约吗?".getBytes());

        // 3.得到输入流读取数据
        InputStream in = socket.getInputStream();
        byte[] buf = new byte[1024];
        int len = in.read(buf);
        System.out.println("客户端收到: " + new String(buf, 0, len));

        // 4.关闭资源
        in.close();
        out.close();
        socket.close();
    }
}

# 代码实现 TCP 服务端

实现 TCP 服务端步骤:

  1. 创建 TCP 服务端
  2. 同意客户端的请求
  3. 得到输入流读取数据
  4. 得到输出流写数据
  5. 关闭资源
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.ServerSocket;
import java.net.Socket;

/*
目标:编写TCP服务端
 */
public class TCPServer {
    public static void main(String[] args) throws IOException {
        System.out.println("服务端启动啦!");
        // 1.创建TCP服务端, 创建的服务端会使用本机的10086端口,将来别的程序通过10086端口就能找到这个服务端
        ServerSocket serverSocket = new ServerSocket(10086);
        // 2.同意客户端的请求, 如果没有客户端连接就一直等
        Socket socket = serverSocket.accept();

        // 3.得到输入流读取数据
        InputStream in = socket.getInputStream();
        byte[] buf = new byte[1024];
        int len = in.read(buf); // 没有数据会一直等
        System.out.println("服务端收到: " + new String(buf, 0, len));

        // 4.得到输出流写数据
        OutputStream out = socket.getOutputStream();
        out.write("老地方见!".getBytes());

        // 5.关闭资源
        out.close();
        in.close();
        socket.close();
        serverSocket.close();
    }
}

# 文件上传案例

# 理解 TCP 文件上传流程

客户端读取本地的文件,通过 Socket 流发送给服务端。服务端读取 Socket 流中的数据,写到文件中。
image.png

# 编写 TCP 文件上传客户端

  1. 创建客户端
  2. 创建文件输入流
  3. 得到 Socket 的输出流
  4. 循环读写数据
  5. 得到 Socket 输入流读取数据
  6. 关闭资源
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.Socket;

/*
目标:编写文件上传的客户端

Socket(String host, int port) 创建客户端, 会自动连接服务端
    String host: 服务端的IP
    int port: 服务端的端口
 */
public class UploadClient {
    public static void main(String[] args) throws IOException {
        System.out.println("文件上传客户端启动啦!");
        // 1.创建客户端, 客户端会连接127.0.0.1这个IP的电脑的9999端口
        Socket socket = new Socket("127.0.0.1", 9999);
        // 2.创建文件输入流
        FileInputStream fis = new FileInputStream("D:\\MyFileTest\\xyz.png");
        // 3.得到Socket的输出流
        OutputStream out = socket.getOutputStream();

        // 4.循环读写数据
        byte[] buf = new byte[1024 * 8]; // 保存读取的数据
        int len; // 保存读取的数量
        while ((len = fis.read(buf)) != -1) {
            out.write(buf, 0, len);
        }
        System.out.println("客户端发送文件完成!");
        socket.shutdownOutput(); // 把客户端socket的输出流断掉,服务端的输入也就端掉啦.

        // 5.得到Socket输入流读取数据
        InputStream in = socket.getInputStream();
        len = in.read(buf);
        System.out.println("客户端收到: " + new String(buf, 0, len));

        // 6.关闭资源
        in.close();
        out.close();
        fis.close();
        socket.close();
    }
}

# 编写 TCP 文件上传服务端

  1. 创建服务端
  2. 同意客户端的连接
  3. 得到 Socket 输入流
  4. 创建文件输出流
  5. 循环读写数据
  6. 得到 Socket 的输出流写数据
  7. 关闭
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.ServerSocket;
import java.net.Socket;
import java.util.UUID;

/*
目标:编写文件上传的服务端

我们现在学习的Socket和ServerSocket,没有数据会一直等待,不会结束,称为BIO(Blocking IO/阻塞式IO)

目标:解决上传文件重名问题
    UUID生成一个随机的字符串


 */
public class UploadServer {
    public static void main(String[] args) throws IOException {
        System.out.println("文件上传服务端启动啦!");
        // 1.创建服务端, 服务端会使用本机的9999端口, 将来客户端通过9999端口可以找到服务端
        ServerSocket serverSocket = new ServerSocket(9999);
        // 2.同意客户端的连接
        Socket socket = serverSocket.accept();
        // 3.得到Socket输入流
        InputStream in = socket.getInputStream();
        // 4.创建文件输出流
        FileOutputStream fos = new FileOutputStream("study_day12\\upload\\1.png");

        // 5.循环读写数据
        byte[] buf = new byte[1024 * 8]; // 保存读取的数据
        int len; // 保存读取的数量
        while ((len = in.read(buf)) != -1) {
            fos.write(buf, 0, len);
        }
        System.out.println("服务端接收完成!");

        // 6.得到Socket的输出流写数据
        OutputStream out = socket.getOutputStream();
        out.write("上传完成!".getBytes());

        // 7.关闭
        out.close();
        fos.close();
        in.close();
        socket.close();
        serverSocket.close();
    }
}

# 解决服务端无法停止问题

image.png
如何解决服务端无法停止问题:
image.png

# 完成文件上传加强版

  1. 上传图片名称不同
1. 时间戳: long fileName = System.currentTimeMillis();
2. 随机名字: String fileName = UUID.randomUUID().toString();
  1. 支持多人上传
while(true) {
    serverSocket.accept();
}
  1. 支持多人"同时"上传:线程池
// 创建线程池
ExecutorService pool = Executors.newFixedThreadPool(10);
while (true) {
    // 2.同意客户端的连接
    Socket socket = serverSocket.accept();

    // 开始上传的代码写到新线程中
    UploadRunnable up = new UploadRunnable(socket);

    // 提交任务到线程池
    pool.submit(up);
}

客户端代码:

package demo05文件上传多线程版本_练习;

import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.Socket;

/*
目标:编写文件上传的客户端

Socket(String host, int port) 创建客户端, 会自动连接服务端
    String host: 服务端的IP
    int port: 服务端的端口
 */
public class UploadClient {
    public static void main(String[] args) throws IOException {
        System.out.println("文件上传客户端启动啦!");
        // 1.创建客户端, 客户端会连接127.0.0.1这个IP的电脑的9999端口
        Socket socket = new Socket("127.0.0.1", 9999);
        // 2.创建文件输入流
        FileInputStream fis = new FileInputStream("D:\\MyFileTest\\xyz.png");
        // 3.得到Socket的输出流
        OutputStream out = socket.getOutputStream();

        // 4.循环读写数据
        byte[] buf = new byte[1024 * 8]; // 保存读取的数据
        int len; // 保存读取的数量
        while ((len = fis.read(buf)) != -1) {
            out.write(buf, 0, len);
        }
        System.out.println("客户端发送文件完成!");
        socket.shutdownOutput(); // 把客户端socket的输出流断掉,服务端的输入也就端掉啦.

        // 5.得到Socket输入流读取数据
        InputStream in = socket.getInputStream();
        len = in.read(buf);
        System.out.println("客户端收到: " + new String(buf, 0, len));

        // 6.关闭资源
        in.close();
        out.close();
        fis.close();
        socket.close();
    }
}

服务端代码:

package demo05文件上传多线程版本_练习;

import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.ServerSocket;
import java.net.Socket;
import java.util.UUID;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

/*
目标:编写文件上传的服务端

我们现在学习的Socket和ServerSocket,没有数据会一直等待,不会结束,称为BIO(Blocking IO/阻塞式IO)

目标:解决上传文件重名问题
    UUID生成一个随机的字符串

目标: 多人上传
    循环同意accept,并传输数据

目标: 多人"同时"上传
    使用多线程
 */
public class UploadServer {
    public static void main(String[] args) throws IOException {
        System.out.println("文件上传服务端启动啦!");
        // 1.创建服务端, 服务端会使用本机的9999端口, 将来客户端通过9999端口可以找到服务端
        ServerSocket serverSocket = new ServerSocket(9999);
        // 创建线程池
        ExecutorService pool = Executors.newFixedThreadPool(10);

        while (true) {
            // 2.同意客户端的连接
            Socket socket = serverSocket.accept();

            // 开始上传的代码写到新线程中
            UploadRunnable up = new UploadRunnable(socket);
            // 提交任务到线程池
            pool.submit(up);
        }

        // 服务端不需要关闭
        // serverSocket.close();
    }
}

package demo05文件上传多线程版本_练习;

import java.io.FileOutputStream;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.Socket;
import java.util.UUID;

public class UploadRunnable implements Runnable {
    // 成员变量
    private Socket socket;
    // 构造器给成员变量赋值
    public UploadRunnable(Socket socket) {
        this.socket = socket;
    }

    @Override
    public void run() {
        try {
            System.out.println(socket.getRemoteSocketAddress() + "开始上传!");
            // 3.得到Socket输入流
            InputStream in = socket.getInputStream();
            // 4.创建文件输出流
            // 让文件名不同
            // 1.时间戳: long fileName = System.currentTimeMillis();
            // 2.随机名字
            String fileName = UUID.randomUUID().toString();
            FileOutputStream fos = new FileOutputStream("study_day12\\upload\\" + fileName + ".png");

            // 5.循环读写数据
            byte[] buf = new byte[1024 * 8]; // 保存读取的数据
            int len; // 保存读取的数量
            while ((len = in.read(buf)) != -1) {
                fos.write(buf, 0, len);
            }
            System.out.println(socket.getRemoteSocketAddress() + "接收完成!");

            // 6.得到Socket的输出流写数据
            OutputStream out = socket.getOutputStream();
            out.write("上传完成!".getBytes());

            // 7.关闭
            out.close();
            fos.close();
            in.close();
            socket.close();
        } catch (Exception e) {
            System.out.println("文件上传失败: " + e.toString());
        }
    }
}

# 模拟 B\S 服务器

需求:模拟网站服务器,使用浏览器访问自己编写的服务端程序,浏览器得到服务器发送回的数据。

分析:
1. 准备页面数据,web文件夹。
2. 我们模拟服务器端,ServerSocket类监听端口,使用浏览器访问,查看网页效果

代码如下:

package demo07模拟网站服务器;

import java.io.IOException;
import java.io.OutputStream;
import java.net.ServerSocket;
import java.net.Socket;

public class WebServer {
    public static void main(String[] args) throws IOException {
        System.out.println("服务器启动成功!");
        ServerSocket serverSocket = new ServerSocket(3006);

        while (true) {
            Socket socket = serverSocket.accept();
            OutputStream outputStream = socket.getOutputStream();

            outputStream.write("HTTP/1.1 200 OK\r\n".getBytes());
            // 告诉浏览器返回一个 html ,编码是 utf-8
            outputStream.write("Content-Type:text/html;charset=utf-8;\r\n".getBytes());
            outputStream.write("\r\n".getBytes());
            outputStream.write("<h1>你好 world</h1>".getBytes());

            outputStream.close();
        }
    }
}

浏览器的底层是什么? TCP 客户端
服务器的底层是什么? TCP 服务端

# NIO

# NIO 概述介绍阻塞、非阻塞概念

NIO 概述:

  • JDK1.4 以前: InputStream/OutputStream 称为 BIO(Blocking IO) 阻塞式 IO
  • JDK1.4 推出了一套新的 IO 体系称为 NIO (New IO/ Not Blocking IO) 非阻塞式 IO

# 阻塞、非阻塞概念

阻塞和非阻塞都是处理 IO 数据的方式:

  • 阻塞:如果没有数据就一直等待
  • 非阻塞:如果没有数据,不会一直等待,可以做其他事情非阻塞的好处,不需要一直等待,当有数据来才需要处理,没有数据可以做其他操作

# 介绍 BIO 处理数据方式

BIO 处理数据:
image.png

# 介绍 NIO 处理数据方式

NIO 处理数据:
image.png
NIO 的优点:不需要一直等待,当有数据来才需要处理,没有数据可以做其他操作

# 介绍 NIO3 个角色

  • Channel(通道)可以双向传输数据
  • ByteBuffer 相当于之前 BIO 的 byte[],可以保存要发送和接收的数据, ByteBuffer 效率比 byte[] 要高,功能更强大
  • Selector 选择器(相等于门卫大爷),可以管理多个连接

image.png
使用了多路复用,只需要一个线程就可以处理多个通道,降低内存占用率,减少 CPU 切换时间,在高并发、高频段业务环境下有非常重要的优势

# 演示 NIO 代码

package demo08_了解NIO;

import java.io.IOException;
import java.net.InetSocketAddress;
import java.nio.ByteBuffer;
import java.nio.channels.SelectionKey;
import java.nio.channels.Selector;
import java.nio.channels.ServerSocketChannel;
import java.nio.channels.SocketChannel;
import java.util.Iterator;
import java.util.Set;

public class Server {
    public static void main(String[] args) throws IOException {
        ServerSocketChannel serverSocketChannel1 = ServerSocketChannel.open();
        serverSocketChannel1.bind(new InetSocketAddress(3006));
        System.out.println("serverSocketChannel1 = " + serverSocketChannel1);

        ServerSocketChannel serverSocketChannel2 = ServerSocketChannel.open();
        serverSocketChannel2.bind(new InetSocketAddress(3007));
        System.out.println("serverSocketChannel2 = " + serverSocketChannel2);

        ServerSocketChannel serverSocketChannel3 = ServerSocketChannel.open();
        serverSocketChannel3.bind(new InetSocketAddress(3008));
        System.out.println("serverSocketChannel3 = " + serverSocketChannel3);

        // 设置为非阻塞式
        serverSocketChannel1.configureBlocking(false);
        serverSocketChannel2.configureBlocking(false);
        serverSocketChannel3.configureBlocking(false);

        // 得到选择器
        Selector selector = Selector.open();

        // 将通道注册到选择器上
        serverSocketChannel1.register(selector, SelectionKey.OP_ACCEPT);
        serverSocketChannel2.register(selector, SelectionKey.OP_ACCEPT);
        serverSocketChannel3.register(selector, SelectionKey.OP_ACCEPT);

        while (true) {
            // Selector的select()方法 作用:查看是否有客户端连接
            int count = selector.select();

            // Selector的selectedKeys()方法
            // 此方法返回一个Set<SelectionKey>集合,表示:当前已连接的通道的集合。每个已连接通道同一封装为一个SelectionKey对象。
            // 取出已经连接的服务端Channel
            Set<SelectionKey> selectionKeys = selector.selectedKeys();

            Iterator<SelectionKey> iterator = selectionKeys.iterator();
            while (iterator.hasNext()) {
                SelectionKey selectionKey = iterator.next();
                ServerSocketChannel ssChannel = (ServerSocketChannel) selectionKey.channel();
                SocketChannel socketChannel = ssChannel.accept();// 同意客户端的请求
                System.out.println("我处理了ServerSocketChanner的连接: " + ssChannel);

                // 接收客户端发过来的数据
                ByteBuffer buffer = ByteBuffer.allocate(1024);
                int len = socketChannel.read(buffer); // 接收的数据保存buffer中,返回接收的个数
                System.out.println(ssChannel + ", 服务端收到: " + new String(buffer.array(), 0, len));
                // 已经连接的通道使用完毕必须删除,不然再次调用selectkeys的时候还是会迭代出来。
                iterator.remove();
            }
        }
    }
}

package demo08_了解NIO;

import java.io.IOException;
import java.net.InetSocketAddress;
import java.nio.ByteBuffer;
import java.nio.channels.SocketChannel;

/*
1.创建客户端,连接服务器
2.通过通道发数据
3.关闭资源
 */
public class CLient3006 {
    public static void main(String[] args) throws IOException {
        // 1.创建客户端,连接服务器
        SocketChannel socketChannel = SocketChannel.open();
        socketChannel.connect(new InetSocketAddress("127.0.0.1", 3006));

        // 2.通过通道发数据
        ByteBuffer buffer = ByteBuffer.allocate(1024);
        buffer.put("hello, 3006".getBytes());
        buffer.flip(); // 方便下次获取Buffer中的数据
        socketChannel.write(buffer);

        // 3.关闭资源
        socketChannel.close();
    }
}

package demo08_了解NIO;

import java.io.IOException;
import java.net.InetSocketAddress;
import java.nio.ByteBuffer;
import java.nio.channels.SocketChannel;
public class Client3007 {
    public static void main(String[] args) throws IOException {
        // 1.创建客户端,连接服务器
        SocketChannel socketChannel = SocketChannel.open();
        socketChannel.connect(new InetSocketAddress("127.0.0.1", 3007));

        // 2.通过通道发数据
        ByteBuffer buffer = ByteBuffer.allocate(1024);
        buffer.put("hello, ".getBytes());
        buffer.flip(); // 方便下次获取Buffer中的数据
        socketChannel.write(buffer);

        // 3.关闭资源
        socketChannel.close();
    }
}

# commons-io 工具包

commons-io 概述:

  • commons-io 是 apache 开源基金组织提供的一组有关 IO 操作的类库,可以挺提高 IO 功能开发的效率。
  • commons-io 工具包提供了很多有关 io 操作的类。有两个主要的类 FileUtils, IOUtils

FileUtils 主要有如下方法:

方法名 说明
String readFileToString(File file, String encoding) 读取文件中的数据, 返回字符串
void copyFile(File srcFile, File destFile) 复制文件。
void copyDirectoryToDirectory(File srcDir, File destDir) 复制文件夹。

commons-io 使用步骤:

  1. 导入 commons-io-2.6.jar
  2. 在项目中创建一个文件夹:lib
  3. 将 commons-io-2.6.jar 文件复制到 lib 文件夹
  4. 在 jar 文件上点右键,选择 Add as Library -> 点击 OK
  5. 在类中导包使用

IDEA导入jar.png
使用 commons-io 实现读文件、复制文件、复制文件夹的功能:

package demo09_commons_io使用;

import org.apache.commons.io.FileUtils;

import java.io.File;
import java.io.IOException;

public class Demo {
    public static void main(String[] args) throws IOException {
        //        读文件
        String s = FileUtils.readFileToString(new File("D:\\learn\\java\\code\\javaUp\\day12\\doc\\1.txt"), "utf-8");
        System.out.println(s);

        //        复制文件
        FileUtils.copyFile(new File("D:\\learn\\java\\code\\javaUp\\day12\\doc\\1.txt"), new File("D:\\learn\\java\\code\\javaUp\\day12\\doc\\2.txt"));

        //        复制文件夹
        FileUtils.copyDirectoryToDirectory(new File("D:\\learn\\java\\code\\javaUp\\day12\\doc"), new File("D:\\learn\\java\\code\\javaUp\\day12\\doc2"));
    }
}