安安静静学JAVA(十三)

网络基础

软件结构

  • C/S

    • 全称为Client/Server结构,是指客户端和服务器结构。常见程序有QQ、迅雷等软件。
  • B/C

    • 全称为Browser/Server结构,是指浏览器和服务器结构。常见浏览器有谷歌、火狐等。

网络编程意义

  • 在一定的协议下,实现两台计算机的通信的程序。

网络结构

  • 应用层

    • HTTP、FTP、TFTP、SMTP、SNMP、DNS
  • 传输层

    • TCP

      • 1.面向有连接, 数据安全, 速度慢
      • 2.三次握手:TCP协议中,在发送数据的准备阶段,客户端与服务器之间的三次交互,以保证连接的可靠。
      • 3.保证传输数据的安全,所以应用十分广泛,例如下载文件、浏览网页等。
    • UDP

      • 1.面向无连接, 数据不安全, 速度快(数据包大小有限制: 64K)
      • 2.它是不可靠协议,因为无连接,所以传输速度快,但是容易丢失数据。
      • 3.日常应用中,例如视频会议、QQ聊天等。
  • 网络层

    • ICMP、IGMP、IP、ARP、RARP
  • 数据链路层

  • 物理层

网络编程三要素

  • 协议

    • 计算机网络通信必须遵守的规则
  • IP地址

    • 指互联网协议地址(Internet Protocol Address),俗称IP(IP地址用来给一个网络中的计算机设备做唯一的编号)

    • 分类有IPV4、IPV6

      • IPV4

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

        • 为了扩大地址空间,拟通过IPv6重新定义地址空间,采用128位地址长度,每16个字节一组,分成8组十六进制数,表示成ABCD:EF01:2345:6789:ABCD:EF01:2345:6789,号称可以为全世界的每一粒沙子编上一个网址,这样就解决了网络地址资源数量不够的问题。
    • InetAddress : 表示IP地址, 主机名

public class Demo01 {
    public static void main(String[] args) throws UnknownHostException {

        // 获取本机
        InetAddress localHost = InetAddress.getLocalHost();
        System.out.println(localHost);

        System.out.println("主机名: " + localHost.getHostName());
        System.out.println("IP地址: " + localHost.getHostAddress());

        // 获取指定的IP地址
        InetAddress pyn = InetAddress.getByName("写IP地址");

        System.out.println(pyn.getHostName());
        System.out.println(pyn.getHostAddress());


    }
}
  • 端口号(0~65535)

    • 唯一标识设备中的进程(应用程序)

UDP

发送端

  • DatagramSocket()
  • DatagramPacket(byte[] arr, int len, InetAddress ip, int port)
  • send(DatagramPacket)

接收端

  • DatagramSocket(int port)
  • DatagramPacket(byte[] arr, int len)
  • receive(DatagramPacket)

发送端给接收端发送一句话

/**
 * 发送端
 */
public class SendTest {
    public static void main(String[] args) throws Exception {
        // 创建DatagramSocket
        DatagramSocket socket = new DatagramSocket();// 建议不指定端口号, 但是如果指定必须**不能**和接收端是一样的

        // 将字符串转换成字节数组, 就是要发送的数据
        byte[] arr = "人无远虑必有近忧".getBytes();

        // 获取发送给那个接收端的IP地址
        InetAddress address = InetAddress.getLocalHost();

        // 创建DatagramPacket
        DatagramPacket packet = new DatagramPacket(arr, arr.length, address, 8888);

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

        // 释放资源
        socket.close();
    }
}
/**
 * 接收端
 */
