第十二天、网络通信
网络通信介绍
什么是网络通信?
-
可以让设备中的程序与网络上其他设备中的程序进行数据交互(实现网络通信的)
-
比如:微信聊天、LOL游戏、京东、淘宝网站的访问
基本的通信架构
-
基本的通信架构有2种形式:CS架构( Client客户端/Server服务端 ) 、 BS架构(Browser浏览器/Server服务端)
网络通信三要素
网络通信的关键三要素
IP地址
IP地址
-
IP(Internet Protocol):全称”互联网协议地址”,是分配给上网设备的唯一标志
-
IP地址有两种形式:IPv4、IPv6
IPv4
IPv6地址
-
IPv6:共128位,号称可以为地球每一粒沙子编号。
-
IPv6分成8段表示,每段每四位编码成一个十六进制位表示, 数之间用冒号(:)分开。
公网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地址:检查网络是否连通
IP域名
-
(或更常被称为“域名”)是用于标识和定位互联网上的资源,如网站或服务器的地址。虽然IP地址(如
192.168.1.1
或104.236.166.99
)是计算机在网络中相互通信的基础,但域名提供了一种更易于记忆和使用的替代方式。
以下是关于IP域名的一些关键点:
-
*易于记忆**:与IP地址相比,域名更容易记忆。例如,
www.baidu.com
比104.236.166.99
更容易记住。 -
层次结构:域名具有层次结构,通常包括顶级域名(TLD,如
.com
、.net
、.org
等)、二级域名(如example
在上述示例中)、三级域名(如果存在)等。 -
DNS(域名系统):DNS是一个分布式数据库,用于将域名转换为相应的IP地址。当你尝试访问一个网站时,你的浏览器首先会查询DNS来找出该网站IP地址,然后使用该地址来连接网站。
-
注册和维护:域名通常由域名注册机构(如GoDaddy、Namecheap等)出售和管理。你需要选择一个合适的域名注册机构来购买和管理你的域名。
-
SSL/TLS证书:许多现代网站都使用SSL/TLS证书来加密其与用户的通信,并提供安全的HTTPS连接。这通常要求你为域名购买一个SSL/TLS证书。
InetAddress
-
代表IP地址
常用方法
-
public static void main(String[] args) throws Exception { //1.获取本机地址 InetAddress byAddress = InetAddress.getLocalHost(); System.out.println(byAddress); //2.获取百度的IP InetAddress byName = InetAddress.getByName("www.baidu.com"); System.out.println(byName); //3.获取百度主机名 String hostName = byName.getHostName(); System.out.println(hostName); //4.获取百度ip String hostAddress = byName.getHostAddress(); System.out.println(hostAddress); //5.查看在10毫秒是否能够和百度联通 boolean reachable = byName.isReachable(100); System.out.println(reachable); }
端口号
端口
-
标记正在计算机设备上运行的应用程序的一个数字,范围是 0~65535。
分类
-
周知端口:0~1023,被预先定义的知名应用占用(如:HTTP占用 80,FTP占用21)
-
注册端口:1024~49151,分配给用户进程或某些应用程序
-
动态端口:49152到65535,之所以称为动态端口,是因为它一般不固定分配某种进程,而是动态分配
-
注意:我们自己开发的程序一般选择使用注册端口,且一个设备中不能出现两个程序的端口号一样,否则出错。
协议
通信协议
-
网络上通信的设备,事先规定的连接规则,以及传输数据的规则被称为网络通信协议。
开放式网络互联标准:OSI网络参考模型
-
OSI网络参考模型:全球网络互联标准
-
TCP/IP网络模型:事实上的国际标准
传输层的2个通信协议
-
UDP(User Datagram Protocol):用户数据报协议
-
TCP(Transmission Control Protocol) :传输控制协议
UDP协议
-
特点:无连接、不可靠通信
-
不事先建立连接,数据按照包发,一包数据包含:自己的IP、程序端口,目的地IP、程序端口和数据(限制在64KB内)等
-
发送方不管对方是否在线,数据在中间丢失也不管,如果接收方收到数据也不返回确认,故是不可靠的
TCP协议
-
特点:面向连接、可靠通信
-
TCP的最终目的:要保证在不可靠的信道上实现可靠的传输
-
TCP主要有三个步骤实现可靠传输:三次握手建立连接,传输数据进行确认,四次挥手断开连接
TCP协议:三次握手建立可靠连接
-
可靠连接:确定通信双方,收发消息都是正常无问题的!
TCP协议:四次挥手断开连接
-
目的:确保双方数据的收发都已经完成!
UDP通信
快速入门
UDP通信
-
特点:无连接、不可靠通信
-
不事先建立连接;发送端每次把要发送的数据(限制在64KB内)、接收端IP、等信息封装成一个数据包,发出去就不管了
-
Java提供了一个java.net.DatagramSocket类来实现UDP通信
DatagramSocket: 用于创建客户端、服务端
DatagramPacket:创建数据包
初步了解客户端与服务端的联系
-
客户端: // 创建一个UDP套接字,用于发送数据包 DatagramSocket socket = new DatagramSocket(); // 定义要发送的消息 String msg = "嗨嗨嗨"; // 将消息转换为字节数组,这是UDP数据包传输的数据形式 byte[] bytes = msg.getBytes(); // 获取本地主机的IP地址,用于构建数据包的目标地址 InetAddress host = InetAddress.getLocalHost(); // 创建一个DatagramPacket,包含要发送的数据、数据长度、目标地址和端口 DatagramPacket packet = new DatagramPacket(bytes, bytes.length, host, 8943);//8943为自定义4位数端口 // 发送数据包 socket.send(packet); // 关闭套接字,因为不再需要它 socket.close(); } 服务端: public static void main(String[] args) throws Exception { // 创建一个UDP套接字并绑定到8943端口,用于接收数据。 DatagramSocket socket = new DatagramSocket(8943);//8943为自定义4位数端口 // 创建一个足够大的字节数组,用于存储接收的数据。 byte[] bytes = new byte[64 * 1024]; // 根据字节数组长度创建一个数据报包,用于接收数据。 DatagramPacket packet = new DatagramPacket(bytes, bytes.length); // 接收数据,此操作会阻塞直到有数据到达。 socket.receive(packet); // 从接收到的字节数组中构造一个字符串,只包含实际接收到的数据部分。 String s = new String(bytes, 0, packet.getLength()); // 打印发送数据的IP地址和接收到的数据内容。 System.out.println(packet.getAddress().getHostAddress()+":"+s); // 关闭UDP套接字。 socket.close(); }
多发多收
多发多收
-
其定义就是客户端可以反复发送数据,服务端可以反复接收数据
UDP多发多收代码运用
-
客户端: public static void main(String[] args) throws Exception { // 创建一个DatagramSocket实例,用于发送和接收数据报。 DatagramSocket socket = new DatagramSocket(); // 使用Scanner从控制台读取输入。 Scanner scanner = new Scanner(System.in); while (true) { // 提示用户输入数据。 System.out.println("请输入数据"); // 读取用户输入的字符串。 String msg = scanner.next(); // 如果输入为"exit",则退出循环。 if ("exit".equals(msg)){ break; } // 将字符串转换为字节数组。 byte[] bytes = msg.getBytes(); // 获取本地主机的IP地址。 InetAddress host = InetAddress.getLocalHost(); // 创建一个DatagramPacket,包含要发送的数据、数据长度、目标主机和端口。 DatagramPacket packet = new DatagramPacket(bytes, bytes.length, host, 8943);//8943为自定义4位数端口 // 发送数据报。 socket.send(packet); } // 关闭DatagramSocket。 socket.close(); } 服务端: public static void main(String[] args) throws Exception { // 在8943端口打开一个UDP套接字。 DatagramSocket socket = new DatagramSocket(8943);//8943为自定义4位数端口 // 创建一个足够大的字节数组,用于接收来自客户端的数据包。 byte[] bytes = new byte[64 * 1024]; // 无限循环,以持续监听和接收数据包。 while (true) { // 创建一个数据包对象,用于接收数据。 DatagramPacket packet = new DatagramPacket(bytes, bytes.length); // 接收来自客户端的数据包。 socket.receive(packet); // 将接收到的字节数据转换为字符串。 String s = new String(bytes, 0, packet.getLength()); // 打印发送数据的客户端地址、端口和数据内容。 System.out.println(packet.getAddress().getHostAddress() + ":" + packet.getPort() + ":" + s); // 注释掉的socket.close()如果被取消注释,将导致服务器在接收一个数据包后关闭,因此不适合此处。 //socket.close(); } }
TCP通信
快速入门
TCP通信
-
特点:面向连接、可靠通
-
通信双方事先会采用“三次握手”方式建立可靠连接,实现端到端的通信;底层能保证数据成功传给服务端
-
Java提供了一个java.net.Socket类来实现TCP通信
TCP通信之-客户端开发
-
客户端程序就是通过java.net包下的Socket类来实现的
TCP通信-服务端程序的开发
-
服务端是通过java.net包下的ServerSocket类来实现的
ServerSocket
TCP通信客户端与服务端的联系
-
客户端: public static void main(String[] args) throws Exception { //创建客户端的Socket对象,请求与服务端的链接 Socket socket = new Socket("127.0.0.1",8848);//127.为本机ip,8848为自定义4位数端口 //使用socket对象调用getOutputStream()方法得到字节输出流 OutputStream os = socket.getOutputStream(); //使用字节输出流完成数据的发送 ObjectOutputStream oos = new ObjectOutputStream(os); oos.writeUTF("哈哈,在一起?"); //释放资源:关闭socke管道 oos.close(); socket.close(); } 服务端: public static void main(String[] args) throws Exception { //创建ServerSocket对象,注册服务端端口 ServerSocket serverSocket = new ServerSocket(8848);//8848为自定义4位数端口 //调用ServerSocket对象的accept()方法,等待客户端的连接,并得到Socket管道对象 Socket socket = serverSocket.accept(); //通过Socket对象调用getInputStream()方法得到字节输入流、完成数据的接收 InputStream inputStream = socket.getInputStream(); ObjectInputStream ois = new ObjectInputStream(inputStream); //获取客户端访问者的ip地址对象 InetAddress inetAddress = socket.getInetAddress(); //获取端口 int port = socket.getPort(); String msg = ois.readUTF(); System.out.println(inetAddress.getHostAddress() + ":" + port + "--->" + msg); //释放资源:关闭管道 ois.close(); socket.close(); }
多发多收
多发多收
-
其定义就是客户端可以反复发送数据,服务端可以反复接收数据
TCP多发多收代码运用
-
客户端: public static void main(String[] args) throws Exception { //创建客户端的Socket对象,请求与服务端的链接 Socket socket = new Socket("127.0.0.1", 8858);//127.为本机ip,8858为自定义4位数端口 //使用socket对象调用getOutputStream()方法得到字节输出流 OutputStream os = socket.getOutputStream(); //使用字节输出流完成数据的发送 ObjectOutputStream oos = new ObjectOutputStream(os); Scanner scanner = new Scanner(System.in); while (true) { System.out.println("请输入内容:"); String msg = scanner.next(); if ("exit".equals(msg)) { break; } oos.writeUTF(msg); oos.flush(); } //释放资源:关闭socke管道 oos.close(); socket.close(); } 服务端: public static void main(String[] args) throws Exception { // 监听客户端连接的端口 // 创建ServerSocket对象,注册服务端端口 ServerSocket serverSocket = new ServerSocket(8858);//8858为自定义4位数端口 // 等待并接受客户端的连接 // 调用ServerSocket对象的accept()方法,等待客户端的连接,并得到Socket管道对象 Socket socket = serverSocket.accept(); // 获取客户端发送的数据流 // 通过Socket对象调用getInputStream()方法得到字节输入流、完成数据的接收 InputStream inputStream = socket.getInputStream(); // 将输入流封装为对象输入流,以便读取序列化对象 ObjectInputStream ois = new ObjectInputStream(inputStream); // 获取客户端的IP地址和端口 // 获取客户端访问者的ip地址对象 InetAddress inetAddress = socket.getInetAddress(); // 获取端口 int port = socket.getPort(); // 无限循环,持续接收和处理客户端发送的消息 while (true) { // 读取客户端发送的字符串消息 String msg = ois.readUTF(); // 打印消息,包括发送者的IP地址和端口 System.out.println(inetAddress.getHostAddress() + ":" + port + "--->" + msg); // 注释掉的代码块原意是处理完消息后关闭流和socket,但在此场景下应保持连接以持续接收消息 // ois.close(); // socket.close(); } }
多客户端通信
目前我们开发的服务端程序,是否可以支持与多个客户端同时通信 ?
-
不可以的
-
因为服务端现在只有一个主线程,只能处理一个客户端的消息
多客户通信代码运用
-
客户端: public static void main(String[] args) throws Exception { // 创建一个Socket对象,连接到本地服务器 // 创建客户端的Socket对象,请求与服务端的链接 Socket socket = new Socket("127.0.0.1", 8858);//127.为本机ip,8858为自定义4位数端口 // 获取Socket的输出流,用于向服务器发送数据 // 使用socket对象调用getOutputStream()方法得到字节输出流 OutputStream os = socket.getOutputStream(); // 将OutputStream封装成ObjectOutputStream,用于序列化对象发送给服务器 // 使用字节输出流完成数据的发送 ObjectOutputStream oos = new ObjectOutputStream(os); // 创建Scanner对象,从控制台读取用户输入 Scanner scanner = new Scanner(System.in); // 不断循环,等待用户输入 while (true) { // 提示用户输入消息 System.out.println("请输入内容:"); // 读取用户输入的消息 String msg = scanner.next(); // 如果用户输入了"exit",则退出循环 if ("exit".equals(msg)) { break; } // 将用户输入的消息写入到输出流中,发送给服务器 oos.writeUTF(msg); // 刷新输出流,确保消息立即发送 oos.flush(); } // 关闭ObjectOutputStream和Socket,释放资源 // 释放资源:关闭socke管道 oos.close(); socket.close(); } 服务端: public static void main(String[] args) throws Exception { // 创建ServerSocket对象并绑定到指定端口 // 创建ServerSocket对象,注册服务端端口 ServerSocket serverSocket = new ServerSocket(8858); // 不断等待并接受客户端的连接 // 调用ServerSocket对象的accept()方法,等待客户端的连接,并得到Socket管道对象 while (true) { // 接受客户端连接,返回Socket对象 Socket socket = serverSocket.accept(); // 创建SocketRunnable对象并传入Socket,用于处理客户端连接 SocketRunnable socketRunnable = new SocketRunnable(socket); // 创建新线程并启动,处理客户端通信 new Thread(socketRunnable).start(); } } /** * SocketRunnable类实现了Runnable接口,用于处理客户端的Socket连接。 * 在run方法中,读取客户端发送的数据并打印。 */ class SocketRunnable implements Runnable { private Socket socket; /** * 构造函数,初始化SocketRunnable对象。 * * @param socket 客户端的Socket连接 */ public SocketRunnable(Socket socket) { this.socket = socket; } /** * run方法是线程执行的核心方法。 * 在此方法中,通过输入流读取客户端发送的数据,并打印出来。 */ @Override public void run() { InputStream inputStream = null; ObjectInputStream ois = null; try { // 获取Socket的输入流 inputStream = socket.getInputStream(); // 将输入流封装为ObjectInputStream,用于读取序列化对象 ois = new ObjectInputStream(inputStream); // 获取客户端的IP地址和端口 InetAddress inetAddress = socket.getInetAddress(); int port = socket.getPort(); // 不断读取客户端发送的数据 while (true) { // 读取客户端发送的字符串 String msg = ois.readUTF(); // 打印客户端信息和发送的消息 System.out.println(inetAddress.getHostAddress() + ":" + port + "-->" + msg); } } catch (IOException e) { // 如果发生IO异常,打印提示信息 System.out.println("叉出去!!!"); } finally { // 关闭资源,确保输入流和Socket被正确关闭 try { if (ois != null) ois.close(); if (inputStream != null) inputStream.close(); if (socket != null) socket.close(); } catch (Exception e) { e.printStackTrace(); } } } }
bs程序开发
BS架构的基本原理
-
服务器必须给浏览器响应HTTP协议规定的数据格式,否则浏览器不识别返回的数据
-
HTTP协议规定:响应给浏览器的数据格式必须满足如下格式
BS架构代码基础应用
-
public static void main(String[] args) throws Exception { // 创建ServerSocket对象,指定监听的端口号 // 创建ServerSocket对象,注册服务端端口 ServerSocket serverSocket = new ServerSocket(8668); // 不断循环,等待并处理客户端的连接请求 // 调用ServerSocket对象的accept()方法,等待客户端的连接,并得到Socket管道对象 while (true) { // 接受客户端的连接,返回Socket对象 Socket socket = serverSocket.accept(); // 创建SocketRunnable对象,用于处理客户端请求 SocketRunnable socketRunnable = new SocketRunnable(socket); // 创建线程并启动,处理客户端请求 new Thread(socketRunnable).start(); } } } /** * 实现Runnable接口的类,用于处理客户端的Socket连接。 */ class SocketRunnable implements Runnable { private Socket socket; /** * 构造函数,初始化Socket对象。 * @param socket 客户端的Socket连接对象 */ public SocketRunnable(Socket socket) { this.socket = socket; } /** * Runnable接口的run方法,用于具体处理客户端请求。 */ @Override public void run() { PrintWriter writer = null; try { // 输出客户端连接的端口号 System.out.println("浏览器访问了服务器:" + socket.getPort()); // 创建PrintWriter对象,用于向客户端发送数据 writer = new PrintWriter(socket.getOutputStream()); // 发送HTTP响应头,模拟简单的HTTP服务器 writer.println("HTTP/1.1 200 OK"); writer.println("Content-Type : text/html;charset=UTF-8"); writer.println(); writer.println("<div style=color:green>白龙马</div>"); // 确保数据被发送 writer.flush(); } catch (Exception e) { // 异常处理,此处省略 } finally { try { // 关闭PrintWriter和Socket,释放资源 if (writer != null) { writer.close(); } socket.close(); } catch (Exception e) { // 异常处理,此处省略 } } } }