01_网络编程_传统IO

news2024/12/26 11:01:20

网络编程

1.什么是网络编程

在网络通信协议下,不同计算机上运行的程序,进行的数据传输。

如果想把一个计算的结果,或者是电脑上的文件通过网络传递给你的朋友,就需要用到网络编程。

在实际生活中,网络通信无处不在

  • **应用场景:**即时通信、网游对战、金融证券、国际贸易、邮件、等等

不管是什么场景,都是 计算机计算机 之间通过网络进行数据传输。

  • Java中可以使用 java.net 包下的技术轻松开发出常见的网络应用程序。

目前市面上常见的软件架构:

  • B/S
  • C/S

在这里插入图片描述

在这里插入图片描述

不管是 B/S 架构,还是 C/S 架构,客户端/浏览器负责的仅仅是把数据展示出来,展示给用户去看。在项目中,真正核心的逻辑都是在服务器当中的。

BS架构的优缺点

  1. 不需要开发客户端,只需要页面 + 服务端
  2. 用户不需要下载,打开浏览器就能使用
  3. B/S 的特点主要突出一个:方便
  4. 如果应用过大,用户体验受到影响

CS架构的优缺点

  1. 画面可以做的非常精美,用户体验好(因为所有资源都被用户下载到了本地)
  2. 需要开发客户端,也需要开发服务端(针对公司而言,CS架构的开发、安装、部署、维护非常麻烦)
  3. 用户需要下载和更新的时候太麻烦

在这里插入图片描述

二者都有各自的优缺,具体用哪种,需要结合当前项目的特点来断定。一般而言,是类似于 LOL、王者荣耀这种游戏,它对于画面、音乐都有非常大的要求,就用 CS 架构;如果类似与新闻这种对画面没有太大要求的就可以使用 BS 架构。

在这里插入图片描述

2.网络编程三要素

两台计算机只要需要知道哪些东西才能进行数据传输呢?特别是,我想要给一堆电脑中的其中一台发送数据,需要知道哪些参数才可以呢?

  1. 确定对方电脑在互联网上的地址,这个地址是唯一的——IP
  2. 确定接收数据的软件,从微信->微信,而不是微信->qq,一个端口号只能被一个软件绑定——端口号
  3. 确定网络传输的规则,数据在传输的时候,我们不能随便书写它的格式,想怎么写就怎么写——协议

在这里插入图片描述

需要知道 IP、端口、协议,才能进行数据的传输。手机、IPAD、笔记本,都是可以上网的,只要上网,都需要有一个 IP,所以此处写的是设备。

在这里插入图片描述

在这里插入图片描述

IP

全程:Internet Protocol,是互联网协议地址,也称 IP 地址。是分配给上网设备的数字标签。

通俗理解

上网设备在网络中的地址,是唯一的

常见的IP分类为

IPV4、IPV6

IPV4

全称:Internet Protocol version 4,互联网通信协议第四版。

采用 32 位地址长度,分为 4 组

在这里插入图片描述

真实的 IP 是 32 bit 的二进制数

在这里插入图片描述

因为不好记也不好用,后来采用点分十进制表示法将 8 个 bit 作为一组转化为 10进制,总共分为4组,它们是无符号数,每一组的取值范围是 0 ~ 255。

IPV4 的缺点:在 IPV4 中总共只有不到 43 亿个 IP,IPV4 的数量是有限的,是不够使用的,2019年11月26日全部分配完毕。为了解决 IP 不够用的问题,出现了 IPV6。

IPV6

全称:Internet Protocol version 6,互联网通信协议第六版。

由于互联网的蓬勃发展,IP地址的需求量愈来愈大,而 IPV4 的模式下 IP 的总数是有限的。

IPV6 采用 128 位地址长度,分为 8 组,每一组是 16 个 bit。IPV6 可以给地球上每一粒沙子都定义一个 IP。

在这里插入图片描述
在这里插入图片描述

目前 IPV6 还未普及,在不久的将来,IPV6 会超越 IPV4,成为市场的主流。

在这里插入图片描述

IPV4已经分配完了,IPV6还未普及,目前如何解决IP不够用的问题?

IPV4的地址分类形式

  • 公网地址(万维网使用)和私有地址(局域网使用)。
  • 192.168. 开头的就是私有地址,范围为 192.168.0.0 ~ 192,168.255.255,专门为组织机构内部使用,以此节省 IP

在这里插入图片描述

在网吧中所有电脑共享同一个公网 IP,再由路由器给每一台电脑分配局域网 IP,这样就可以实现节约 IP 的效果。

特殊IP地址

127.0.0.1,也可以是 localhost: 是回送地址也称本地回环地址,也称本地本机 IP,永远只会寻找当前所在本机。

疑问: 假设 192.168.1.100 是我电脑的 IP,那么这个 IP 跟 127.0.0.1 是一样的吗?

它们是不一样的。

假设我们现在有一堆电脑,它们的 IP 都是由路由器分配的。

在这里插入图片描述

在这里插入图片描述

每一个路由器给设备分配的 IP 可能是不一样的,所以会有这么一个情况:当我们换了一个地方上网,我们上网设备的局域网 IP 有可能不一样。