public class ReceiveTest {
    public static void main(String[] args) throws Exception{

        // 创建DatagramSocket, 指定当前接收端的端口号
        DatagramSocket socket = new DatagramSocket(8888);

        // 创建字节数组
        byte[] arr = new byte[1024];

        // 创建DatagramPacket
        DatagramPacket packet = new DatagramPacket(arr, arr.length);

        // 接收数据
        socket.receive(packet); // 接收端接收到的数据, 最终介绍到字节数组中


        InetAddress inetAddress = packet.getAddress();
        String name = inetAddress.getHostName();
        String address = inetAddress.getHostAddress();

        // 获取接收到字节的个数
        int length = packet.getLength();

        // 查看接收到的数据
        // 将字节数组, 转换成字符串 -> 打印
        System.out.println("收到了ip: " + address + ", 主机名: " + name + ", 发送过来的数据: " + new String(arr, 0, length));

        // 释放资源
        socket.close();

    }
}

群聊

//不断接收数据
public class ReceiveAllTest {
    public static void main(String[] args) throws Exception{

        DatagramSocket socket = new DatagramSocket(8888);

        // 创建用来接收数据的字节数组
        byte[] arr = new byte[1024 * 8];

        DatagramPacket packet = new DatagramPacket(arr, arr.length);

        while (true) {
            // 接收数据
            socket.receive(packet);
            // 获取发送人的ip地址和主机名
            InetAddress ip = packet.getAddress();
            String name = ip.getHostName();
            String address = ip.getHostAddress();

            // 数据存放在arr数组中, 所以打印arr数组中的内容
            System.out.println("收到了ip: " + address + ", 主机名: " + name + ", 发送过来的数据: " + new String(arr));
        }

        // socket.close();

    }
}
  • 一个接收端, 对应多个发送端

TCP

客户端

  • Socket(String ip, int port)

    • 指定连接服务器的IP地址和端口号
    • getInputStream()
    • getOutputStream()

服务器

  • ServerSocket(int port)

    • 创建服务器, 指定服务器自己的端口号

    • Socket accept()

      • 接收客户端的连接, 并获取Socket

客户端给服务器写出一句话

/**
 * 客户端 : 给服务器发送一句话
 */
public class ClientTest {
    public static void main(String[] args) throws IOException {
        // 创建Socket对象, 并且指定服务器的IP地址和端口号
        Socket socket = new Socket("127.0.0.1", 8888);

        // 获取和服务器连接的输出流
        OutputStream os = socket.getOutputStream();
        // 写出数据
        os.write("汝妻子吾养也, 汝勿虑之!".getBytes());

        // 释放资源
        os.close();
        socket.close();
    }
}
/**
 * 服务器 : 接收客户端发送过来的数据
 */
public class ServerTest {
    public static void main(String[] args) throws IOException {
        // 创建ServerSocket对象, 指定自己的端口号
        ServerSocket ss = new ServerSocket(8888);
        System.out.println("【服务器】正在等待客户端的连接...");
        // 接收客户端的连接
        Socket socket = ss.accept(); // 阻塞

        // 了解: 通过socket可以获取到客户端的信息
        String address = socket.getInetAddress().getHostAddress();
        System.out.println("【服务器】" + address + "连接成功");

        // 获取和客户端连接的输入流
        InputStream is = socket.getInputStream();

        // 创建字节数组
        byte[] arr = new byte[1024 * 8];
        // 读数据, 将读取到的数据, 存放到字节数组中
        // 返回读取到有效的字节个数
        int len = is.read(arr);

        // 打印读取到的数据
        System.out.println(new String(arr, 0, len));

        // 释放资源
        is.close();
        socket.close();
        ss.close();
    }
}

客户端和服务器互相说一句话

