01_网络编程_传统IO

网络编程

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.105System.out.println(address2.getHostName()); // DESKTOP-V32JVCDString hostAddress = address.getHostAddress();System.out.println(hostAddress); // 192.168.0.105System.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;}@Overridepublic 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;}@Overridepublic 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.rhkb.cn/news/134091.html

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

linux相关知识以及有关指令3

在linux的世界中我们首先要有万物皆文件的概念&#xff0c;那么在系统中有那么多的文件&#xff0c;我们该怎么区分呢&#xff1f;文章目录 1. 文件分类2. 文件的权限1). 拥有者和所属组以及other2). 文件的权限3). 粘滞位4). 对于权限修改的拓展知识点a.修改权限b.修改拥有者所…

学校项目培训之Carla仿真平台之安装Carla

官网&#xff1a;http://carla.org/ 写在前面 由于安装都写了很多东西&#xff0c;所以我单独将安装弄出来记录一下。 如果你在安装9.12版本的时候遇到了很多问题&#xff0c;你可以考虑以下几点&#xff1a; - 楼梯可能不太行&#xff0c;需要更换&#xff0c;这是我实践得到的…

Jmeter 实现 mqtt 协议压力测试

1. 下载jmeter&#xff0c;解压 https://jmeter.apache.org/download_jmeter.cgi 以 5.4.3 为例&#xff0c;下载地址&#xff1a; https://dlcdn.apache.org//jmeter/binaries/apache-jmeter-5.4.3.zip linux下解压&#xff1a; unzip apache-jmeter-5.4.3.zip 2. 下载m…

Docker实战技巧(一):Kubernetes基础操作实战

Kubernetes定位在Saas层,重点解决了微服务大规模部署时的服务编排问题 1、关闭防火墙并设置开机禁用   systemctl stop firewalld   systemctl disable firewalld 2、配置repo   cd /etc/yum.repos.d/   下载Docker repo   wget https://mirrors.aliyun.com/docker-…

在工作流引擎设计领域,是否自动计算未来的处理人的设计模式有哪些?

概述 流程的第一个节点发送下去的时候&#xff0c;就要把以后所有节点的处理人计算出来,能清楚的知道每个节点都是那些人处理. 以驰骋bpm为例来说明这个设计 计算未来处理人包括抄送节点、与待办节点. 默认的模式为&#xff1a;每个节点发送的时候即使计算,就是不计算未来处理…

JavaScript 中的 `this` 指向问题与其在加密中的应用

JS中的 this 关键字是一个非常重要的概念&#xff0c;它在不同情况下会指向不同的对象或值。在本文中&#xff0c;我们将深入探讨 JavaScript 中 this 的各种情况&#xff0c;并思考如何将其应用于 JS加密中的一些有趣用途。 1. 全局上下文中的 this 在全局上下文中&#xff…

苹果cms大橙子vfed 5.0去授权完美破解主题模板

大橙模版算是在苹果 cms 众多主题里&#xff0c;较为亮眼的一款了&#xff0c;主题简洁&#xff0c;功能众多&#xff0c;非常的齐全。 今天分享的就是大橙 5.0 版本模板&#xff0c;自定义菜单输入下列代码使用主题设置和资源采集。 vfed 主题设置&#xff0c;/index.php/la…

docker系列(1) - docker环境篇

文章目录 1. docker环境1.1 docker安装1.2 阿里云镜像加速器1.2 docker管理工具(portainer)1.3 docker网络1.3.1 网络说明1.3.2 创建指定网关的网络 1. docker环境 1.1 docker安装 #CentOS 6 rpm -iUvh http://dl.fedoraproject.org/pub/epel/6/x86_64/epel-release-6-8.noar…

【深度学习】 Python 和 NumPy 系列教程(廿七):Matplotlib详解:3、多子图和布局:散点矩阵图(Scatter Matrix Plot)

目录 一、前言 二、实验环境 三、Matplotlib详解 1、2d绘图类型 2、3d绘图类型 3、多子图和布局 1. subplot()函数 2. subplots()函数 3. 散点矩阵图&#xff08;Scatter Matrix Plot&#xff09; 一、前言 Python是一种高级编程语言&#xff0c;由Guido van Rossum于…