但是,如果我们往 127.0.0.1 发送数据,那么它是不经过路由器的。我们的数据经过网卡的时候,网卡发现,我们要往 127.0.0.1 发送数据,此时,它就把这个数据给我们自己发过来了,不管在哪里上网,永远都是这样的。这就是两者的区别。

在这里插入图片描述

建议: 在练习的时候,如果我们是自己给自己发送数据,就写 127.0.0.1 就可以了。

在这里插入图片描述

常见 CMD 命令

  • ipconfig: 查看本机 IP 地址
  • ping: 检查网络是否连通,不仅可以检查局域网是否畅通,还可以检查外网是否畅通。在 ping 的后边,既可以跟随 IP,也可以跟随 网址。

网址的底层逻辑其实也是 ip

在这里插入图片描述

InetAddress 的使用

在Java中用来表示 IP 的类:InetAddress

在这里插入图片描述

在底层它会进行判断,我们用的是 6 版本的还是 4 版本的,如果是 4 版本的,它其实创建的是它的子类 Inet4Address 的对象并返回;如果是 6 版本的则会创建 Inet6Address 并返回。

在这里插入图片描述

由于该类没有对外暴露构造方法,所以我们需要通过它的构造方法 getByName 来获取它的对象。这个方法的底层其实就是判断是 IPV4 和 IPV6,判断完之后会创建对应的子类对象并返回。

public class MyInetAddressDemo1 {

    public static void main(String[] args) throws Exception {
        /**
         * static InetAddress getByName(String host) 确定主机名称的 ip 地址。主机名称可以是及其名称,也可以是 ip 地址
         * String getHostName()                      获取 IP 地址的主机名
         * String getHostAddress()                   返回文本显示中的 IP 地址字符串
         */

        // 1.获取 InetAddress 的对象
        // IP 的对象 ——> 一台电脑的对象
        InetAddress address = InetAddress.getByName("192.168.0.105");
        System.out.println("ipv4 address:" + address); // ipv4 address:/192.168.0.105
        // 主机名其实就是我们给自己电脑起的名字,如果没起也会有默认的 Win + E 打开我的电脑,右键 + 属性可以查看
        InetAddress address2 = InetAddress.getByName("DESKTOP-V32JVCD");
        System.out.println("hostname:" + address2); // hostname:DESKTOP-V32JVCD/192.168.0.105

        // getHostName 方法的小细节:如果电脑因为网络原因或者局域网中压根就没有这台电脑,此时获取不到主机名,此时以 IP 的形式进行展示
        String hostName = address.getHostName();
        System.out.println(hostName); // 192.168.0.105
        System.out.println(address2.getHostName()); // DESKTOP-V32JVCD

        String hostAddress = address.getHostAddress();
        System.out.println(hostAddress); // 192.168.0.105
        System.out.println(address2.getHostAddress()); // 192.168.0.105

        /**
         * 这只是一个前置的代码,一旦我们获取到 IP 之后,我们就可以给某一台电脑发送消息了
         */
    }
}

端口号

应用程序在设备中唯一的标识

端口号:由两个字节表示的整数,取值范围:0~65535

​ 其中 0 ~ 1023 之间的端口号用于一些知名的网络服务或者应用

​ 我们自己使用 1024 以上的端口号就可以了。

注意:一个端口号只能被一个应用程序使用。

端口其实就是电脑往外发送数据的出口,或者说是电脑接收外部数据的入口。
在这里插入图片描述

软件一运行就要绑定一个端口,如果没有绑定端口,那么它就是单击的,是无法往外发送数据/接收数据的。

协议

在计算机网络中,连接和通信的规则被称为网络通信协议。协议就是数据传输的规则

在传输数据的时候,国际标准组织定义了一个 OSI 的网络参考模型,把传输数据分成了7层

  • OSI 参考模型:世界互联协议标准,全球通信规范,单模型过于理想化,未能在因特网上进行广泛推广。
  • TCP/IP 参考模型(或 TCP/IP 协议):事实上的国际标准。

在这里插入图片描述

代码运行在应用层,如果我们想给对方电脑发送数据,则会一层一层的往下,到达物理层,然后转化为二进制数据,再发送给对方电脑。对方电脑接收到数据之后会进行解析,再一层一层的传输给最上方的应用层,我们的程序就可以接收到数据了。

在这里插入图片描述
在这里插入图片描述

UDP 协议

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

  • UDP 是 面向无连接 通信协议。

    速度快,有大小限制一次最多发送 64 k,数据不安全,易丢失数据

面向无连接:UDP 协议传输数据时,不会管两台设备之间的网络是否畅通,直接发,你能收到就收到,收不到就拉倒。

UDP 适用于丢失一点数据不会产生任何影响的场景,比如:网络会议、语言通话、在线视频

在这里插入图片描述

TCP 协议

  • 传输控制协议 TCP(Transmission Control Protocol)

  • TCP 协议是 面向连接 的通信协议。

    速度慢,没有大小限制,数据安全。

TCP 发送数据之前会先检查两台设置之间的网络畅通。简单来说,就是确保连接成功才会发送数据。

