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.mzph.cn/news/80249.shtml

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

相关文章

Windows 10 Enterprise LTSC 2021 (x86) - DVD (Chinese-Simplified)文件分享

Windows 10 Enterprise LTSC 2021 (x64) - DVD (Chinese-Simplified) SW_DVD9_WIN_ENT_LTSC_2021_64BIT_ChnSimp_MLF_X22-84402.ISO 镜像文件&#xff1a; 链接&#xff1a;https://pan.quark.cn/s/2f8f61ec4a98 Windows 10 Enterprise LTSC 2021 (x86) - DVD (Chinese-Simpli…

python selenium 爬虫教程

Python和Selenium是很强大的爬虫工具&#xff0c;可以用于自动化地模拟浏览器行为&#xff0c;从网页中提取数据。下面是一个简单的使用Python和Selenium进行爬虫的案例。 入门&#xff1a; 1. 安装和配置&#xff1a; 首先&#xff0c;你需要安装Python和Selenium。可以使用…

Postman的高级用法一:重新认识postman核心模块

本请求示例来自于免费天气API&#xff1a; 实况天气接口API开发指南 未来一天天气预报api - 天气API 关于Postman的核心模块 全局变量请求接口请求体预处理脚本 类似beforeTest&#xff0c;在发起请求前的预执行逻辑&#xff0c;通常是生成一些动态变量值 测试用例模块 测试者…

开源社区赋能,Walrus 用户体验再升级

基于平台工程理念的应用管理平台 Walrus 已于上月正式开源&#xff0c;目前在 GitHub 已收获 177 颗星&#x1f31f; Walrus 希望打造简洁清爽的应用部署与管理体验&#xff0c;帮助研发与运维团队减少“内耗”工作&#xff0c;提升开发体验。 我们十分重视 Walrus 用户的…

Ubuntu安装深度学习环境相关(yolov8-python部署)

Ubuntu安装深度学习环境相关(yolov8-python部署) 本文将从如下几个方面总结相关的工作过程&#xff1a; Ubuntu系统安装(联想小新pro16) 2.显卡驱动安装3.测试深度学习模型 1. Ubunut 系统安装 之前在台式机上安装过Ubuntu&#xff0c;以为再在笔记本上安装会是小菜一碟&…

查询IP地址可得到哪些信息

通过IP地址定位&#xff0c;可以获取一些基本的信息&#xff0c;包括以下内容&#xff1a; 1. 地理位置&#xff1a;你可以确定IP地址所在的地理位置&#xff0c;包括国家、州或省、城市和地理坐标。这通常是通过将IP地址与地理位置数据库进行匹配来实现的。 2. ISP&#xff…

JDK13特性

文章目录 JAVA13概述语法层面特性switch表达式(预览)文本块(预览) API层次特性重新实现旧版套接字API 其他变化ZGC取消未使用的内存增加废弃和移除增加项移除项废弃项 JAVA13概述 2019年9月17日&#xff0c;国际知名的OpenJDK开源社区发布了Java编程语言环境的最新版本OpenJDK…

【资源监视器】设备占用,强制弹出移动硬盘

设备占用&#xff0c;强制弹出移动硬盘 任务管理器中找到资源监视器 资源监视器&#xff1a;找到CPU 输入磁盘&#xff1a;如H: , 点击旁边的刷新 结束句柄 右键

简单工厂模式 和 工厂方法 和 抽象工厂的区别

简单工厂模式、工厂方法模式和抽象工厂模式是三种不同的创建型设计模式&#xff0c;它们在对象的创建和封装方面有不同的用途和实现方式。以下是它们之间的主要区别&#xff1a; 1. **简单工厂模式&#xff08;Simple Factory Pattern&#xff09;**&#xff1a; - **目的**&a…

二十一、MySQL(多表)内连接、外连接、自连接实现