public class ClientTest {
    public static void main(String[] args) throws IOException {
        // 创建Socket对象, 并且指定服务器的IP地址和端口号
        Socket socket = new Socket("127.0.0.1", 8888);

        // 获取和服务器连接的输出流
        OutputStream os = socket.getOutputStream();
        // 写出数据
        os.write("汝妻子吾养也, 汝勿虑之!".getBytes());

        // ************客户端接收服务器的回写***************
        // 获取和服务器连接的 输入流
        InputStream is = socket.getInputStream();
        // 创建字节数组
        byte[] arr = new byte[1024 * 8];
        // 读取数据
        int len = is.read(arr);
        // 打印
        System.out.println(new String(arr, 0, len));
        // *********************************************

        // 释放资源
        is.close();
        os.close();
        socket.close();
    }
}
public class ServerTest {
    public static void main(String[] args) throws IOException {
        // 创建ServerSocket对象, 指定自己的端口号
        ServerSocket ss = new ServerSocket(Constant.MY_PORT);
        System.out.println("【服务器】正在等待客户端的连接...");
        // 接收客户端的连接
        Socket socket = ss.accept(); // 阻塞

        // 了解: 通过socket可以获取到客户端的信息
        String address = socket.getInetAddress().getHostAddress();
        System.out.println("【服务器】" + address + "连接成功");

        // 获取和客户端连接的输入流
        InputStream is = socket.getInputStream();

        // 创建字节数组
        byte[] arr = new byte[1024 * 8];
        // 读数据, 将读取到的数据, 存放到字节数组中
        // 返回读取到有效的字节个数
        int len = is.read(arr);

        // 打印读取到的数据
        System.out.println(new String(arr, 0, len));

        // ************服务器回写给客户端一句话*****************
        // 获取和客户端连接的输出流
        OutputStream os = socket.getOutputStream();
        // 写出一句话
        os.write("我谢谢你!".getBytes());
        // *************************************************

        // 释放资源
        os.close();
        is.close();
        socket.close();
        ss.close();
    }
}

综合练习

文件上传案例

  • 普通文件上传
/**
 * 文件上传案例的客户端
 */
public class UploadClient {
    public static void main(String[] args) throws IOException {
        // 创建和硬盘连接的输入流
        BufferedInputStream bis = new BufferedInputStream(new FileInputStream("day14-code\\test.txt"));
        // 创建Socket对象, 并指定服务器的IP地址和端口号
        Socket socket = new Socket("192.168.39.80", 8888);
        // 获取和服务器连接的输出流
        OutputStream os = socket.getOutputStream();
        // 高效的字节输出流
        BufferedOutputStream bos = new BufferedOutputStream(os);

        // 读写数据
        int b;
        while ((b = bis.read()) != -1) {
            bos.write(b);
        }

        // 释放资源
        bos.close();
        socket.close();
        bis.close();


    }
/**
 * 文件上传案例的服务器
 */
public class UploadServer {
    public static void main(String[] args) throws IOException {
        // 创建ServerSocket对象, 并指定服务器自己的端口号
        ServerSocket ss = new ServerSocket(8888);
        System.out.println("【服务器】正在等待连接...");
        // 接收客户端的连接
        Socket socket = ss.accept();
        System.out.println("【服务器】连接成功!~开始传输数据!");
        // 获取和客户端连接的输入流
        BufferedInputStream bis = new BufferedInputStream(socket.getInputStream());

        // 获取和硬盘连接的输出流
        BufferedOutputStream bos = new BufferedOutputStream(new FileOutputStream("g:\\upload\\a.txt"));

        int b;
        while ((b = bis.read()) != -1) {
            bos.write(b);
        }

        System.out.println("【服务器】文件上传成功!~");

        // 释放资源
        bos.close();
        bis.close();
        socket.close();
        ss.close();


    }
}
  • 改进一