TCP 适用于对数据有非常大的需求,一点都不能丢的情况。比如:下载软件、文字聊天、发送邮件

在这里插入图片描述

3.UDP通信程序

UDP 通信程序(发送数据)

在这里插入图片描述

在这里插入图片描述

UDP 协议发送数据

public class SendMessageDemo {

    public static void main(String[] args) throws Exception {
        // 发送数据

        // 1.创建 DatagramSocket对象(快递公司)
        // 细节:
        // 绑定端口:以后我们就是通过这个端口往外发送数据
        // 空参:所有可用的端口中随机一个进行使用
        // 有参:指定端口号进行绑定
        // DatagramSocket socket = new DatagramSocket(8000);
        DatagramSocket socket = new DatagramSocket();

        // 2.打包数据
        String str = "你好威猛啊!!!";
        byte[] bytes = str.getBytes();
        InetAddress address = InetAddress.getByName("127.0.0.1");
        int port = 10086;

        DatagramPacket dp = new DatagramPacket(bytes, bytes.length, address, port);

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

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

UDP 通信程序(接收数据)

在这里插入图片描述

在这里插入图片描述

注意:一定得先运行接收端再运行发送端

public class ReceiveMessageDemo {

    public static void main(String[] args) throws Exception {
        // 接收数据
        // 注意:一定得先运行接收端再运行发送端

        // 1.创建DatagramSocket对象(快递公司)
        // 细节:
        // 在接收的时候,一定要绑定端口
        // 而且绑定的端口,一定要跟发送的端口保持一直
        DatagramSocket socket = new DatagramSocket(10086);


        // 2.接收数据包,既然此处是用来接收数据的,所以只需要传一个数组即可,IP 和 端口就不用了
        byte[] bytes = new byte[1024];
        DatagramPacket packet = new DatagramPacket(bytes, bytes.length);
        
        // 该方法是阻塞的
        // 程序执行到这一步的时候,会在这里死等
        // 等发送端发送消息
        socket.receive(packet);

        // 3.解析数据包
        byte[] data = packet.getData();
        int length = packet.getLength();
        InetAddress address = packet.getAddress();
        int port = packet.getPort();

        System.out.println("接收到的数据" + new String(data, 0, length));
        System.out.println("该数据是从" + address + "这台电脑中的" + port + "端口发送过来的");

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

从哪个端口发出无所谓,只要发送的 ip + port 指向接收端即可。

练习:聊天室

在这里插入图片描述

public class SendMessageChat {

    public static void main(String[] args) throws Exception {

        DatagramSocket socket = new DatagramSocket(7070);
        Scanner scanner = new Scanner(System.in);
        while (true) {
            System.out.println("请输入您要说的话");
            String next = scanner.nextLine();
            byte[] bytes = next.getBytes();
            DatagramPacket packet = new DatagramPacket(bytes, bytes.length, InetAddress.getByName("127.0.0.1"), 7071);
            socket.send(packet);
            if (next.equals("886")) {
                break;
            }
        }
        System.out.println("退出发送端");
        socket.close();
    }
}
public class ReceiveMessageChat {

    public static void main(String[] args) throws Exception {
        DatagramSocket socket = new DatagramSocket(7071);
        // 每次接收到数据都会对 bytes 进行覆盖
        byte[] bytes = new byte[1024];
        DatagramPacket packet = new DatagramPacket(bytes, bytes.length);
        while (true) {
            socket.receive(packet);
            System.out.println(packet.getAddress().getHostAddress() + ":" + packet.getPort() + "的设备发来数据:" + new String(packet.getData(), 0, packet.getLength()));
        }
    }
}

允许某个类可以允许多个

在这里插入图片描述

UDP 的三种通信方式

  1. 单播:一对一,左边的发送端只给右边的一台设备发送消息

    以前的代码就是单播

    在这里插入图片描述

  2. 组播:可以给一组设备发送消息

    组播地址:224.0.0.0 ~ 239.255.255.255

    ​ 其中 244.0.0.0 ~ 244.0.0.255 为预留的组播地址(我们自己想用只能用这个范围之内的)

    之前的 ip 只能表示一台电脑,而这里随便一个组播地址就可以表示多台电脑

    在这里插入图片描述

    组播发送端代码

    public class SendMessageDemo {
    
        public static void main(String[] args) throws Exception {
    
            /**
             * 组播发送代码
             */
    
            // 通过 MulticastSocket 对象
            MulticastSocket ms = new MulticastSocket();
    
            // 创建 DatagramPacket 对象
            String str = "你好,你好!";
            byte[] bytes = str.getBytes();
            // 发送时指定 IP 地址一定要指定组播地址
            InetAddress address = InetAddress.getByName("224.0.0.1");
            int port = 10000;
    
            DatagramPacket datagramPacket = new DatagramPacket(bytes, bytes.length, address, port);
    
            // 调用 MulticastSocket 发送数据方法发送数据
            ms.send(datagramPacket);
    
            // 释放资源
            ms.close();
        }
    }
    

    组播接收端代码

    public class ReceiveMessageDemo1 {
    
        public static void main(String[] args) throws Exception {
    
            /**
             * 组播接收端代码
             */
    
            // 1.创建 MulticastSocket 对象
            MulticastSocket ms = new MulticastSocket(10000);
    
            // 2.将当前本机,添加到 224.0.0.1 的这一组当中
            InetAddress address = InetAddress.getByName("224.0.0.1");
            ms.joinGroup(address);
    
            // 3.创建 DatagramPacket 数据包对象
            byte[] bytes = new byte[1024];
            DatagramPacket dp = new DatagramPacket(bytes, bytes.length);
    
            // 4.接收数据
            ms.receive(dp);
    
            // 5.解析数据
            byte[] data = dp.getData();
            int len = dp.getLength();
            String ip = dp.getAddress().getHostAddress();
            String name = dp.getAddress().getHostName();
    
            System.out.println("ip 为:" + ip + ",主机名为:" + name + "的人,发送了数据:" + new String(data, 0, len));
    
            // 6. 释放资源
            ms.close();
        }
    }
    
  3. 广播:可以给局域网中所有设备发送消息

    广播地址:255.255.255

    在这里插入图片描述

广播代码几乎和单播一模一样,只需要修改 ip 地址为 255.255.255.255 即可

广播发送端

public class SendMessageDemo {

    public static void main(String[] args) throws Exception {
        // 发送数据

        // 1.创建 DatagramSocket对象(快递公司)
        // 细节:
        // 绑定端口:以后我们就是通过这个端口往外发送数据
        // 空参:所有可用的端口中随机一个进行使用
        // 有参:指定端口号进行绑定
        // DatagramSocket socket = new DatagramSocket(8000);
        DatagramSocket socket = new DatagramSocket();

        // 2.打包数据
        String str = "你好威猛啊!!!";
        byte[] bytes = str.getBytes();
        InetAddress address = InetAddress.getByName("255.255.255.255");
        int port = 10000;

        DatagramPacket dp = new DatagramPacket(bytes, bytes.length, address, port);

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

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

广播接收端:

public class ReceiveMessageDemo {

    public static void main(String[] args) throws Exception {
        // 接收数据
        // 注意:一定得先运行接收端再运行发送端

        // 1.创建DatagramSocket对象(快递公司)
        // 细节:
        // 在接收的时候,一定要绑定端口
        // 而且绑定的端口,一定要跟发送的端口保持一直
        DatagramSocket socket = new DatagramSocket(10000);


        // 2.接收数据包,既然此处是用来接收数据的,所以只需要传一个数组即可,IP 和 端口就不用了
        byte[] bytes = new byte[1024];
        DatagramPacket packet = new DatagramPacket(bytes, bytes.length);
        socket.receive(packet);

        // 3.解析数据包
        byte[] data = packet.getData();
        int length = packet.getLength();
        InetAddress address = packet.getAddress();
        int port = packet.getPort();

        System.out.println("接收到的数据" + new String(data, 0, length));
        System.out.println("该数据是从" + address + "这台电脑中的" + port + "端口发送过来的");

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

4.TCP通信程序

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

通信之前要保证连接已经建立

通过 Socket 产生 IO 流来进行网络通信

在这里插入图片描述

TCP 发送数据

public class Client {

    public static void main(String[] args) throws Exception {
        // TCP 协议,发送数据

        // 1.创建 Socket 对象
        // 细节:在创建对象的同时会连接服务端,如果连接不上,代码会报错
        Socket socket = new Socket("127.0.0.1", 10000);

        // 2.可以从连接通道中获取输入流
        OutputStream os = socket.getOutputStream();
        // 写出数据
        os.write("你好你好".getBytes()); // 字节流输出时只能输出字节数据

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

TCP 读取数据

public class Server {

    public static void main(String[] args) throws Exception {
        // TCP 协议,接收数据

        // 1.创建对象 ServerSocket
        // 注意:创建对象时一定要绑定一个端口,而且此端口一定要跟客户端连接的端口保持一致
        // 如果不一致的话,客户端还是连接不上
        ServerSocket serverSocket = new ServerSocket(10000);

        // 2.监听客户端的连接
        Socket socket = serverSocket.accept();

        // 3.从连接通道中获取输入流读取数据
        /*InputStream is = socket.getInputStream();
        // 结局中文乱码问题:写的时候是把一个中文字符分为3个字节来输出,读的时候需要一个字符一个字符的读
        InputStreamReader isr = new InputStreamReader(is);*/
        BufferedReader br = new BufferedReader(new InputStreamReader(socket.getInputStream()));

        int buff;
        while ((buff = br.read()) != -1) {
            System.out.print((char) buff);
        }

        // 4.释放资源
        br.close();
        // 断开于客户端之间的连接
        socket.close();
        // 关闭服务器
        serverSocket.close();
    }
}

在这里插入图片描述

TCP 通信程序(三次握手)

ICMP协议

在这里插入图片描述

TCP 通信程序(四次挥手)

ICMP协议

四次回收必须等待服务端把连接通道里的数据处理完毕了连接才能断开

在这里插入图片描述

在这里插入图片描述

5.综合练习

TCP通信练习1 — 多发多收

客户端:多次发送数据

public class Client {

    public static void main(String[] args) throws Exception {
        // 客户端:多次发送数据
        // 服务器:接收多次接收数据,并打印

        // 1.创建 Socket 对象并连接服务端
        Socket socket = new Socket("127.0.0.1", 10000);

        // 2.写出数据
        OutputStream outputStream = socket.getOutputStream();
        Scanner sc = new Scanner(System.in);
        while (true) {
            System.out.println("请输入您要发送的信息");
            String msg = sc.nextLine();
            if (msg.equals("886")) {
                break;
            }
            outputStream.write(msg.getBytes());
        }
        System.out.println("客户端下线!");

        // 3.释放资源
        sc.close();
        socket.close();
    }
}

服务端:接收多次接收数据,并打印

public class Server {

    public static void main(String[] args) throws Exception {
        // 客户端:多次发送数据
        // 服务器:接收多次接收数据,并打印

        // 1. 创建对象绑定 10000 端口
        ServerSocket serverSocket = new ServerSocket(10000);

        // 2.等待客户端连接
        Socket socket = serverSocket.accept();

        // 3.读取数据
        BufferedReader br = new BufferedReader(new InputStreamReader(socket.getInputStream()));
        int buff;
        while ((buff = br.read()) != -1) {
            System.out.print((char) buff);
        }

        // 4.释放资源
        socket.close();
        serverSocket.close();
    }
}

TCP通信练习2 — 接收和反馈

客户端:发送一条数据,接收服务端反馈的消息并打印

public class Client {

    public static void main(String[] args) throws Exception {
        // 客户端:发送一条数据,接收服务端反馈的消息并打印
        // 服务端:接收数据并打印,再给客户端反馈消息

        // 1.创建 Socket 对象连接服务器
        Socket socket = new Socket("127.0.0.1", 10000);

        // 2.写出数据
        String str = "见到你很高兴!";
        OutputStream os = socket.getOutputStream();
        os.write(str.getBytes());
        
        // 写出一个结束标记
        socket.shutdownOutput(); // 结束输出流

        // 3.接收服务端回写的数据
        BufferedReader br = new BufferedReader(new InputStreamReader(socket.getInputStream()));
        int b;
        while ((b = br.read()) != -1) {
            System.out.print((char) b);
        }

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

服务端:接收数据并打印,再给客户端反馈消息

public class Server {

    public static void main(String[] args) throws Exception {
        // 客户端:发送一条数据,接收服务端反馈的消息并打印
        // 服务端:接收数据并打印,再给客户端反馈消息

        // 1.创建对象并绑定 10000 端口
        ServerSocket ss = new ServerSocket(10000);

        // 2.等待客户端连接
        Socket socket = ss.accept();

        // 3.socket中获取输入流读取数据
        BufferedReader br = new BufferedReader(new InputStreamReader(socket.getInputStream()));
        int b;
        /**
         * 细节:
         * read 方法会从连接通道中读取数据
         * 但是,需要有一个结束标记,此处的循环才会停止
         * 否则,程序就会一直停在 read 方法这里,等待读取下面的数据
         */
        while ((b = br.read()) != -1) {
            System.out.print((char) b);
        }

        // 4.回写数据
        String str = "到底有多开心?";
        OutputStream os = socket.getOutputStream();
        os.write(str.getBytes());

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

TCP通信练习3 — 上传文件

客户端:将本地文件上传到服务器。接收服务器的反馈。

public class Client {

    public static void main(String[] args) throws Exception {
        // 客户端:将本地文件上传到服务器。接收服务器的反馈。
        //服务器:接收客户端上传的文件,上传完毕之后给出反馈。

        // 1.创建 Socket 对象连接服务器
        Socket socket = new Socket("127.0.0.1", 10000);

        // 2.读取本地文件中的数据,并写到服务器中
        BufferedInputStream bis = new BufferedInputStream(new FileInputStream("./clientdir/a.png"));
        BufferedOutputStream bos = new BufferedOutputStream(socket.getOutputStream());
        byte[] bytes = new byte[1024];
        int len;
        while ((len = bis.read(bytes)) != -1) {
            bos.write(bytes, 0, len);
        }

        // 输出文件结束符
        socket.shutdownOutput();

        // 回写数据
        BufferedReader br = new BufferedReader(new InputStreamReader(socket.getInputStream()));
        System.out.println(br.readLine());

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

服务器:接收客户端上传的文件,上传完毕之后给出反馈。

public class Server {

    public static void main(String[] args) throws Exception {
        // 客户端:将本地文件上传到服务器。接收服务器的反馈。
        //服务器:接收客户端上传的文件,上传完毕之后给出反馈。

        // 1.创建对象并绑定 10000 端口
        ServerSocket ss = new ServerSocket(10000);

        // 2.等待客户端连接
        Socket socket = ss.accept();

        // 3.读取数据并保存到本地文件中
        BufferedInputStream bis = new BufferedInputStream(socket.getInputStream());
        BufferedOutputStream bos = new BufferedOutputStream(new FileOutputStream("./serverdir/b.png"));
        byte[] bytes = new byte[1024];
        int len;
        while ((len = bis.read(bytes)) != -1) {
            bos.write(bytes, 0, len);
        }

        // 回写数据
        BufferedWriter bw = new BufferedWriter(new OutputStreamWriter(socket.getOutputStream()));
        bw.write("上传成功!");
        bw.newLine();
        bw.flush();

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

在这里插入图片描述

TCP通信练习4 — 上传文件(文件名重复问题)

解决上一题文件名重复问题

客户端不变,服务端输出文件时文件名使用 UUID 生成

public class Server {

    public static void main(String[] args) throws Exception {
        // 客户端:将本地文件上传到服务器。接收服务器的反馈。
        //服务器:接收客户端上传的文件,上传完毕之后给出反馈。

        // 1.创建对象并绑定 10000 端口
        ServerSocket ss = new ServerSocket(8000);

        // 2.等待客户端连接
        Socket socket = ss.accept();

        // 3.读取数据并保存到本地文件中
        BufferedInputStream bis = new BufferedInputStream(socket.getInputStream());
        String name = UUID.randomUUID().toString().replace("-", "") + ".png";
        BufferedOutputStream bos = new BufferedOutputStream(new FileOutputStream("./serverdir/" + name));
        byte[] bytes = new byte[1024];
        int len;
        while ((len = bis.read(bytes)) != -1) {
            bos.write(bytes, 0, len);
        }

        // 回写数据
        BufferedWriter bw = new BufferedWriter(new OutputStreamWriter(socket.getOutputStream()));
        bw.write("上传成功!");
        bw.newLine();
        bw.flush();

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

TCP通信练习5 — 上传文件(多线程版)

想要服务器不停止,能接收很多用户上传的图片。

该怎么做呢?

提示:可以用循环或者多线程。

但是循环不合理,最优解法是多线程改写。

public class Server {

    public static void main(String[] args) throws Exception {
        // 客户端:将本地文件上传到服务器。接收服务器的反馈。
        // 服务器:接收客户端上传的文件,上传完毕之后给出反馈。

        // 1.创建对象并绑定 10000 端口
        ServerSocket ss = new ServerSocket(8000);

        while (true) {
            // 2.等待客户端连接
            Socket socket = ss.accept();

            // 开启一条线程
            // 一个用户就对应服务端的一条线程
            new Thread(new MyRunnable(socket)).start();
        }
    }

    static class MyRunnable implements Runnable {

        private Socket socket;

        public MyRunnable(Socket socket) {
            this.socket = socket;
        }

        @Override
        public void run() {
            try {
                // 3.读取数据并保存到本地文件中
                BufferedInputStream bis = new BufferedInputStream(socket.getInputStream());
                String name = UUID.randomUUID().toString().replace("-", "") + ".png";
                BufferedOutputStream bos = new BufferedOutputStream(new FileOutputStream("serverdir/" + name));
                byte[] bytes = new byte[1024];
                int len;
                while ((len = bis.read(bytes)) != -1) {
                    bos.write(bytes, 0, len);
                }

                // 回写数据
                BufferedWriter bw = new BufferedWriter(new OutputStreamWriter(socket.getOutputStream()));
                bw.write("上传成功!");
                bw.newLine();
                bw.flush();

                // 4.释放资源
                bos.close();
            } catch (IOException e) {
                throw new RuntimeException(e);
            } finally {
                if (null != socket) {
                    try {
                        socket.close();
                    } catch (IOException e) {
                        throw new RuntimeException(e);
                    }
                }
            }
        }
    }
}

TCP通信练习6 — 上传文件(线程池版)

频繁创建并销毁线程非常浪费系统资源,所以需要用线程池优化。

public class Server {

    public static void main(String[] args) throws Exception {
        // 客户端:将本地文件上传到服务器。接收服务器的反馈。
        // 服务器:接收客户端上传的文件,上传完毕之后给出反馈。

        // 创建线程池对象
        ThreadPoolExecutor pool = new ThreadPoolExecutor(
                3, // 核心线程数
                16, // 线程池总大小
                60, // 空闲时间
                TimeUnit.SECONDS, // 空闲时间单位
                new ArrayBlockingQueue<>(2), // 阻塞队列
                Executors.defaultThreadFactory(), // 线程工厂:让线程池如何创建线程对象
                new ThreadPoolExecutor.AbortPolicy() // 拒绝策略
        );

        // 1.创建对象并绑定 10000 端口
        ServerSocket ss = new ServerSocket(8000);

        while (true) {
            // 2.等待客户端连接
            Socket socket = ss.accept();

            // 开启一条线程
            // 一个用户就对应服务端的一条线程
            pool.submit(new MyRunnable(socket));
        }
    }

    static class MyRunnable implements Runnable {

        private Socket socket;

        public MyRunnable(Socket socket) {
            this.socket = socket;
        }

        @Override
        public void run() {
            try {
                // 3.读取数据并保存到本地文件中
                BufferedInputStream bis = new BufferedInputStream(socket.getInputStream());
                String name = UUID.randomUUID().toString().replace("-", "") + ".png";
                BufferedOutputStream bos = new BufferedOutputStream(new FileOutputStream("serverdir/" + name));
                byte[] bytes = new byte[1024];
                int len;
                while ((len = bis.read(bytes)) != -1) {
                    bos.write(bytes, 0, len);
                }

                // 回写数据
                BufferedWriter bw = new BufferedWriter(new OutputStreamWriter(socket.getOutputStream()));
                bw.write("上传成功!");
                bw.newLine();
                bw.flush();

                // 4.释放资源
                bos.close();
            } catch (IOException e) {
                throw new RuntimeException(e);
            } finally {
                if (null != socket) {
                    try {
                        socket.close();
                    } catch (IOException e) {
                        throw new RuntimeException(e);
                    }
                }
            }
        }
    }
}

TCP通信练习7 — BS(接收浏览器的消息并打印)

客户端:不需要写

服务端:接收数据并打印

public class Server {

    public static void main(String[] args) throws Exception {
        // 服务端:BS架构->接收浏览器的消息并打印

        // 1.创建对象并绑定 10000 端口
        ServerSocket ss = new ServerSocket(8000);

        // 2.等待客户端连接
        Socket socket = ss.accept();

        // 3.socket中获取输入流读取数据
        BufferedReader br = new BufferedReader(new InputStreamReader(socket.getInputStream()));
        int b;
        while ((b = br.read()) != -1) {
            System.out.print((char) b);
        }

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

浏览器直接请求 127.0.0.1:8000 即可看到控制台输出:

GET / HTTP/1.1
Host: 127.0.0.1:8000
Connection: keep-alive
sec-ch-ua: "Chromium";v="116", "Not)A;Brand";v="24", "Microsoft Edge";v="116"
sec-ch-ua-mobile: ?0
sec-ch-ua-platform: "Windows"
Upgrade-Insecure-Requests: 1
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/116.0.0.0 Safari/537.36 Edg/116.0.1938.76
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.7
Sec-Fetch-Site: none
Sec-Fetch-Mode: navigate
Sec-Fetch-User: ?1
Sec-Fetch-Dest: document
Accept-Encoding: gzip, deflate, br
Accept-Language: zh-CN,zh;q=0.9,en;q=0.8,en-GB;q=0.7,en-US;q=0.6

TCP通信练习8 — 网络编程(课后大作业)

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1015558.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

javabasic

计算机基础 一、学前知识 1. 计算机组成 2. 进制的概念 &#xff08;1&#xff09;二进制的存储 计算机不管是内存还是硬盘&#xff0c;都是以二进制的形式存储。如同一张巨大的表格&#xff0c;里面都是由0和1组成。 二进制&#xff1a;逢2进1 基数为2&#xff0c;数值部分…

DP读书:《openEuler操作系统》(二)操作系统的发展史

操作系统的发展历史 操作系统的发展历史手工操作时代批处理系统多道程序系统分时操作系统CTSSMULTICS的历史UNIX和Linux的历史Debian系列Red Hat系列 DOS和Windows的历史DOS的历史&#xff1a;Windows的历史&#xff1a; Android和iOS的历史Android&#xff1a;iOS&#xff1a;…

MCU软核 3. Xilinx Artix7上运行cortex-m3软核

0. 环境 - win10 vivado 2018.3 keil mdk - jlink - XC7A35TV12 1. 下载资料 https://keilpack.azureedge.net/pack/Keil.V2M-MPS2_DSx_BSP.1.1.0.pack https://gitee.com/whik/cortex_m3_on_xc7a100t 2. vivado 2018 Create Project -> Next -> -> Project n…

web安全漏洞-SQL注入攻击实验

实验目的 学习sql显注的漏洞判断原理掌握sqlmap工具的使用分析SQL注入漏洞的成因 实验工具 sqlmap是用python写的开源的测试框架&#xff0c;支持MySQL&#xff0c;Oracle&#xff0c;PostgreSQL&#xff0c;Microsoft SQL Server&#xff0c;Microsoft Access&#xff0c;I…

2023微信头像生成小程序源码/基于skyline+vue3+t

微信头像生成小程序&#xff0c;基于skylinevue3t该项目已适配微信小程序隐私政策&#xff0c;截图、获取头像等诸多新的接口 如何使用&#xff1a; 将代码导入到HBuilderX开发工具&#xff0c;可无需修改任何代码&#xff0c;运行并编译到微信开发者工具 skyline需要使用微…

C++ 的继任者:Carbon Language | 开源日报 0911

carbon-language/carbon-lang Stars: 31.1k License: NOASSERTION Carbon Language 是一个实验性的 C 继任者项目。它旨在提供与 C 相当的性能&#xff0c;并具有低级访问位和地址的功能&#xff0c;以及与现有 C 代码进行互操作、快速可扩展构建等特点。该项目还强调了以下核…

io概述及其分类

一、IO概念 • I/O 即输入Input/ 输出Output的缩写&#xff0c;其实就是计算机调度把各个存储中&#xff08;包括内存和外部存储&#xff09;的数据写入写出的过程&#xff1b; I : Input O : Output 通过IO可以完成硬盘文件的读和写。 • java中用“流&#xff08;stream&am…

太牛啦!Python入门到精通 只用了3个多月

最近 GitHub 热榜上持续有一个这样的 Python 项目&#xff0c;自称「100天从新手到大师」。目前 Star 数量已有 19000 多。 作者在开头列出了 Python 的一些应用领域 作者的计划目录 Day01~15 - Python语言基础 Day16~20 - Python语言进阶 Day21~30 - Web前端入门 Day31~3…

基于RuoYi-Flowable-Plus的ruoyi-nbcio项目的formdesigner文件上传与回显处理

更多ruoyi-nbcio功能请看演示系统 gitee源代码地址 前后端代码&#xff1a; https://gitee.com/nbacheng/ruoyi-nbcio 本节说明ruoyi-nbcio项目的formdesigner文件上传与回显处理&#xff0c;不过目前还只实现oss的formdesigner的文件上传与回显功能。 1、前端方面 formdesigne…

IDEA中Debug测试的基本使用

Debug简介 Debug是用来追踪代码&#xff0c;通常在程序运行中出现异常的时候启动debug模式可以分析定位异常发生的位置&#xff0c;以及在运行过程中参数的变化&#xff0c;通常我们也可以启动Debug模式来跟踪代码的运行流程去学习三方框架的源码 Debug与Junit的区别 Debug与JU…

力扣236 补9.14

做不来&#xff0c;我做中等题基本上都是没有思路&#xff0c;这里需要先遍历祖先节点&#xff0c;那必然用先序遍历&#xff0c;这题还是官方题解容易理解&#xff0c;第二火的题解反而把我弄得脑袋昏昏的。 class Solution { TreeNode ans; public TreeNode lowestCommonAnce…

私域流量的优势

私域流量是指由自身品牌或个人拥有并具备完全掌控权的流量资源。它相比于传统的广告推广&#xff0c;拥有独特的优势。 首先&#xff0c;私域流量能够更加精准地定位目标用户&#xff0c;实现精准传播。不再盲目投放广告&#xff0c;而是通过建立自身社群、粉丝群&#xff0c;获…

Postman应用——下载注册和登录

文章目录 下载安装注册登录注册账号登录账号 下载安装 Postman下载&#xff1a;https://www.postman.com/ 访问链接后&#xff0c;进入首页&#xff0c;根据自己的操作系统下载对应的版本。 找到下载到的目录直接双击.exe文件&#xff0c;会默认安装在C盘&#xff0c;安装完会…

使用VMware 16 安装银河麒麟V10 --九五小庞

1.下载 银河麒麟系统V10 服务版镜像包&#xff1a; Kylin-Server-10-SP1-Release-Build04-20200711-x86_64.iso 百度云盘链接&#xff1a;https://pan.baidu.com/s/1z0GCEadvefUA8R988qDP5Q 提取码&#xff1a;1l0g 2.运行VMware Workstation&#xff0c;创建新的虚拟机&…

【动态规划刷题 15】最长定差子序列 最长的斐波那契子序列的长度

1218. 最长定差子序列 链接: 1218. 最长定差子序列 给你一个整数数组 arr 和一个整数 difference&#xff0c;请你找出并返回 arr 中最长等差子序列的长度&#xff0c;该子序列中相邻元素之间的差等于 difference 。 子序列 是指在不改变其余元素顺序的情况下&#xff0c;通…

python程序商业化,代码安全最终方案,pyinstaller与cython打包python执行程序

其实一般的程序安全上只需要两步就行&#xff0c;没必要再加密改解释器等&#xff0c;已经不可能反编译到原有python了&#xff0c;因为动态链接库就是汇编了&#xff0c;中间经历了python转c,c在转动态库&#xff0c;代码已经没有可读性了。但是一些密码等重要字符还是要处理好…

构建无限画布,协作数字绘图 | 开源日报 0915

tldraw/tldraw Stars: 16.4k License: Apache-2.0 tldraw 是一个协作数字白板项目&#xff0c;可在 tldraw.com 上使用。它的编辑器、用户界面和其他底层库都是开源的&#xff0c;并且可以通过 npm 进行分发。您可以使用 tldraw 为产品创建一个即插即用的白板&#xff0c;或者…

Smart Community(1)之设计规范

通过前面大数据开发相关知识的学习&#xff0c;准备做一个项目进行练习---我给他起了一个响亮的名字&#xff1a;基于HadoopHA的智慧社区服务平台 设计规范&#xff1a; 做一个项目之前肯定要先规定一些开发过程中的设计规范 &#xff08;一&#xff09;数据埋点规范&#xf…

Python中异常处理4-4

在Python中的异常处理4-1_棉猴的博客-CSDN博客中提到&#xff0c;在try块中的代码运行时如果出现异常&#xff0c;会自动抛出这个异常。可以通过raise语句手动抛出异常。 1 raise语句手动抛出异常 raise后面跟要抛出的异常类或者异常类的实例&#xff0c;表示手动抛出该异常&…

位图+布隆过滤器+海量数据并查集(它们都是哈希的应用)

一)位图: 首先计算一下存储一下10亿个整形数据&#xff0c;需要多大内存呢&#xff0c;多少个G呢&#xff1f; 2^3010亿&#xff0c;10亿个字节 byte kb mb gb 100000000个字节/1024/1024/10241G 所以10亿个字节就是1G&#xff0c;所以40亿个字节就是4G&#xff0c;也就是10个整…