1、多表查询 &#xff08;1&#xff09;基础概念&#xff1a; &#xff08;2&#xff09;多表查询的分类&#xff1a; 2、内连接 &#xff08;1&#xff09;基础概念&#xff1a; &#xff08;2&#xff09;隐式内连接&#xff1a; 基础语法&#xff1a; select 表1.name,…

私域流量的优势

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

xshell---git上传文件到gitee远程仓库配置

1.git下载 如果没有xshell下没有下载过git&#xff0c;可以参考这篇的教程&#xff1a;Linux配置安装 git 详细教程 下载后可以通过 git --version 查看git的版本号&#xff0c;验证是否安装成功 2.新建仓库 首先需要在gitee上注册一个账号 然后再主页面点击右上边框的 号…

7.从句学习

目录 一、从句。 &#xff08;1&#xff09; 从句总结。 &#xff08;2&#xff09;从句类型。 &#xff08;3&#xff09;引导词&#xff08;常见的引导词&#xff09;。 &#xff08;3.1&#xff09;名词性从句&#xff1a; &#xff08;3.2&#xff09;形容词性从句&a…

Springboot 实践(17)spring boot整合Nacos配置中心

前文我们讲解了Nacos服务端的下载安装&#xff0c;本文我们降价spring boot整合nacos&#xff0c;实现Nacos服务器配置参数的访问。 一、启动Nacos服务&#xff0c;创建三个配置文件&#xff0c;如下所示 Springboot-Nacos-Client-dev.yaml文件配置参数 Springboot-Nacos-Clie…

jitsi音视频会议集成到自己的网页中

将jitsi视频会议集成到自己的web网站&#xff1a; 源码&#xff1a; <!DOCTYPE html> <html lang"en" xmlns:th"http://www.thymeleaf.org"> <head><meta charset"UTF-8"><meta name"viewport" content&…

算法训练 第二周

二、反转链表 本题给我们了一个单链表的头节点head&#xff0c;要求我们把这个单链表的连接顺序进行逆置&#xff0c;并返回逆置后的链表头节点。 1.头插法 我们需要先创建一个新的头节点ph&#xff0c;然后遍历给出的单链表&#xff0c;把遍历到的每一个节点用头插法接到ph的…

高德地图实现-微信小程序地图导航

效果图&#xff1a; 一、准备阶段 1、在高德开放平台注册成为开发者2、申请开发者密钥&#xff08;key&#xff09;。3、下载并解压高德地图微信小程序SDK 高德开放平台&#xff1a; 注册账号(https://lbs.amap.com/)) 申请小程序应用的 key 应用管理(https://console.ama…

Hadoop源码阅读(二):DataNode启动

说明&#xff1a; 1.Hadoop版本&#xff1a;3.1.3 2.阅读工具&#xff1a;IDEA 2023.1.2 3.源码获取&#xff1a;Index of /dist/hadoop/core/hadoop-3.1.3 (apache.org) 4.工程导入&#xff1a;下载源码之后得到 hadoop-3.1.3-src.tar.gz 压缩包&#xff0c;在当前目录打开Pow…

千里共婵娟 | 结合微信公众号用JavaScript完整开发实现换中秋头像的功能

&#x1f3c6;作者简介&#xff0c;黑夜开发者&#xff0c;CSDN领军人物&#xff0c;全栈领域优质创作者✌&#xff0c;阿里云社区专家博主&#xff0c;2023年6月csdn上海赛道top4。 &#x1f3c6;数年电商行业从业经验&#xff0c;历任核心研发工程师&#xff0c;项目技术负责…

差分数组leetcode 2770 数组的最大美丽值

什么是差分数组 差分数组是一种数据结构&#xff0c;它存储的是一个数组每个相邻元素的差值。换句话说&#xff0c;给定一个数组arr[]&#xff0c;其对应的差分数组diff[]将满足&#xff1a; diff[i] arr[i1] - arr[i] 对于所有 0 < i < n-1 差分数组的作用 用于高效…