    • 文件名
    • while(true)
//使用UUID保证文件名字不重复
public class Demo02_UUID {
    public static void main(String[] args) {

        for (int i = 0; i < 10; i++) {
            UUID uuid = UUID.randomUUID();
            String s = uuid.toString();
            System.out.println(s);
        }

    }
}

/**
 * 文件上传案例的客户端
 */
public class UploadClient {
    public static void main(String[] args) throws IOException {
        // 创建和硬盘连接的输入流
        BufferedInputStream bis = new BufferedInputStream(new FileInputStream("day14-code\\test.txt"));
        // 创建Socket对象, 并指定服务器的IP地址和端口号
        Socket socket = new Socket("192.168.39.80", Constant.MY_PORT);
        // 获取和服务器连接的输出流
        OutputStream os = socket.getOutputStream();
        // 高效的字节输出流
        BufferedOutputStream bos = new BufferedOutputStream(os);

        // 读写数据
        int b;
        while ((b = bis.read()) != -1) {
            bos.write(b);
        }

        // 释放资源
        bos.close(); // 由于代码指定到close, 写出了结束标记, 所以程序才可以正常执行
        socket.close();
        bis.close();
    }
}
/**
 * 文件上传案例的服务器
 */
public class UploadServer {
    public static void main(String[] args) throws IOException {
        // 创建ServerSocket对象, 并指定服务器自己的端口号
        ServerSocket ss = new ServerSocket(8888);

        while (true) {
            System.out.println("【服务器】正在等待连接...");
            // 接收客户端的连接
            Socket socket = ss.accept(); // 阻塞

            // 在接收客户端连接之后, 开启新线程
            // 新线程的任务就是去拷贝
            new Thread(() -> {
                try {
                    String address = socket.getInetAddress().getHostAddress();

                    System.out.println("【服务器】连接成功!~开始传输数据!");

                    // 获取和客户端连接的输入流
                    BufferedInputStream bis = new BufferedInputStream(socket.getInputStream());

                    // 字符串通过+的拼接, 以及StringBuilder的append方法
                    // 如果拼接引用数据类型, 会默认调用toString()方法
                    String path = "g:\\upload\\" + UUID.randomUUID() + ".jpg";

                    // 获取和硬盘连接的输出流
                    BufferedOutputStream bos = new BufferedOutputStream(new FileOutputStream(path));

                    int b;
                    while ((b = bis.read()) != -1) {
                        bos.write(b);
                    }

                    System.out.println("【服务器】文件上传成功!~");
                    System.out.println(address + "上传了文件" + path);

                    // 释放资源
                    bos.close();
                    bis.close();
                    socket.close();
                    // ss.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }).start();
        }
    }
}
  • 改进三

    • 开启线程

    • 回写

/**
 * 文件上传案例的客户端
 */
public class UploadClient {
    public static void main(String[] args) throws IOException {
        // 创建和硬盘连接的输入流
        BufferedInputStream bis = new BufferedInputStream(new FileInputStream("day14-code\\test.txt"));
        // 创建Socket对象, 并指定服务器的IP地址和端口号
        Socket socket = new Socket("192.168.39.80", Constant.MY_PORT);
        // 获取和服务器连接的输出流
        OutputStream os = socket.getOutputStream();
        // 高效的字节输出流
        BufferedOutputStream bos = new BufferedOutputStream(os);

        // 读写数据
        int b;
        while ((b = bis.read()) != -1) {
            bos.write(b);
        }

        // 如果代码中加了回写, 一定要加上下面两条语句
        // 刷缓冲区
        bos.flush();
        // 禁用此套接字(Socket)的输出流, 任何先前的数据将会被发送, 可以写出结束标记 -> shutdownOutput()
        // 不会清空缓冲区
        socket.shutdownOutput();

        // ****************接收回写**********************
        // 获取和服务器连接的输入流
        InputStream is = socket.getInputStream();

        byte[] arr = new byte[1024];

        System.out.println("111");
        int len = is.read(arr); // 读取, 阻塞
        System.out.println("222");

        // 打印
        System.out.println(new String(arr, 0, len));

        // *********************************************


        // 释放资源
        is.close();
        bos.close(); // 由于代码指定到close, 写出了结束标记, 所以程序才可以正常执行
        socket.close();
        bis.close();
    }
}
/**
 * 文件上传案例的服务器
 */
public class UploadServer {
    public static void main(String[] args) throws IOException {
        // 创建ServerSocket对象, 并指定服务器自己的端口号
        ServerSocket ss = new ServerSocket(Constant.MY_PORT);

        while (true) {
            System.out.println("【服务器】正在等待连接...");
            // 接收客户端的连接
            Socket socket = ss.accept(); // 阻塞

            // 在接收客户端连接之后, 开启新线程
            // 新线程的任务就是去拷贝
            new Thread(() -> {
                try {
                    String address = socket.getInetAddress().getHostAddress();

                    System.out.println("【服务器】连接成功!~开始传输数据!");

                    // 获取和客户端连接的输入流
                    BufferedInputStream bis = new BufferedInputStream(socket.getInputStream());

                    // 字符串通过+的拼接, 以及StringBuilder的append方法
                    // 如果拼接引用数据类型, 会默认调用toString()方法
                    String path = "g:\\upload\\" + UUID.randomUUID() + ".jpg";

                    // 获取和硬盘连接的输出流
                    BufferedOutputStream bos = new BufferedOutputStream(new FileOutputStream(path));

                    System.out.println("333");
                    int b;
                    while ((b = bis.read()) != -1) {  // 阻塞
                        bos.write(b);
                    }
                    System.out.println("444");

                    // *****************回写******************
                    // 获取和客户端连接的输出流
                    OutputStream os = socket.getOutputStream();
                    os.write("上传成功".getBytes());

                    // **************************************


                    // 释放资源
                    os.close();
                    bos.close();
                    bis.close();
                    socket.close();
                    // ss.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }).start();

        }
    }
}

模拟B/S

BIO

NIO

public class Client {
    public static void main(String[] args) throws IOException, InterruptedException {

        while (true) {
            Thread.sleep(1000);
            // 客户端通道
            SocketChannel channel = SocketChannel.open();
            // 连接
            channel.connect(new InetSocketAddress("127.0.0.1", 10000));
            // 写出
            channel.write(ByteBuffer.wrap("你好啊!~".getBytes()));
            // 释放资源
            channel.close();
        }


    }
}
public class Server {
    public static void main(String[] args) throws IOException {

        // 服务器通道, 只做接收客户端的连接
        ServerSocketChannel serverSocketChannel = ServerSocketChannel.open();
        // 将通道设置为非阻塞
        serverSocketChannel.configureBlocking(false);
        // 绑定端口号
        serverSocketChannel.bind(new InetSocketAddress(10000));

        // 创建多路复用器/ 选择器
        Selector selector = Selector.open();

        // 注册到一起, 指定接收新连接的操作
        serverSocketChannel.register(selector, SelectionKey.OP_ACCEPT);

        // 不断的轮询
        while (true) {
            // 得到操作集的操作个数
            int count = selector.select();
            // 判断, 个数>0
            if (count > 0) {
                // 获取操作集  key
                Set<SelectionKey> selectionKeys = selector.selectedKeys();
                // 涉及到要删除集合中的元素, 所以遍历方式使用 -> 迭代器
                Iterator<SelectionKey> it = selectionKeys.iterator();
                // 遍历
                while (it.hasNext()) {
                    SelectionKey key = it.next();
                    // 移除对应的操作
                    it.remove();
                    // 接收新连接
                    if (key.isAcceptable()) {
                        // 获取服务器通道, 用来接收连接
                        ServerSocketChannel ssc = (ServerSocketChannel) key.channel();
                        // 接收客户端的连接, 并获取客户端通道
                        SocketChannel channel = ssc.accept();
                        // 设置阻塞状态为非阻塞
                        channel.configureBlocking(false);
                        // 注册
                        channel.register(selector, SelectionKey.OP_READ);
                    } else if (key.isReadable()) {
                        // 读取数据
                        // 获取通道
                        SocketChannel channel = (SocketChannel) key.channel();

                        // 创建字节缓冲区
                        ByteBuffer byteBuffer = ByteBuffer.allocate(1024);

                        while (channel.read(byteBuffer) > 0) {
                            // 写 -> 读
                            byteBuffer.flip();
                            System.out.println(new String(byteBuffer.array()));
                            // 读 -> 写
                            byteBuffer.clear();
                        }

                        channel.close();

                    }

                }
            }

        }

    }
}

AIO

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值