upload-labs文件上传靶场实操

文章目录 1.Pass-012.Pass-023.Pass-034.Pass-045.Pass-056.Pass-067.Pass-078.Pass-089.Pass-0910.Pass-1011.Pass-1112.Pass-1213.Pass-1314.Pass-1415.Pass-1516.Pass-1617.Pass-1718.Pass-1819.Pass-1920.Pass-20 上传姿势总结&#xff1a; 1)改后缀名绕过 2)Content-Type绕…

合宙Air724UG LuatOS-Air LVGL API控件-图片(Gif)

图片&#xff08;Gif&#xff09; GIF图片显示&#xff0c;core版本号要>3211 示例代码 方法一 -- 创建GIF图片控件 glvgl.gif_create(lvgl.scr_act()) -- 设置显示的GIF图像 lvgl.gif_set_src(g,"/lua/test.gif") -- gif图片居中 lvgl.obj_align(g, nil, lvgl…

【AIGC】Stable Diffusion Prompt 每日一练0916

一、前言 1.1 写在前面 本文是一个系列&#xff0c;有点类似随笔&#xff0c;每天一次更新&#xff0c;重点就Stable Diffusion Prompt进行专项训练&#xff0c;本文是第022篇《Stable Diffusion Prompt 每日一练0916》。上一篇《Stable Diffusion Prompt 每日一练0915》 1.…

聚类分析 | MATLAB实现基于SOM自组织特征映射聚类可视化

聚类分析 | MATLAB实现基于SOM自组织特征映射聚类可视化 目录 聚类分析 | MATLAB实现基于SOM自组织特征映射聚类可视化效果一览基本介绍程序设计参考资料 效果一览 基本介绍 基于自组织特征映射聚类算法(SOM)的数据聚类可视化 可直接运行 注释清晰 Matlab语言 1.多特征输入&…

SEO优化:提升网站排名和流量的终极指南

&#x1f482; 个人网站:【工具大全】【游戏大全】【神级源码资源网】&#x1f91f; 前端学习课程&#xff1a;&#x1f449;【28个案例趣学前端】【400个JS面试题】&#x1f485; 寻找学习交流、摸鱼划水的小伙伴&#xff0c;请点击【摸鱼学习交流群】 搜索引擎优化&#xff0…

多线程案例(3) - 定时器,线程池

一&#xff0c;定时器 定时器作用&#xff1a;约定一个时间间隔&#xff0c;时间到达后&#xff0c;执行某段代码逻辑。实际上就是一个 "闹钟" 。 1.1使用标准库中的定时器 标准库中提供了一个 Timer 类. Timer 类的核心方法为 schedule .Timer 类中含有一个扫描线…

防止泄露,保护隐私!如何清除你的谷歌搜索历史记录

按照以下说明学习如何从你的谷歌帐户、谷歌Chrome浏览器、谷歌iOS或Android应用程序或谷歌应用程序中删除你的谷歌历史记录。 如何从你的谷歌帐户中删除搜索历史记录 清除你的谷歌搜索历史并不意味着谷歌实际上会删除你的搜索数据。谷歌仍然会记录你如何以及何时使用某些功能…

C语言经典100例题(56-60)--画圆;画方;画线

目录 【程序56】题目&#xff1a;画图&#xff0c;学用circle画圆形 【程序57】题目&#xff1a;画图&#xff0c;学用line画直线。 【程序58】题目&#xff1a;画图&#xff0c;学用rectangle画方形。 【程序59】题目&#xff1a;画图&#xff0c;综合例子。 【程序60】题…

力扣236 补9.14

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

索引-动图演示存储过程

索引 二叉树存储过程演示 BThree存储过程 sql二级索引搜索过程 Id是唯一键&#xff0c;聚集索引 只存在一个 Name是二级索引 可以存在多个 第一种效率更高&#xff0c;不需要回表

数据治理的 “独孤九剑”

加gzh“大数据食铁兽”&#xff0c;了解更多大数据资讯&#xff01; 来源&#xff1a;与数据同行 免责声明&#xff1a;以上报告均系本平台通过公开、合法渠道获得&#xff0c;报告版权归原撰写/发布机构所有&#xff0c;如涉侵权&#xff0c;请联系删除 &#xff1b;资料…