【Java从入门到大牛】网络编程 Myth丶恋晨 2024-04-26 02:56 40阅读 0赞 > ? 本文由 [程序喵正在路上][Link 1] 原创,CSDN首发! > ? 系列专栏:[Java从入门到大牛][Java] > ? 首发时间:2023年11月23日 > ? 欢迎关注?点赞?收藏?留言? #### 目录 #### * 网络编程 * * 基本的通信架构 * 网络通信三要素 * * IP地址 * 端口号 * 协议 * UDP通信-快速入门 * UDP通信-多发多收 * TCP通信-快速入门 * TCP通信-多发多收 * TCP通信-支持与多个客户端同时通信 * TCP通信-综合案例 * * 即时通信-群聊 * 实现一个简易版的BS架构 * 改进 ## 网络编程 ## **什么是网络编程?** 可以让设备中的程序与网络上的其他设备中的程序进行数据交互,也就是实现网络通信 Java的 java.net.\* 包下提供了网络编程的解决方案 ### 基本的通信架构 ### 基本的通信架构有两种形式:CS架构(Client客户端/Server服务端)、BS架构(Brower浏览器/Server服务端) ![在这里插入图片描述][2dd779b01a074c398d1f8d8d1994ced5.png] ![在这里插入图片描述][52eb12550fee40afb25de3b5f7576ea5.png] 无论是CS架构,还是BS架构的软件都必须依赖网络编程 ## 网络通信三要素 ## ![在这里插入图片描述][a33a9ed812b04492888d57453da0084f.png] ### IP地址 ### * IP(Internet Protocol):全称 “互联网协议地址”, 是分配给上网设备的唯一标志 * IP地址有两种形式:IPv4、IPv6 **IPv4** ![在这里插入图片描述][39a6d6a7040e44bfb25ec6e2209d1ca3.png] **IPv6** * IPv6:共128位,号称可以为地球每一粒沙子编号 * IPv6分成8段表示,每段每四位编码成一个十六进制位表示,数之间用冒号分开 ![在这里插入图片描述][59c76b72a62b4446b35bc3032c3927f9.png] **IP域名** 由于IP地址比较难记住,所以有了IP域名,例如 www.baidu.com 等等 **公网IP,内网IP** * 公网IP,是可以连接互联网的IP地址;内网IP,也叫局域网IP,只能组织机构内部使用 * 192.168. 开头的就是常见的局域网地址,范围即为 192.168.0.0-192.168.255.255,专门为组织机构内部使用 **特殊IP地址** * 127.0.0.1、localhost:代表本机IP,只会寻找当前所在的主机 **IP常用命令** * ipconfig:查看本机IP地址 * ping IP地址:检查网络是否连通 **InetAddress** * 代表IP地址 **InetAddress的常用方法如下** ![在这里插入图片描述][c04698788b704de398b1a484fe1eaa08.png] **应用** import java.net.InetAddress; /** * 目标:掌握InetAddress类的使用 */ public class InetAddressTest { public static void main(String[] args) throws Exception { // 1、获取本机IP地址对象的 InetAddress ip1 = InetAddress.getLocalHost(); System.out.println(ip1.getHostName()); System.out.println(ip1.getHostAddress()); // 2、获取指定IP或者域名的IP地址对象。 InetAddress ip2 = InetAddress.getByName("www.baidu.com"); System.out.println(ip2.getHostName()); System.out.println(ip2.getHostAddress()); // ping www.baidu.com System.out.println(ip2.isReachable(6000)); } } ### 端口号 ### **端口** * 标记正在计算机设备上运行的应用程序的,被规定为一个16位的二进制,范围是0~65535 **分类** * 周知端口:0~1023,被预先定义的知名应用占用,如HTTP占用80,FTP占用21 * 注册端口:1024~49151,分配给用户进程或某些应用程序 * 动态端口:49152~65535,之所以被称为动态端口,是因为它一般不固定分配某种进程,而是动态分配 注意:我们自己开发的程序一般选择使用注册端口,且一个设备中不能出现两个程序的端口号一样,否则会出错 ### 协议 ### **通信协议** * 网络上通信的设备,事先规定的连接规则,以及传输数据的规则被称为网络通信协议 **开放式网络互联标准:OSI网络参考模型** * OSI网络参考模型:全球网络互联标准 * TCP/IP网络模型:事实上的国际标准 ![在这里插入图片描述][64697b6c506a411482a0fe71c15aaaaa.png] **传输层的两个通信协议** * UDP (User Datagram Protocol):用户数据报协议; TCP (Transmission Control Protocol) :传输控制协议 **UDP协议** * 特点:无连接、不可靠通信,通信效率高,通常用于语音通话、视频直播等 * 不事先建立连接,数据按照包发,一包数据包含:自己的IP、程序端口,目的地IP、程序端口和数据(限制在64KB内)等 * 发送方不管对方是否在线,数据在中间丢失也不管,如果接收方收到数据也不返回确认,故是不可靠的 **TCP协议** * 特点:面向连接、可靠通信,通信效率相对不高,通常用于网页、文件下载、支付等 * TCP的最终目的:要保证在不可靠的信道上实现可靠的传输 * TCP主要有三个步骤实现可靠传输:三次握手建立连接,传输数据进行确认,四次挥手断开连接 **TCP协议:三次握手建立可靠连接** * 可靠连接:确定通信双方,收发消息都是正常无问题的!(全双工) ![在这里插入图片描述][7c24fceb4a9546fba2e7a63b77b8a043.png] **TCP协议:四次挥手断开连接** * 目的:确保双方数据的收发都已经完成 ![在这里插入图片描述][5fb6317423464bbbbf62c3e1925ead44.png] ## UDP通信-快速入门 ## Java提供了一个 java.net.DatagramSocket 类来实现UDP通信 **DatagramSocket: 用于创建客户端、服务端** ![在这里插入图片描述][4949d81e0f3c42cb9aac542506690f75.png] **DatagramPacket:创建数据包** ![在这里插入图片描述][eeeac2d00d264b8da250f44a915c0bbd.png] **实战:使用UDP通信实现发送消息、接收消息** **Client.java** import java.net.DatagramPacket; import java.net.DatagramSocket; import java.net.InetAddress; /** * 目标:完成UDP通信快速入门:实现1发1收。 */ public class Client { public static void main(String[] args) throws Exception { // 1、创建客户端对象 DatagramSocket socket = new DatagramSocket(); // 2、创建数据包对象封装要发出去的数据 byte[] bytes = "我是快乐的客户端,我爱你abc".getBytes(); DatagramPacket packet = new DatagramPacket(bytes, bytes.length , InetAddress.getLocalHost(), 6666); // 3、开始正式发送这个数据包的数据出去了 socket.send(packet); System.out.println("客户端数据发送完毕~~~"); socket.close(); // 释放资源! } } **Server.java** import java.net.DatagramPacket; import java.net.DatagramSocket; /** * 目标:完成UDP通信快速入门-服务端开发 */ public class Server { public static void main(String[] args) throws Exception { System.out.println("----服务端启动----"); // 1、创建一个服务端对象,注册端口 DatagramSocket socket = new DatagramSocket(6666); // 2、创建一个数据包对象,用于接收数据的 byte[] buffer = new byte[1024 * 64]; // 64KB. DatagramPacket packet = new DatagramPacket(buffer, buffer.length); // 3、开始正式使用数据包来接收客户端发来的数据 socket.receive(packet); // 4、从字节数组中,把接收到的数据直接打印出来 // 接收多少就倒出多少 // 获取本次数据包接收了多少数据 int len = packet.getLength(); String rs = new String(buffer, 0 , len); System.out.println(rs); System.out.println(packet.getAddress().getHostAddress()); System.out.println(packet.getPort()); socket.close(); // 释放资源 } } 先启动服务端,再启动客户端 ![在这里插入图片描述][17b9e3ee8c7144f9aeff3d94f1be1068.png] ## UDP通信-多发多收 ## **客户端可以反复发送数据** * 创建DatagramSocket对象(发送端对象) * 使用while死循环不断的接收用户的数据输入,如果用户输入的exit则退出程序 * 如果用户输入的不是exit, 把数据封装成DatagramPacket * 使用DatagramSocket对象的send方法将数据包对象进行发送 * 释放资源 **接收端可以反复接收数据** * 创建DatagramSocket对象并指定端口(接收端对象) * 创建DatagramPacket对象接收数据(数据包对象) * 使用DatagramSocket对象的receive方法传入DatagramPacket对象 * 使用while死循环不断的进行第3步 **Client.java** import java.net.DatagramPacket; import java.net.DatagramSocket; import java.net.InetAddress; import java.util.Scanner; /** * 目标:完成UDP通信快速入门:实现客户端反复的发 */ public class Client { public static void main(String[] args) throws Exception { // 1、创建客户端对象 DatagramSocket socket = new DatagramSocket(); // 2、创建数据包对象封装要发出去的数据 Scanner sc = new Scanner(System.in); while (true) { System.out.println("请说:"); String msg = sc.nextLine(); // 一旦发现用户输入的exit命令,就退出客户端 if("exit".equals(msg)){ System.out.println("欢迎下次光临!退出成功!"); socket.close(); // 释放资源 break; // 跳出死循环 } byte[] bytes = msg.getBytes(); DatagramPacket packet = new DatagramPacket(bytes, bytes.length , InetAddress.getLocalHost(), 6666); // 3、开始正式发送这个数据包的数据出去了 socket.send(packet); } } } **Server.java** import java.net.DatagramPacket; import java.net.DatagramSocket; /** * 目标:完成UDP通信快速入门-服务端反复的收 */ public class Server { public static void main(String[] args) throws Exception { System.out.println("----服务端启动----"); // 1、创建一个服务端对象 DatagramSocket socket = new DatagramSocket(6666); // 2、创建一个数据包对象,用于接收数据的 byte[] buffer = new byte[1024 * 64]; // 64KB. DatagramPacket packet = new DatagramPacket(buffer, buffer.length); while (true) { // 3、开始正式使用数据包来接收客户端发来的数据 socket.receive(packet); // 4、从字节数组中,把接收到的数据直接打印出来 // 接收多少就倒出多少 // 获取本次数据包接收了多少数据 int len = packet.getLength(); String rs = new String(buffer, 0 , len); System.out.println(rs); System.out.println(packet.getAddress().getHostAddress()); System.out.println(packet.getPort()); System.out.println("--------------------------------------"); } } } ![在这里插入图片描述][3107b637328e4010ae9cafb29af6d33b.png] ![在这里插入图片描述][9655ec3333ef4fdebdfaa0be3644f825.png] 在这个基础上,我们还可以实现多个客户端同时向服务端发送消息 首先,我们需要在IDEA里面修改一下客户端这个程序的配置,让它可以同时运行多个客户端,具体步骤如下 1、在IDEA右上角找到这个,点击进入 ![在这里插入图片描述][e5482eac33b7471a88be19e3bc9821b1.png] 2、如图操作,注意是客户端 ![在这里插入图片描述][e349ed75eef1495b9a122e963774acac.png] 3、多次执行 Client.java 这个程序,我们就可以得到多个客户端 ![在这里插入图片描述][127e35f513a34202bad5784b8cdc9d37.png] ![在这里插入图片描述][c6b4ddfb80d8448d965c8911e6007314.png] ![在这里插入图片描述][1d92226ee84d4a54bb7a60463d894296.png] ## TCP通信-快速入门 ## Java提供了一个 java.net.Socket 类来实现TCP通信 ![在这里插入图片描述][71aee26f71f44b899922f28ba3ced319.png] **TCP通信之客户端开发** * 客户端程序就是通过java.net包下的Socket类来实现的 ![在这里插入图片描述][edc6053ec72f41b9b28a18414ab2a818.png] **客户端发送消息** * 创建客户端的Socket对象,请求与服务端的连接 * 使用socket对象调用getOutputStream()方法得到字节输出流 * 使用字节输出流完成数据的发送 * 释放资源:关闭socket管道 **Client.java** import java.io.DataOutputStream; import java.io.OutputStream; import java.net.Socket; /** * 目标:完成TCP通信快速入门-客户端开发:实现1发1收 */ public class Client { public static void main(String[] args) throws Exception { // 1、创建Socket对象,并同时请求与服务端程序的连接 Socket socket = new Socket("127.0.0.1", 8888); // 2、从socket通信管道中得到一个字节输出流,用来发数据给服务端程序 OutputStream os = socket.getOutputStream(); // 3、把低级的字节输出流包装成数据输出流 DataOutputStream dos = new DataOutputStream(os); // 4、开始写数据出去了 dos.writeUTF("在一起,好吗?"); dos.close(); socket.close(); // 释放连接资源 } } **TCP通信之服务端程序的开发** * 服务端是通过java.net包下的ServerSocket类来实现的 ![在这里插入图片描述][12440d9044924cc898c39adf15bff4b6.png] **服务端接收消息** * 创建ServerSocket对象,注册服务端端口 * 调用ServerSocket对象的accept()方法,等待客户端的连接,并得到Socket管道对象 * 通过Socket对象调用getInputStream()方法得到字节输入流、完成数据的接收 * 释放资源:关闭socket管道 **Server.java** import java.io.DataInputStream; import java.io.InputStream; import java.net.ServerSocket; import java.net.Socket; /** * 目标:完成TCP通信快速入门-服务端开发:实现1发1收 */ public class Server { public static void main(String[] args) throws Exception { System.out.println("-----服务端启动成功-------"); // 1、创建ServerSocket的对象,同时为服务端注册端口。 ServerSocket serverSocket = new ServerSocket(8888); // 2、使用serverSocket对象,调用一个accept方法,等待客户端的连接请求 Socket socket = serverSocket.accept(); // 3、从socket通信管道中得到一个字节输入流 InputStream is = socket.getInputStream(); // 4、把原始的字节输入流包装成数据输入流 DataInputStream dis = new DataInputStream(is); // 5、使用数据输入流读取客户端发送过来的消息 String rs = dis.readUTF(); System.out.println(rs); // 其实我们也可以获取客户端的IP地址 System.out.println(socket.getRemoteSocketAddress()); dis.close(); socket.close(); } } ![在这里插入图片描述][35ca212387934d9db79ee61b5d2e614b.png] ## TCP通信-多发多收 ## **使用TCP通信实现:多发多收消息** * 客户端使用死循环,让用户不断输入消息 * 服务端也使用死循环,控制服务端收完消息,继续等待接收下一个消息 **Client.java** import java.io.DataOutputStream; import java.io.OutputStream; import java.net.Socket; import java.util.Scanner; /** * 目标:完成TCP通信快速入门-客户端开发:实现客户端可以反复的发消息出去 */ public class Client { public static void main(String[] args) throws Exception { // 1、创建Socket对象,并同时请求与服务端程序的连接。 Socket socket = new Socket("127.0.0.1", 8888); // 2、从socket通信管道中得到一个字节输出流,用来发数据给服务端程序 OutputStream os = socket.getOutputStream(); // 3、把低级的字节输出流包装成数据输出流 DataOutputStream dos = new DataOutputStream(os); Scanner sc = new Scanner(System.in); while (true) { System.out.println("请说:"); String msg = sc.nextLine(); // 一旦用户输入了exit,就退出客户端程序 if("exit".equals(msg)){ System.out.println("欢迎您下次光临!退出成功!"); dos.close(); socket.close(); break; } // 4、开始写数据出去了 dos.writeUTF(msg); dos.flush(); } } } **Server.java** import java.io.DataInputStream; import java.io.InputStream; import java.net.ServerSocket; import java.net.Socket; /** * 目标:完成TCP通信快速入门-服务端开发:实现服务端反复发消息 */ public class Server { public static void main(String[] args) throws Exception { System.out.println("-----服务端启动成功-------"); // 1、创建ServerSocket的对象,同时为服务端注册端口。 ServerSocket serverSocket = new ServerSocket(8888); // 2、使用serverSocket对象,调用一个accept方法,等待客户端的连接请求 Socket socket = serverSocket.accept(); // 3、从socket通信管道中得到一个字节输入流 InputStream is = socket.getInputStream(); // 4、把原始的字节输入流包装成数据输入流 DataInputStream dis = new DataInputStream(is); while (true) { try { // 5、使用数据输入流读取客户端发送过来的消息 String rs = dis.readUTF(); System.out.println(rs); } catch (Exception e) { System.out.println(socket.getRemoteSocketAddress() + "离线了!"); dis.close(); socket.close(); break; } } } } ![在这里插入图片描述][3846bb49480344f0a8669cae051f588d.png] ![在这里插入图片描述][0adf557b54f14e9c8058c4a4cbba2389.png] ## TCP通信-支持与多个客户端同时通信 ## 如果我们像刚才UDP通信那样,同时启动多个客户端,你会发现服务端只能接收到一个客户端发送的消息,那么我们该如何实现服务端与多个客户端同时通信呢 目前我们开发的服务端程序,是不可以与多个客户端同时通信的,因为服务端只有一个主线程,只能处理一个客户端的消息 **Client.java** 不变 添加一个 **ServerReaderThread** 线程类,每个线程处理一个客户端 import java.io.DataInputStream; import java.io.InputStream; import java.net.Socket; public class ServerReaderThread extends Thread{ private Socket socket; public ServerReaderThread(Socket socket){ this.socket = socket; } @Override public void run() { try { InputStream is = socket.getInputStream(); DataInputStream dis = new DataInputStream(is); while (true){ try { String msg = dis.readUTF(); System.out.println(msg); } catch (Exception e) { System.out.println("有人下线了:" + socket.getRemoteSocketAddress()); dis.close(); socket.close(); break; } } } catch (Exception e) { e.printStackTrace(); } } } **Server.java** import java.net.ServerSocket; import java.net.Socket; /** * 目标:完成TCP通信快速入门-服务端开发:要求实现与多个客户端同时通信 */ public class Server { public static void main(String[] args) throws Exception { System.out.println("-----服务端启动成功-------"); // 1、创建ServerSocket的对象,同时为服务端注册端口。 ServerSocket serverSocket = new ServerSocket(8888); while (true) { // 2、使用serverSocket对象,调用一个accept方法,等待客户端的连接请求 Socket socket = serverSocket.accept(); System.out.println("有人上线了:" + socket.getRemoteSocketAddress()); // 3、把这个客户端对应的socket通信管道,交给一个独立的线程负责处理 new ServerReaderThread(socket).start(); } } } ![在这里插入图片描述][90e257ea214a4378b59400a922d07fb9.png] ## TCP通信-综合案例 ## ### 即时通信-群聊 ### **什么是群聊?怎么实现?** * 是指一个客户端把消息发出去,其他在线的全部客户端都可以收到消息 * 需要用到端口转发的设计思想 * 服务端需要把在线的Socket管道存起来,一旦收到一个消息要推送给其他管道 **TCP通信-端口转发** ![在这里插入图片描述][04d58a157ac24641a6a26da8093cb261.png] **代码实现** 改进 **Client.java** import java.io.DataOutputStream; import java.io.OutputStream; import java.net.Socket; import java.util.Scanner; /** * 目标:完成TCP通信快速入门-客户端开发:实现客户端可以反复的发消息出去 */ public class Client { public static void main(String[] args) throws Exception { // 1、创建Socket对象,并同时请求与服务端程序的连接。 Socket socket = new Socket("127.0.0.1", 8888); // 改进:创建一个独立的线程,负责随机从socket中接收服务端发送过来的消息 new ClentReaderThread(socket).start(); // 2、从socket通信管道中得到一个字节输出流,用来发数据给服务端程序 OutputStream os = socket.getOutputStream(); // 3、把低级的字节输出流包装成数据输出流 DataOutputStream dos = new DataOutputStream(os); Scanner sc = new Scanner(System.in); while (true) { System.out.println("请说:"); String msg = sc.nextLine(); // 一旦用户输入了exit,就退出客户端程序 if("exit".equals(msg)){ System.out.println("欢迎您下次光临!退出成功!"); dos.close(); socket.close(); break; } // 4、开始写数据出去了 dos.writeUTF(msg); dos.flush(); } } } 添加 **ClientReaderThread.java** import java.io.DataInputStream; import java.io.InputStream; import java.net.Socket; // 该线程不停接收服务端转发过来的消息并打印,相当于收到群聊里别人的信息 // 思路和服务端接收消息差不多 public class ClentReaderThread extends Thread{ private Socket socket; public ClentReaderThread(Socket socket){ this.socket = socket; } @Override public void run() { try { InputStream is = socket.getInputStream(); DataInputStream dis = new DataInputStream(is); while (true){ try { String msg = dis.readUTF(); System.out.println(msg); } catch (Exception e) { System.out.println("自己下线了:" + socket.getRemoteSocketAddress()); dis.close(); socket.close(); break; } } } catch (Exception e) { e.printStackTrace(); } } } 改进 **Server.java** import java.net.ServerSocket; import java.net.Socket; import java.util.ArrayList; import java.util.List; /** * 目标:完成TCP通信快速入门-服务端开发:要求实现与多个客户端同时通信 */ public class Server { // 改进:添加一个集合保存在线的所有客户端,便于转发消息 public static List<Socket> onLineSockets = new ArrayList<>(); public static void main(String[] args) throws Exception { System.out.println("-----服务端启动成功-------"); // 1、创建ServerSocket的对象,同时为服务端注册端口。 ServerSocket serverSocket = new ServerSocket(8888); while (true) { // 2、使用serverSocket对象,调用一个accept方法,等待客户端的连接请求 Socket socket = serverSocket.accept(); onLineSockets.add(socket); // 有客户端上线就添加 System.out.println("有人上线了:" + socket.getRemoteSocketAddress()); // 3、把这个客户端对应的socket通信管道,交给一个独立的线程负责处理。 new ServerReaderThread(socket).start(); } } } 改进 **ServerReaderThread.java** import java.io.*; import java.net.Socket; public class ServerReaderThread extends Thread{ private Socket socket; public ServerReaderThread(Socket socket){ this.socket = socket; } @Override public void run() { try { InputStream is = socket.getInputStream(); DataInputStream dis = new DataInputStream(is); while (true){ try { String msg = dis.readUTF(); System.out.println(msg); // 把这个消息分发给全部客户端进行接收。 sendMsgToAll(msg); } catch (Exception e) { System.out.println("有人下线了:" + socket.getRemoteSocketAddress()); Server.onLineSockets.remove(socket); // 客户端下线就删除 dis.close(); socket.close(); break; } } } catch (Exception e) { e.printStackTrace(); } } // 添加消息转发方法 private void sendMsgToAll(String msg) throws IOException { // 发送给全部在线的socket管道接收。 for (Socket onLineSocket : Server.onLineSockets) { OutputStream os = onLineSocket.getOutputStream(); DataOutputStream dos = new DataOutputStream(os); dos.writeUTF(msg); dos.flush(); } } } ### 实现一个简易版的BS架构 ### **需求描述** 要求从浏览器中访问服务器,并立即让服务器响应一个很简单的网页给浏览器展示 **BS架构的基本原理** BS架构的客户端就是浏览器,客户从浏览器访问服务器的方法就是通过网址来访问,一般的格式是 `http://服务器IP:服务器端口` ![在这里插入图片描述][a6d3fde4f29448cf91c67f862af4ac55.png] 注意:服务器必须给浏览器响应HTTP协议规定的数据格式,否则浏览器不会识别返回的数据,也就是说,服务端返回数据不能还是用以前那种打印语句直接打印 HTTP协议规定:响应给浏览器的数据格式必须满足如下格式 ![在这里插入图片描述][4d935c54d630451482d41d95ac12b3b8.png] **需求实现** **Server.java**,和前面的一样 import java.net.ServerSocket; import java.net.Socket; /** * 目标:完成TCP通信快速入门-服务端开发:要求实现与多个客户端同时通信。 */ public class Server { public static void main(String[] args) throws Exception { System.out.println("-----服务端启动成功-------"); // 1、创建ServerSocket的对象,同时为服务端注册端口。 ServerSocket serverSocket = new ServerSocket(8080); while (true) { // 2、使用serverSocket对象,调用一个accept方法,等待客户端的连接请求 Socket socket = serverSocket.accept(); System.out.println("有人上线了:" + socket.getRemoteSocketAddress()); // 3、把这个客户端对应的socket通信管道,交给一个独立的线程负责处理。 new ServerReaderThread(socket).start(); } } } 改进 **ServerReaderThread.java** import java.io.OutputStream; import java.io.PrintStream; import java.net.Socket; public class ServerReaderThread extends Thread{ private Socket socket; public ServerReaderThread(Socket socket){ this.socket = socket; } @Override public void run() { // 立即响应一个网页内容给浏览器展示。 try { OutputStream os = socket.getOutputStream(); PrintStream ps = new PrintStream(os); ps.println("HTTP/1.1 200 OK"); ps.println("Content-Type:text/html;charset=UTF-8"); ps.println(); // 必须换行 ps.println("<div style='color:blue;font-size:120px;text-align:center'>66666666666<div>"); // 访问完就要关闭 ps.close(); socket.close(); } catch (Exception e) { e.printStackTrace(); } } } **效果** 随便打开一个浏览器,输入网址 `127.0.0.1:8080` 回车即可,其中 127.0.0.1 代表本机IP,8080 是我们在服务端指定的端口 ![在这里插入图片描述][20244069810f4d7c8d1f2fc7873d340c.png] ### 改进 ### 在上面的程序中,客户端的每次请求都会开一个新的线程,这样做好不好呢? 其实是不好的,当访问人数特别多时,也就是高并发时,就会很容易宕机 很明显,我们都知道要使用线程池来进行优化 ![在这里插入图片描述][6172915bab0d42ac9442a8fdfb2e57a4.png] **优化后的代码** **Server.java** import java.net.ServerSocket; import java.net.Socket; import java.util.concurrent.ArrayBlockingQueue; import java.util.concurrent.Executors; import java.util.concurrent.ThreadPoolExecutor; import java.util.concurrent.TimeUnit; /** * 目标:完成TCP通信快速入门-服务端开发:要求实现与多个客户端同时通信。 */ public class Server { public static void main(String[] args) throws Exception { System.out.println("-----服务端启动成功-------"); // 1、创建ServerSocket的对象,同时为服务端注册端口 ServerSocket serverSocket = new ServerSocket(8080); // 创建出一个线程池,负责处理通信管道的任务 ThreadPoolExecutor pool = new ThreadPoolExecutor(16 * 2, 16 * 2, 0, TimeUnit.SECONDS, new ArrayBlockingQueue<>(8), Executors.defaultThreadFactory(), new ThreadPoolExecutor.AbortPolicy()); while (true) { // 2、使用serverSocket对象,调用一个accept方法,等待客户端的连接请求 Socket socket = serverSocket.accept(); // 3、把这个客户端对应的socket通信管道,交给一个独立的线程负责处理 pool.execute(new ServerReaderRunnable(socket)); } } } **ServerReaderRunnable.java** import java.io.OutputStream; import java.io.PrintStream; import java.net.Socket; public class ServerReaderRunnable implements Runnable{ private Socket socket; public ServerReaderRunnable(Socket socket){ this.socket = socket; } @Override public void run() { // 立即响应一个网页内容给浏览器展示 try { OutputStream os = socket.getOutputStream(); PrintStream ps = new PrintStream(os); ps.println("HTTP/1.1 200 OK"); ps.println("Content-Type:text/html;charset=UTF-8"); ps.println(); // 必须换行 ps.println("<div style='color:red;font-size:120px;text-align:center'>88888<div>"); ps.close(); socket.close(); } catch (Exception e) { e.printStackTrace(); } } } ![在这里插入图片描述][670258f4e16b43fc8f464c6f44c4c2d0.png] [Link 1]: https://blog.csdn.net/weixin_62511863?spm=1011.2421.3001.5343 [Java]: https://blog.csdn.net/weixin_62511863/category_12365679.html?spm=1001.2014.3001.5482 [2dd779b01a074c398d1f8d8d1994ced5.png]: https://image.dandelioncloud.cn/pgy_files/images/2024/04/26/a3820eaabe3e4f3fb6724b6d6e989c8c.png [52eb12550fee40afb25de3b5f7576ea5.png]: https://image.dandelioncloud.cn/pgy_files/images/2024/04/26/3791c9e948a94b58a3b45837b61552ae.png [a33a9ed812b04492888d57453da0084f.png]: https://image.dandelioncloud.cn/pgy_files/images/2024/04/26/13747101e86b4d6fae57d9481cedd481.png [39a6d6a7040e44bfb25ec6e2209d1ca3.png]: https://image.dandelioncloud.cn/pgy_files/images/2024/04/26/2a216099b2bc496a8b468545dceb7488.png [59c76b72a62b4446b35bc3032c3927f9.png]: https://image.dandelioncloud.cn/pgy_files/images/2024/04/26/b1c73896adab447db160a2987a3d24d7.png [c04698788b704de398b1a484fe1eaa08.png]: https://image.dandelioncloud.cn/pgy_files/images/2024/04/26/838d7ae6e3884520b0d8875ba7fa0d07.png [64697b6c506a411482a0fe71c15aaaaa.png]: https://image.dandelioncloud.cn/pgy_files/images/2024/04/26/5c5918aebf1c49d9a95321998a681a51.png [7c24fceb4a9546fba2e7a63b77b8a043.png]: https://image.dandelioncloud.cn/pgy_files/images/2024/04/26/6a03f657a09048db9d86938b044dc01b.png [5fb6317423464bbbbf62c3e1925ead44.png]: https://image.dandelioncloud.cn/pgy_files/images/2024/04/26/e2ba2f56a950466f966b9806a408352b.png [4949d81e0f3c42cb9aac542506690f75.png]: https://image.dandelioncloud.cn/pgy_files/images/2024/04/26/846073bec6134eb7a0cb1bf5e2bce9af.png [eeeac2d00d264b8da250f44a915c0bbd.png]: https://image.dandelioncloud.cn/pgy_files/images/2024/04/26/4df31380e60f4cc9b520b1bb6ad1ffff.png [17b9e3ee8c7144f9aeff3d94f1be1068.png]: https://image.dandelioncloud.cn/pgy_files/images/2024/04/26/561ecb81cd004f798345fc4c8792733a.png [3107b637328e4010ae9cafb29af6d33b.png]: https://image.dandelioncloud.cn/pgy_files/images/2024/04/26/1037ab56b7ab4cb2aef6d11395b54df1.png [9655ec3333ef4fdebdfaa0be3644f825.png]: https://image.dandelioncloud.cn/pgy_files/images/2024/04/26/016050292db04e91ad3872b8964313ac.png [e5482eac33b7471a88be19e3bc9821b1.png]: https://image.dandelioncloud.cn/pgy_files/images/2024/04/26/f1dcca3bcc3241228306e27b10f9d375.png [e349ed75eef1495b9a122e963774acac.png]: https://image.dandelioncloud.cn/pgy_files/images/2024/04/26/2da36bfe4ba44a579c0a57c6e84ba5e6.png [127e35f513a34202bad5784b8cdc9d37.png]: https://image.dandelioncloud.cn/pgy_files/images/2024/04/26/3add020393634735a78a627d0a2710e9.png [c6b4ddfb80d8448d965c8911e6007314.png]: https://image.dandelioncloud.cn/pgy_files/images/2024/04/26/a9f5140abde54fa8857cdae9c5e3c6bc.png [1d92226ee84d4a54bb7a60463d894296.png]: https://image.dandelioncloud.cn/pgy_files/images/2024/04/26/8fa04c952d9c4ec597ae6d95bf27ebb6.png [71aee26f71f44b899922f28ba3ced319.png]: https://image.dandelioncloud.cn/pgy_files/images/2024/04/26/5ee6c9afe42d47709d4717fc1a179b71.png [edc6053ec72f41b9b28a18414ab2a818.png]: https://image.dandelioncloud.cn/pgy_files/images/2024/04/26/af8bb6ab5ec049d0b8a5b469dc5efee5.png [12440d9044924cc898c39adf15bff4b6.png]: https://image.dandelioncloud.cn/pgy_files/images/2024/04/26/4d6ecdee75ca48b7a08a9b0eb135fbb0.png [35ca212387934d9db79ee61b5d2e614b.png]: https://image.dandelioncloud.cn/pgy_files/images/2024/04/26/cab1f3eb94ed4aee83f02a3d597f886b.png [3846bb49480344f0a8669cae051f588d.png]: https://image.dandelioncloud.cn/pgy_files/images/2024/04/26/b36c124fd40042cdb4d7a09346c385f6.png [0adf557b54f14e9c8058c4a4cbba2389.png]: https://image.dandelioncloud.cn/pgy_files/images/2024/04/26/986e2fd232a749fb83a3af33828098b4.png [90e257ea214a4378b59400a922d07fb9.png]: https://image.dandelioncloud.cn/pgy_files/images/2024/04/26/c78b123b74af469f92bf3f32a4303738.png [04d58a157ac24641a6a26da8093cb261.png]: https://image.dandelioncloud.cn/pgy_files/images/2024/04/26/283043936ff4476798bc7da4118d4e61.png [a6d3fde4f29448cf91c67f862af4ac55.png]: https://image.dandelioncloud.cn/pgy_files/images/2024/04/26/f44fec80f2fc4d44bc6098d1d348e322.png [4d935c54d630451482d41d95ac12b3b8.png]: https://image.dandelioncloud.cn/pgy_files/images/2024/04/26/fc0e2bc8e1b04c4d860a7abd904b3edb.png [20244069810f4d7c8d1f2fc7873d340c.png]: https://image.dandelioncloud.cn/pgy_files/images/2024/04/26/8417127a75d540f5b92aae5209598f90.png [6172915bab0d42ac9442a8fdfb2e57a4.png]: https://image.dandelioncloud.cn/pgy_files/images/2024/04/26/f44d83e4dc1d459bad390fc6d4c71d5e.png [670258f4e16b43fc8f464c6f44c4c2d0.png]: https://image.dandelioncloud.cn/pgy_files/images/2024/04/26/771ba82539ec4641a150bdfd16a24bee.png
相关 【Java从入门到大牛】Java高级技术 注解(Annotation)就是 Java 代码里的特殊标记,比如:@Override、@Test 等,作用是:让其他程序根据注解信息来决定怎么执行该程序自定义注解,就是... 忘是亡心i/ 2024年04月26日 02:57/ 0 赞/ 41 阅读
相关 【Java从入门到大牛】网络编程 可以让设备中的程序与网络上的其他设备中的程序进行数据交互,也就是实现网络通信Java的 java.net.* 包下提供了网络编程的解决方案。 Myth丶恋晨/ 2024年04月26日 02:56/ 0 赞/ 41 阅读
相关 【Java从入门到大牛】String和ArrayList 本文主要介绍了String类和ArrayList类的基本概念及其使用方法。 淩亂°似流年/ 2024年04月26日 02:42/ 0 赞/ 35 阅读
相关 【Java从入门到大牛】编程思维训练 本文讲解了几个小案例,巩固了之前学习Java的一些基础知识,同时积累了一定的代码量 本是古典 何须时尚/ 2024年04月26日 02:40/ 0 赞/ 39 阅读
还没有评论,来说两句吧...