当前位置: 首页 > news >正文

【网络通信】详解网络通信、实现 CS / BS架构 通信

文章目录

  • 2种基本通信架构:
  • 网络通信三要素
  • IP地址
      • DNS域名解析:
      • IP常用指令
  • 端口
  • 通信协议【CS架构】
    • (1)UDP
      • 一发一收
      • 多发多收
    • (2)TCP
      • 一发一收
      • 多发多收
      • 与多个客户端同时通信
  • BS架构
      • BS架构的基本原理
      • 使用线程池进行优化
  • 项目实战
      • 获取时间
      • StringBuilder
      • BigDecimal

2种基本通信架构:

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

网络通信三要素

在这里插入图片描述

IP地址

IP:”互联网协议地址”,是分配给上网设备的唯一标识

被广泛采用的IP地址形式 :
IPv4:使用32位地址,通常以点分十进制表示。
在这里插入图片描述
IPv6:使用128位地址
冒分十六进制:IPv6分成8段,每段每四位编码成一个十六进制位表示, 每段之间用冒号(:)分开。
在这里插入图片描述

IP域名用于在互联网上我们能识别和定位网站的名称。例如:www.baidu.com。

DNS域名解析:

是互联网中用于将域名转换为对应IP地址的分布式命名系统。它充当了互联网的“电话簿”,将易记的域名映射到数字化的IP地址,使得用户可以通过域名来访问网站和其他网络资源。
在这里插入图片描述

  1. 公网IP:是可以连接到互联网的IP地址。
  2. 内网IP:也叫局域网IP,是只能组织机构内部使用的IP地址;范围为192.168.0.0–192.168.255.255。
  3. 本机IP:127.0.0.1、localhost,只会寻找当前程序所在的主机。

IP常用指令

  • ipconfig:查看本机IP地址。
  • ping IP地址:检查网络是否连通。

InetAddress:代表IP地址。常用方法:
在这里插入图片描述

端口

用来唯一标识正在计算机设备上运行的应用程序,被规定为一个 16 位的二进制,范围是 0~65535。

端口分类概念
周知端口0~1023,被预先定义的知名应用占用
(如:HTTP占用 80,FTP占用21)
注册端口1024~49151,分配给用户进程或某些应用程序
动态端口49152到65535,它一般不固定分配某种进程,而是动态分配

注意:
自己开发的程序一般选择使用注册端口,且一个设备中不能出现两个程序的端口号一样,否则报错。

通信协议【CS架构】

通信协议:是指网络上通信的设备,事先规定的连接规则,以及传输数据的规则

(1)OSI网络参考模型:全球网络互联标准。
(2)TCP/IP网络模型:事实上的国际标准。
在这里插入图片描述

(1)UDP

UDP:是用户数据报协议。通常用在:视频直播、语音通话。
特点:无连接、不可靠通信

  1. 无连接:不事先建立连接。发送方不管对方是否在线,数据在中间丢失也不管
  2. 不可靠通信:接收方收到数据也不返回确认

Java提供了一个java.net.DatagramSocket类来实现UDP通信。
在这里插入图片描述
在这里插入图片描述

一发一收

【客户端】:

import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.net.InetAddress;public class UDPClientDemo1 {public static void main(String[] args) throws Exception {System.out.println("===客户端启动==");// 1、创建DatagramSocket对象(客户端对象)DatagramSocket socket = new DatagramSocket(); // 随机端口// 2、创建DatagramPacket对象封装需要发送的数据(数据包对象)   byte[] bytes = "我是客户端".getBytes();/***   public DatagramPacket(byte[] buf, int length,*                         InetAddress address, int port)* 参数一:发送的数据,字节数组* 参数二:发送的字节长度。* 参数三:目的地的IP地址。* 参数四:服务端程序的端口号*/DatagramPacket packet = new DatagramPacket(bytes, bytes.length, InetAddress.getLocalHost(), 8080);// 3、使用DatagramSocket对象的send方法,传入数据包的数据socket.send(packet);// 4、释放资源socket.close();}
}

【服务端】:

import java.net.DatagramPacket;
import java.net.DatagramSocket;public class UDPServerDemo2 {public static void main(String[] args) throws Exception {System.out.println("==服务端启动了===");// 1、创建DatagramSocket对象并指定端口(服务端对象)DatagramSocket socket = new DatagramSocket(8080);// 2、创建DatagramPacket对象接收数据(数据包对象)byte[] buf = new byte[1024 * 64];DatagramPacket packet = new DatagramPacket(buf, buf.length);// 3、接收数据,将数据封装到数据包对象的字节数组中去socket.receive(packet);// 4、看看数据是否收到了int len = packet.getLength();   // 获取当前收到的数据长度。String data = new String(buf, 0 , len);System.out.println("服务端收到了:" + data);// 获取对方的ip对象和程序端口String ip = packet.getAddress().getHostAddress();int port = packet.getPort();System.out.println("对方ip:" + ip + "   对方端口:" + port);socket.close();}
}

多发多收

【客户端】:

public class UDPClientDemo1 {public static void main(String[] args) throws Exception {System.out.println("===客户端启动==");// 1、创建DatagramSocket对象(发送端对象)DatagramSocket socket = new DatagramSocket(); // 随机端口Scanner sc = new Scanner(System.in);// 使用while死循环不断的接收用户的数据输入while (true) {// 2、创建DatagramPacket对象封装需要发送的数据(数据包对象)  System.out.println("请说:");String msg = sc.nextLine();// 如果用户输入的是 exit,则退出if ("exit".equals(msg)) {System.out.println("===客户端退出==");socket.close();break;}// 把数据封装成DatagramPacketbyte[] bytes = msg.getBytes();DatagramPacket packet = new DatagramPacket(bytes, bytes.length,InetAddress.getLocalHost(), 8080);// 3、让发送端对象发送数据包的数据socket.send(packet);// 4、释放资源socket.close();}}
}

【接收端】:接收端只负责接收数据包,无所谓是哪个发送端的数据包

public class UDPServerDemo2 {public static void main(String[] args) throws Exception {// 目标:完成UDP通信多发多收:服务端开发。System.out.println("==服务端启动了===");// 1、创建DatagramSocket对象并指定端口(接收端对象)DatagramSocket socket = new DatagramSocket(8080);// 2、创建DatagramPacket对象接收数据(数据包对象)byte[] buf = new byte[1024 * 64];DatagramPacket packet = new DatagramPacket(buf, buf.length);while (true) {// 3、接收数据,将数据封装到数据包对象的字节数组中去socket.receive(packet); // 等待式接收数据。// 4、看看数据是否收到了int len = packet.getLength();   // 获取当前收到的数据长度。String data = new String(buf, 0 , len);System.out.println("服务端收到了:" + data);// 获取对方的ip对象和程序端口String ip = packet.getAddress().getHostAddress();int port = packet.getPort();System.out.println("对方ip:" + ip + "   对方端口:" + port);System.out.println("----------------------------------------------");}}
}

(2)TCP

TCP:是传输控制协议。特点:面向连接、可靠通信
可以保证在不可靠的信道上实现可靠的数据传输。通常用在:文件下载、支付。

  1. 三次握手建立可靠连接:确保通信的双方收发消息都是没问题的(全双工)
    在这里插入图片描述

  2. 四次挥手断开连接:确保通信的双方收发消息都已经完成。
    在这里插入图片描述

Java提供了一个java.net.Socket类来实现TCP通信。
在这里插入图片描述

一发一收

【客户端】:通过 java.net.Socket 类来实现
在这里插入图片描述

import java.io.DataOutputStream;
import java.io.OutputStream;
import java.net.Socket;public class ClientDemo1 {public static void main(String[] args) throws Exception {System.out.println("客户端启动....");// 1、创建客户端的Socket对象,请求与服务端的Socket连接Socket socket = new Socket("127.0.0.1", 9999);// 2、使用socket对象调用getOutputStream()方法得到字节输出流OutputStream os = socket.getOutputStream();// 3、特殊数据流DataOutputStream dos = new DataOutputStream(os);dos.writeInt(1);dos.writeUTF("我想你了,你在哪儿?");// 4、释放资源socket.close();}
}

【服务端】:通过java.net.ServerSocket类来实现的
在这里插入图片描述

import java.io.DataInputStream;
import java.io.InputStream;
import java.net.ServerSocket;
import java.net.Socket;public class ServerDemo2 {public static void main(String[] args) throws Exception {System.out.println("服务端启动了...");// 1、创建服务端ServerSocket对象,绑定端口号,监听客户端连接ServerSocket ss = new ServerSocket(9999);// 2、调用accept方法,阻塞等待客户端连接,一旦有客户端链接会返回一个Socket对象Socket socket = ss.accept();// 3、获取输入流,读取客户端发送的数据InputStream is = socket.getInputStream();// 4、把字节输入流包装成特殊数据输入流DataInputStream dis = new DataInputStream(is);// 5、读取数据int id = dis.readInt();String msg = dis.readUTF();System.out.println("id=" + id + ",收到的客户端msg=" + msg);// 6、客户端的ip和端口System.out.println("客户端的ip=" + socket.getInetAddress().getHostAddress());System.out.println("客户端的端口=" + socket.getPort());}
}

多发多收

【客户端】:

public class ClientDemo1 {public static void main(String[] args) throws Exception {System.out.println("客户端启动....");// 1、常见Socket管道对象,请求与服务端的Socket链接。可靠链接Socket socket = new Socket("127.0.0.1", 9999);// 2、从socket通信管道中得到一个字节输出流。OutputStream os = socket.getOutputStream();// 3、特殊数据流。DataOutputStream dos = new DataOutputStream(os);Scanner sc = new Scanner(System.in);// while死循环,让用户不断输入消息while (true) {System.out.println("请说:");String msg = sc.nextLine();if ("exit".equals(msg)) {System.out.println("退出成功!");dos.close(); // 关闭输出流socket.close(); // 关闭socketbreak;}dos.writeUTF(msg); // 发送数据dos.flush();}}
}

【服务端】:

public class ServerDemo2 {public static void main(String[] args) throws Exception {System.out.println("服务端启动了...");// 1、创建服务端ServerSocket对象,绑定端口号,监听客户端连接ServerSocket ss = new ServerSocket(9999);// 2、调用accept方法,阻塞等待客户端连接,一旦有客户端链接会返回一个Socket对象Socket socket = ss.accept();// 3、获取输入流,读取客户端发送的数据InputStream is = socket.getInputStream();// 4、把字节输入流包装成特殊数据输入流DataInputStream dis = new DataInputStream(is);// while死循环,控制服务端程序收完消息后,继续去接收下一个消息。while (true) {// 5、读取数据String msg = dis.readUTF(); // 等待读取客户端发送的数据System.out.println("收到的客户端msg=" + msg);// 6、客户端的ip和端口System.out.println("客户端的ip=" + socket.getInetAddress().getHostAddress());System.out.println("客户端的端口=" + socket.getPort());System.out.println("--------------------------------------------------");}}
}

与多个客户端同时通信

(1)主线程定义了循环负责接收客户端Socket管道连接
(2)每接收到一个Socket通信管道后分配一个独立的线程负责处理它
在这里插入图片描述
【客户端】:

import java.io.DataOutputStream;
import java.io.OutputStream;
import java.net.Socket;
import java.util.Scanner;public class ClientDemo1 {public static void main(String[] args) throws Exception {System.out.println("客户端启动....");// 1、常见Socket管道对象,请求与服务端的Socket链接。可靠链接Socket socket = new Socket("127.0.0.1", 9999);// 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();if ("exit".equals(msg)) {System.out.println("退出成功!");dos.close(); // 关闭输出流socket.close(); // 关闭socketbreak;}dos.writeUTF(msg); // 发送数据dos.flush();}}
}

【服务端】:

import java.net.ServerSocket;
import java.net.Socket;public class ServerDemo2 {public static void main(String[] args) throws Exception {System.out.println("服务端启动了...");// 1、创建服务端ServerSocket对象,绑定端口号,监听客户端连接ServerSocket ss = new ServerSocket(9999);while (true) {// 2、调用accept方法,阻塞等待客户端连接,一旦有客户端链接会返回一个Socket对象Socket socket = ss.accept();System.out.println("一个客户端上线了:" + socket.getInetAddress().getHostAddress());// 3、把这个客户端管道交给一个独立的子线程专门负责接收这个管道的消息。new ServerReader(socket).start();}}
}

【线程】:

import java.io.DataInputStream;
import java.io.InputStream;
import java.net.Socket;public class ServerReader extends Thread{private Socket socket;public ServerReader(Socket socket) {this.socket = socket;}@Overridepublic void run() {try {// 获取输入流,读取客户端发送的数据InputStream is = socket.getInputStream();// 把字节输入流包装成特殊数据输入流DataInputStream dis = new DataInputStream(is);while (true) {// 读取数据String msg = dis.readUTF(); // 等待读取客户端发送的数据System.out.println("收到的客户端msg=" + msg);// 客户端的ip和端口System.out.println("客户端的ip=" + socket.getInetAddress().getHostAddress());System.out.println("客户端的端口=" + socket.getPort());System.out.println("--------------------------------------------------");}} catch (Exception e) {System.out.println("客户端下线了:"+ socket.getInetAddress().getHostAddress());}}
}

BS架构

BS架构的基本原理

  • 客户端使用浏览器发起请求(不需要开发客户端)
  • 服务端必须按照HTTP协议响应数据。
    在这里插入图片描述

注意:服务器必须给浏览器响应HTTP协议规定的数据格式,否则浏览器不识别返回的数据。

HTTP协议规定:响应给浏览器的数据格式必须满足如下格式
在这里插入图片描述
【服务端】:

import java.net.ServerSocket;
import java.net.Socket;
import java.util.concurrent.*;public class ServerDemo {public static void main(String[] args) throws Exception {System.out.println("服务端启动了...");// 1、创建服务端ServerSocket对象,绑定端口号,监听客户端连接ServerSocket ss = new ServerSocket(8080);// 创建线程池ExecutorService pool = new ThreadPoolExecutor(3, 10, 10, TimeUnit.SECONDS, new ArrayBlockingQueue<>(100), Executors.defaultThreadFactory(), new ThreadPoolExecutor.AbortPolicy());while (true) {// 2、调用accept方法,阻塞等待客户端连接,一旦有客户端链接会返回一个Socket对象Socket socket = ss.accept();System.out.println("一个客户端上线了:" + socket.getInetAddress().getHostAddress());// 3、把这个客户端管道包装成一个任务交给线程池处理pool.execute(new ServerReaderRunnable(socket));}}
}

【线程】:

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;}@Overridepublic 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("<html>");ps.println("<head>");ps.println("<meta charset='utf-8'>");ps.println("<title>");ps.println("lm");ps.println("</title>");ps.println("</head>");ps.println("<body>");ps.println("<h1 style='color:red;font-size=20px'>lm</h1>");// 响应一个黑马程序员的log展示ps.println("<img src='https://www.baidu.com/images/logo.png'>");ps.println("</body>");ps.println("</html>");ps.close();socket.close();} catch (Exception e) {System.out.println("客户端下线了:"+ socket.getInetAddress().getHostAddress());}}
}

使用线程池进行优化

在这里插入图片描述

【服务端】:

import java.net.ServerSocket;
import java.net.Socket;
import java.util.concurrent.*;public class ServerDemo {public static void main(String[] args) throws Exception {System.out.println("服务端启动了...");// 1、创建服务端ServerSocket对象,绑定端口号,监听客户端连接ServerSocket ss = new ServerSocket(8080);// 创建线程池ExecutorService pool = new ThreadPoolExecutor(3, 10, 10, TimeUnit.SECONDS, new ArrayBlockingQueue<>(100), Executors.defaultThreadFactory(), new ThreadPoolExecutor.AbortPolicy());while (true) {// 2、调用accept方法,阻塞等待客户端连接,一旦有客户端链接会返回一个Socket对象Socket socket = ss.accept();System.out.println("一个客户端上线了:" + socket.getInetAddress().getHostAddress());// 3、把这个客户端管道包装成一个任务交给线程池处理pool.execute(new ServerReaderRunnable(socket));}}
}

【线程】:

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;}@Overridepublic 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("<html>");ps.println("<head>");ps.println("<meta charset='utf-8'>");ps.println("<title>");ps.println("lm");ps.println("</title>");ps.println("</head>");ps.println("<body>");ps.println("<h1 style='color:red;font-size=20px'>lm</h1>");// 响应一个黑马程序员的log展示ps.println("<img src='https://www.baidu.com/images/logo.png'>");ps.println("</body>");ps.println("</html>");ps.close();socket.close();} catch (Exception e) {System.out.println("客户端下线了:"+ socket.getInetAddress().getHostAddress());}}
}

项目实战

获取时间

// LocalDate LocalTime LocalDateTime 获取此刻日期时间对象
LocalDateTime now = LocalDateTime.now();
System.out.println(now);
System.out.println(now.getYear());
System.out.println(now.getDayOfYear());// 格式化:DateTimeFormatter
// 1、创建一个格式化对象
DateTimeFormatter dtf = DateTimeFormatter.ofPattern("yyyy/MM/dd HH:mm:ss EEE a");
// 2、格式化now对象的时间
String result2 = dtf.format(now);
System.out.println(result2);

在这里插入图片描述

StringBuilder

StringBuilder 代表可变字符串对象,相当于是一个容器。
在这里插入图片描述

StringBuilder sb = new StringBuilder(); // StringBuilder对象是可变内容的容器  sb = "";
for (int i = 0; i < 1000000 ; i++) {sb.append("abc");
}
System.out.println(sb);
// StringBuilder只是拼接字符串的手段,结果还是要恢复成字符串(目的)
String s = sb.toString();
System.out.println(s);StringBuilder sb2 = new StringBuilder();
String result = sb2.append("张三").append("李四").append("王五").toString();
System.out.println(result);

在这里插入图片描述

BigDecimal

用于解决浮点型运算时,出现结果失真的问题。
在这里插入图片描述

double a = 0.1;
double b = 0.2;
System.out.println(a + b); // 0.30000000000000004// 直接调用valueOf方法,内部使用的就是public BigDecimal(String val) 字符串构造器
BigDecimal a1 = BigDecimal.valueOf(a);
BigDecimal b1 = BigDecimal.valueOf(b);
BigDecimal c1 = a1.add(b1);  // 解决精度问题的手段
double result = c1.doubleValue();  // 把BigDecimal对象转成double类型
System.out.println(result);System.out.println("------------");BigDecimal i = BigDecimal.valueOf(0.1);
BigDecimal j = BigDecimal.valueOf(0.3);
// 除法
BigDecimal k = i.divide(j, 2, RoundingMode.HALF_UP);
System.out.println(k);

源码:
在这里插入图片描述

运行结果:
在这里插入图片描述

相关文章:

  • HTML实战:响应式个人资料页面
  • 【容器】docker使用问题处理
  • Eclipse 插件开发 5.3 编辑器 监听输入
  • React Context 与状态管理:用与不用
  • 【数据分析】特征工程-特征选择
  • JS语言基础
  • 【华为战报】4月、5月 HCIP考试战报!
  • 将 AI 解答转换为 Word 文档
  • 题目 3314: 蓝桥杯2025年第十六届省赛真题-魔法科考试
  • 一篇学习CSS的笔记
  • 【深度学习】10. 深度推理(含链式法则详解)RNN, LSTM, GRU,VQA
  • 进阶知识:Selenium底层原理深度解析
  • 如何设计高效的数据湖架构:存储策略、Schema 演进与数据生命周期管理
  • Flask集成Selenium实现网页截图
  • 使用 Selenium 进行自动化测试:入门指南
  • 物流项目第九期(MongoDB的应用之作业范围)
  • 【unity游戏开发——编辑器扩展】EditorUtility编辑器工具类实现如文件操作、进度条、弹窗等操作
  • STM32学习笔记---时钟树
  • 《基于AIGC的智能化多栈开发新模式》研究报告重磅发布! ——AI重塑软件工程,多栈开发引领未来
  • PHP+MySQL开发语言 在线下单订水送水小程序源码及搭建指南
  • 如何做属于自己的网站/优化系统软件
  • 建设网站的个人心得体会/奇零seo赚钱培训
  • 网站建设专业导航网站/百度关键字搜索排名
  • 个人建站提供软件下载/软文自助发稿平台
  • 无线网络优化是做什么的/站长工具seo综合查询5g
  • discuz做视频网站/国际网络销售平台有哪些