【网络编程】Java网络编程中的基本概念及实现UDP、TCP客户端服务器程序(万字博文)

 系列文章目录

【网络通信基础】网络中的常见基本概念

【网络编程】Java网络编程中的基本概念及实现UDP、TCP客户端服务器程序(万字博文)

【网络原理】UDP协议的报文结构 及 校验和字段的错误检测机制(CRC算法、MD5算法)


目录

一、什么是网络编程?

二、网络编程中的基本概念

1. 客户端和服务器

2. 请求和响应

三、Socket套接字

UDP数据报套接字编程

1. DatagramSocket

2. DatagramPacket

3. UDP回显客户端服务器程序

4. UDP字典客户端服务器程序

TCP流套接字编程

1. ServerSocket

2. Socket

3. TCP回显客户端服务器程序

4. 服务器引入多线程

5. 服务器引入线程池

6. TCP字典客户端服务器程序 


一、什么是网络编程?

网络编程,指网络上的主机,通过不同的进程,以编程的方式实现网络通信(或称网络数据传输)

即便是同一个主机,只要是不同进程,基于网络来传输数据,也属于网络编程。但是,我们的目的是提供网络上的不同主机,基于网络来传输数据资源。

网络编程,本质上就是学习“传输层”给“应用层”提供的API,通过代码的形式,把数据交给传输层,进一步的通过层层封装,就可以把数据通过网卡发送出去了。

二、网络编程中的基本概念

1. 客户端和服务器

客户端:主动发起通信的一方,称为客户端.

服务器:被动接受的一方,称为服务器,可以提供对外服务.

同一个程序在不同场景中,可能是客户端也可能是服务器(服务器可能还需要主动向别的服务器发起通信,此时的服务器相对于被发起通信的服务器来说,就是客户端).

2. 请求和响应

请求(request):客户端给服务器发送数据。

响应(response):服务器给客户端返回数据。

一般来说,获取一个网络资源,涉及到两次网络数据传输:

  • 第一次:请求数据的发送.
  • 第二次:响应数据的发送.

就比如在快餐店点一份炒饭:

先要发起请求:点一份炒饭;再有快餐店提供的对于响应:提供一份炒饭。

三、Socket套接字

Socket套接字,是由系统提供的用于网络编程的技术,是基于TCP/IP协议的网络通信的基本操作单元。基于Socket套接字的网络程序开发就是网络编程。

前面说过,要想进行网络编程,需要使用的系统API,本质上是由传输层提供的。

传输层涉及到的主要协议有两个:

  1. 流套接字:TCP(传输控制协议)
  2. 数据报套接字:UDP(用户数据报协议)

TCP的特点:                                                                 

  • 有连接(类似于打电话,需要接通才能通信)             
  • 可靠传输(尽可能完成数据传输,起码可以知道当前这个数据对方是否接收到了)                       
  • 面向字节流(此处字节流和文件中的字节流完全一致,网络中传输数据的基本单位就是字节)
  • 全双工(一个信道,可以双向通信)                            

 UDP的特点:

  • 无连接(类似于发微信/短信,直接发送过去)            
  • 不可靠传输(没有确认、重传机制;如果因为网络故障等原因,数据无法发到对方,UDP协议层也不会给应用层返回任何错误信息)
  • 面向数据报(每次传输的基本单位是一个数据报(由一系列的字节构成的)特定的结构)
  • 全双工(一个信道,可以双向通信)     

UDP数据报套接字编程

UDP socket API的使用

1. DatagramSocket

DatagramSocket 是 UDP Socket(套接字),用于发送和接收UDP数据报

构造方法:

重要方法:

2. DatagramPacket

DatagramPacket 是 UDP Socket(套接字)发送和接收的数据报(每次发送接收数据的基本单位)

构造方法:

3. UDP回显客户端服务器程序

通过这个程序,了解 socket api 的使用,和典型的客户端服务器基本工作流程。

对于服务器,需要指定端口号来创建 socket (类似于饭店,需要指定具体位置),主要流程如下:

  1. 接收客户端请求,并解析
  2. 根据请求,计算出响应(回显服务器,则是直接将请求的数据返回)
  3. 将响应写回给客户端

注意事项详见代码注释: 

import java.io.IOException;
import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.net.SocketException;//Udp回显服务器
public class UdpEchoServer {//Udp套接字private DatagramSocket socket;public UdpEchoServer(int port) throws SocketException {socket = new DatagramSocket(port); //服务器:指定端口号创建}public void start() throws IOException {System.out.println("服务器启动");while (true) {//1.接收客户端的请求,并解析DatagramPacket requestServer = new DatagramPacket(new byte[4096], 4096);socket.receive(requestServer);//2.根据请求,计算出响应String request = new String(requestServer.getData(), 0, requestServer.getLength());String response = process(request);//3.将响应写回给客户端(需要指定发送到的IP地址及端口号)DatagramPacket responseServer = new DatagramPacket(response.getBytes(), response.getBytes().length, requestServer.getSocketAddress());socket.send(responseServer);//打印日志System.out.printf("[%s:%d] request:%s response:%s\n",responseServer.getAddress(), responseServer.getPort(), request, response);}}//根据请求计算响应(由于是回显程序,直接返回请求的内容)public String process(String request) {return request;}public static void main(String[] args) throws IOException {UdpEchoServer udpEchoServer = new UdpEchoServer(9090);udpEchoServer.start();}
}

对于客户端,服务器的端口号可以由系统随机分配,但需要知道服务器的IP地址及端口号(去饭店吃饭,需要知道饭店的地址及具体是哪个门店),主要流程如下:

  1. 客户端读取用户请求
  2. 构造请求的数据报,并发送到服务器(此时就需要指定服务器的IP地址及端口号)
  3. 读取服务器的响应,并解析出响应的内容
  4. 输出服务器的响应
import java.io.IOException;
import java.net.*;
import java.util.Scanner;//Udp回显客户端
public class UdpEchoClient {private DatagramSocket socket;private String address;private int port;//客户端需要知道服务器的IP地址及端口号public UdpEchoClient(String address, int port) throws SocketException {this.address = address;this.port = port;socket = new DatagramSocket(); //服务器:随机端口号创建}public void start() throws IOException {System.out.println("客户端启动");Scanner in = new Scanner(System.in);while (true) {System.out.print("-> ");if (!in.hasNext()) {break;}//1.控制台读取请求内容String request = in.next();//2.构造请求的数据报,并发送到服务器(需要指定目的IP地址和目的端口号发送请求)DatagramPacket requestPacket = new DatagramPacket(request.getBytes(), request.getBytes().length, InetAddress.getByName(address), port);socket.send(requestPacket);//3.读取服务器的响应,并解析出响应的内容DatagramPacket responsePacket = new DatagramPacket(new byte[4096], 4096);socket.receive(responsePacket);String response = new String(responsePacket.getData(), 0, responsePacket.getLength());//4.将响应内容输出到控制台System.out.println(response);}}public static void main(String[] args) throws IOException {UdpEchoClient udpEchoClient = new UdpEchoClient("127.0.0.1", 9090);udpEchoClient.start();}
}

先运行服务器,再运行客户端,看程序的执行效果:

  • 可以看到,服务器端能够正确接收到请求并作出响应,并打印出日志(客户端IP,客户端端口号,请求内容,响应内容)
  • 客户端也能够正确发送请求,并正确解析服务器端返回的响应

这个程序并不能直接做到“跨主机通信”,因为这台主机可能不能直接访问到另一台主机(NAT机制)。但是可以通过以下手段实现“跨主机通信”:

  1. 将服务器端程序打成 jar 包
  2. 把 jar 包传到云服务器上,并运行

经过这样的操作,其他主机通过运行上述的客户端程序,就能够发起通信了。

4. UDP字典客户端服务器程序

基于上述回显服务器,还可以实现出一些其他带有一点业务逻辑的服务器。

改进成一个“字典服务器”,英译汉的效果。请求是一个英文单词,响应返回对应的中文翻译。

主要逻辑其实和回显服务器基本一致,唯一的区别就在于,服务器端将客户端请求的数据,计算成响应的方式不一致。回显服务器是直接返回客户端请求的数据,这里的字典服务器则是英译汉效果。

而上述代码中,这个根据请求数据计算响应数据的操作,是通过process方法实现的。因此只需要让这个字典服务器继承回显服务器,并重写process方法即可。这里英译汉的业务逻辑通过打表的方式实现。

import java.io.IOException;
import java.net.SocketException;
import java.util.HashMap;
import java.util.Map;//Udp字典服务器
public class UdpDictServer extends UdpEchoServer {Map<String, String> map;public UdpDictServer(int port) throws SocketException {super(port);map = new HashMap<>();map.put("cat", "小猫");map.put("dog", "小狗");map.put("animal", "动物");}//通过重写 计算响应的process方法,达成 英->汉 的效果@Overridepublic String process(String request) {return map.getOrDefault(request, "找不到该单词");}public static void main(String[] args) throws IOException {UdpDictServer udpDictServer = new UdpDictServer(9090);udpDictServer.start();}
}

还是先运行字典服务器,再运行回显客户端(这里客户端是通用的,因为回显客户端只进行发送请求和接收响应并解析的操作),看程序的执行效果:

  • 同样,服务器端能够正确接收到请求、解析请求,并计算出响应、写回给客户端,并打印出日志(客户端IP,客户端端口号,请求内容,响应内容)
  • 客户端也能够正确发送请求,并正确解析服务器端返回的响应

TCP流套接字编程

1. ServerSocket

ServerSocket 类是创建TCP服务器端Socket的API. (只能给服务器端使用)

构造方法:

重要方法:

2. Socket

Socket 类用于创建客户端 Socket,或服务器端中接收到客户端建立连接(accept方法)的请求后,返回的服务端Socket. (服务器端和客户端都能使用)

构造方法:

重要方法:

3. TCP回显客户端服务器程序

使用TCP协议实现回显客户端服务器程序。与UDP协议实现的最大区别是,TCP是有连接的,和打电话一样,需要一方(客户端)拨号,一方(服务器)接通,因此TCP协议首要操作就是等待客户端连接。

和UDP回显服务器一样,对于这里的服务器,同样需要指定端口号创建TCP服务器端Socket,即ServerSocket。

  1. 服务器启动后,就需要监听当前绑定端口(accept方法),等待客户端连接。
  2. 当成功建立连接后,会返回一个Socket对象。这个对象保存了对端信息,即客户端信息,可以用来接收和发送数据(TCP是面向字节流的,通过这个Socket对象获取对应输入流和输出流,通过输入输出流进行对 socket 的读写,达成接收和发送数据的功能)。

后续流程和UCP回显服务器一致。此处由于每有一个客户端连接,就会有一个clientSocket,这里消耗的Socket会越来越多,因此每当一个客户端连接结束,就需要释放这个clientSocket。

import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.io.PrintWriter;
import java.net.ServerSocket;
import java.net.Socket;
import java.util.Scanner;//TCP回显服务器
public class TcpEchoServer {private ServerSocket serverSocket;public TcpEchoServer(int port) throws IOException {//指定服务器端口号,创建一个serverSocketserverSocket = new ServerSocket(port);}public void start() throws IOException {System.out.println("服务器启动!");while (true) {//监听当前绑定的端口,等待客户端连接 连接后,返回一个socket,里面保存客户端(对端)信息Socket clientSocket = serverSocket.accept();processConnection(clientSocket);}}private void processConnection(Socket clientSocket) throws IOException {System.out.printf("[%s:%d] 客户端上线\n", clientSocket.getInetAddress(), clientSocket.getPort());try (InputStream inputStream = clientSocket.getInputStream();OutputStream outputStream = clientSocket.getOutputStream()) {while (true) {//1.读取客户端请求的数据//利用scanner读取客户端输入的信息Scanner scanner = new Scanner(inputStream);if (!scanner.hasNext()) {System.out.printf("[%s:%d] 客户端下线\n",clientSocket.getInetAddress(), clientSocket.getPort());break;}//这里的next()需要遇到\n才停止,因此需要对端写入的时候,要同时写入\n换行符String request = scanner.next();//2.解析请求的数据,并计算出响应String response = process(request);//3.将响应写回到客户端//outputStream.write(response.getBytes(), 0, response.getBytes().length);PrintWriter writer = new PrintWriter(outputStream);writer.println(response);writer.flush();//打印日志System.out.printf("[%s:%d] request:%s response:%s\n",clientSocket.getInetAddress(), clientSocket.getPort(), request, response);}} catch (IOException e) {throw new RuntimeException(e);} finally {clientSocket.close();}}//回显服务器,直接返回原数据public String process(String request) {return request;}public static void main(String[] args) throws IOException {TcpEchoServer tcpEchoServer = new TcpEchoServer(9090);tcpEchoServer.start();}
}

对于客户端,需要指定服务器的IP和端口号建立连接,即使用 Socket(String host, int port) 创建Socket的时候,就开始发起与对应服务器建立连接的请求了。

主要流程和UDP回显客户端程序的流程也基本一致,只需要注意请求和响应数据的方式是不同的,是通过操作输入输出流完成的即可。

import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.io.PrintWriter;
import java.net.InetAddress;
import java.net.Socket;
import java.util.Scanner;//TCP回显客户端
public class TcpEchoClient {private Socket clientSocket;//需要指定服务器的IP和端口号public TcpEchoClient(String serverAddress, int serverPort) throws IOException {//与对应客户端建立连接clientSocket = new Socket(InetAddress.getByName(serverAddress), serverPort);}public void start() {System.out.println("客户端启动!");try (Scanner scannerConsole = new Scanner(System.in);InputStream inputStream = clientSocket.getInputStream();OutputStream outputStream = clientSocket.getOutputStream()) {while (true) {//1.用户从控制台输入数据System.out.print("-> ");String request = scannerConsole.next();//2.将该数据作为请求,发送给服务器//outputStream.write(request.getBytes(), 0, request.getBytes().length);//outputStream.write('\n');PrintWriter writer = new PrintWriter(outputStream);writer.println(request);writer.flush(); //刷新缓冲区,确保数据发送出去//3.读取服务器的响应,并解析响应的内容Scanner scannerNetwork = new Scanner(inputStream);String response = scannerNetwork.next();//4.将响应输出到控制台System.out.println(response);}} catch (IOException e) {throw new RuntimeException(e);}}public static void main(String[] args) throws IOException {TcpEchoClient tcpEchoClient = new TcpEchoClient("127.0.0.1", 9090);tcpEchoClient.start();}
}

先运行服务器,再运行客户端,看执行效果:

可以看到,服务器和客户端都能满足我们的需求,但这里其实还存在一个问题。

当我们开启多个客户端想要进行连接通信时,只有第一个连接到的客户端才能正确通信,其他的客户端是没有反应的。要想某个客户端能正常通信,只有当其他客户端都下线(结束程序),这个客户端才能接收到响应数据。

可以看到,此处的这个客户端并没有正确通信,当另一个客户端下线之后,该客户端此前发送的数据又正常请求并响应了。

分析过程:

  • 第一个客户端连上服务器之后,服务器就会从accept这里返回(解除阻塞),然后进入到processConnection方法中.
  • 接下来服务器就会在processConnection循环处理客户端的请求,只有当客户端退出之后,连接结束,才会退出循环.
  • 而服务器在循环处理客户端请求的时候,第二个客户端发起连接请求,而服务器这里并不能执行到accept。因此并不能成功连接,只有当客户端退出,才会执行回到accept进行连接.

第二个客户端之前发的请求为什么能被立即处理?

  • 当前TCP在内核中,每个 socket 都是由缓冲区的。客户端发送的数据通过客户端代码,已经写入到该客户端的输出流(缓冲区)了,这里数据确实发送了,只不过数据在服务器的接收缓冲区中。
  • 一旦第一个客户端退出,回到第一层循环,执行accept连接操作,后续processConnection方法里的 next 就能把之前缓冲区的内容给读出来。

解决上述问题的核心思路就是使用多线程:

  • 单个线程,无法既能给客户端提供服务,又能去快速执行到第二次 accept 方法进行连接。
  • 通过引入多线程,让主线程只负责执行 accept。每次有一个客户端连接上来,就分配一个新的线程,由新的线程负责给客户端提供服务。

由于这里不涉及多个线程修改共享变量,因此没有线程安全问题,我们只需要改动 start 方法即可。

4. 服务器引入多线程

    //多线程public void start() throws IOException {System.out.println("服务器启动!");while (true) {//监听当前绑定的端口,等待客户端连接 连接后,返回一个socket,里面保存客户端(对端)信息Socket clientSocket = serverSocket.accept();Thread t = new Thread(() -> {try {processConnection(clientSocket);} catch (IOException e) {throw new RuntimeException(e);}});t.start();}}

通过引入多线程,这里的服务器就能支持多个客户端同时与其通信了。

上述问题,不是TCP引起的,而是代码两次循环嵌套引起的,UDP服务器,就是只有一层循环,因此不会有这个问题。

而这个多线程版本同样还有一些问题: 

  • 每有一个客户端连接,就会创建一个新的线程,每当这个客户端结束,就要销毁这个线程。
  • 如果客户端比较多,并且频繁连接、关闭,就会使服务器频繁创建和销毁线程。

前面讲过,线程池解决的就是线程频繁创建和销毁的问题,因此,这里的优化方案就是使用线程池。

5. 服务器引入线程池

    public void start() throws IOException {System.out.println("服务器启动!");ExecutorService threadPool = Executors.newCachedThreadPool();while (true) {//监听当前绑定的端口,等待客户端连接 连接后,返回一个socket,里面保存客户端(对端)信息Socket clientSocket = serverSocket.accept();threadPool.submit(new Runnable() {@Overridepublic void run() {try {processConnection(clientSocket);} catch (IOException e) {throw new RuntimeException(e);}}});}}

线程开销问题解决了。但是,如果当前的场景使线程频繁创建,但是不销毁呢?

  • 这种情况下,如果继续使用多线程/线程池,就会导致当前服务器积累大量的线程,此时,对于服务器的负担是非常重的。

要解决这个新问题,还可以引入其他的方案:

  1. 协程:轻量级线程,本质上还是一个线程,用户态可以通过手动调度的方式让这一个线程“并发”执行多个任务。
  2. IO 多路复用:系统内核级别的机制,本质上是让一个线程同时去负责处理多个socket。

6. TCP字典客户端服务器程序 

同UDP字典客户端服务器程序:

import java.io.IOException;
import java.util.HashMap;
import java.util.Map;//TCP字典服务器
public class TcpDictServer extends TcpEchoServer {Map<String, String> map;public TcpDictServer(int port) throws IOException {super(port);map = new HashMap<>();map.put("cat", "小猫");map.put("dog", "小狗");map.put("animal", "动物");}@Overridepublic String process(String request) {return map.getOrDefault(request, "未找到该单词");}public static void main(String[] args) throws IOException {TcpDictServer tcpDictServer = new TcpDictServer(9090);tcpDictServer.start();}
}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://xiahunao.cn/news/2979937.html

如若内容造成侵权/违法违规/事实不符,请联系瞎胡闹网进行投诉反馈,一经查实,立即删除!

相关文章

基于CAPL的RSA文件解析

&#x1f345; 我是蚂蚁小兵&#xff0c;专注于车载诊断领域&#xff0c;尤其擅长于对CANoe工具的使用&#x1f345; 寻找组织 &#xff0c;答疑解惑&#xff0c;摸鱼聊天&#xff0c;博客源码&#xff0c;点击加入&#x1f449;【相亲相爱一家人】&#x1f345; 玩转CANoe&…

施耐德EOCR漏电型保护器有多款不同型号,每款都有其独特的特点和应用场景。以下是一些常见的型号及其区别

施耐德EOCR漏电型保护器有多款不同型号&#xff0c;每款都有其独特的特点和应用场景。以下是一些常见的型号及其区别&#xff1a; EOCR-DG/DZ经济型漏电保护器&#xff1a;这款保护器是具有剩余电流检测型接地故障保护的复合继电器&#xff0c;内置MCU&#xff0c;具有过流、缺…

【java配置】jpcap的下载与idea配置

解决报错&#xff1a;Cannot resolve symbol ‘jpcap’ 1. jpcap的下载 官网下载链接 百度网盘下载 双击WinpPca安装&#xff0c;jacap1和jpcap2任选其中之一 2. idea配置 &#xff08;1&#xff09;查看当前使用jdk目录 File -> Project Settings -> SDKs &#…

Modbus转Profinet网关接称重设备与工控机通讯

Modbus转Profinet网关&#xff08;XD-MDPN100&#xff09;是一种能够实现Modbus协议和Profinet协议之间转换的设备。Modbus转Profinet网关可提供单个或多个RS485接口&#xff0c;使得不同设备之间可以顺利进行通信&#xff0c;进一步提升了工业自动化程度。 通过使用Modbus转Pr…

Git 核心概念与实操

这里写目录标题 1 版本回退2 工作区、暂存区、本地仓库、远程仓库 1 版本回退 原文链接&#xff1a;https://www.liaoxuefeng.com/wiki/896043488029600/897013573512192 首先 git log 查看提交记录 在Git中&#xff0c;用 HEAD 表示当前版本 上一个版本就是 HEAD^ &#xff…

自适应STFT及其在地震时间行程自动拾取中的应用【附MATLAB代码】

文章来源&#xff1a;微信公众号&#xff1a;EW Frontie 摘要 在本文中&#xff0c;首先&#xff0c;我们提出的方法来产生高分辨率的短时傅里叶变换&#xff0c;通过计算最佳瞬时窗口长度。其次&#xff0c;利用生成的时频图提取瞬时走时属性&#xff0c;实现地震同相轴走时的…

ZNS SSD+F2FS文件系统|如何降低GC开销?---1

ZNS出现的背景是什么&#xff1f;ZNS SSD的原理是把namespace空间划分多个zone空间&#xff0c;zone空间内部执行顺序读写。 在ZNS的场景下&#xff0c;不同应用按照Zone配置信息&#xff0c;相应存放业务数据。由于是Host管理数据的摆放和存取位置&#xff0c;会最大程度减少G…

【操作系统复习之路】存储器管理(第四章 超详细讲解)

目录 一、存储器的层次结构 二、程序的装入和链接 2.1 逻辑地址和物理地址 2.2 绝对装入方式 2.3 可重定位装入方式 2.4 动态运行时装入方式 2.5 静态链接 2.6 装入时动态链接 2.7 运行时动态链接 三、连续分配存储器管理方式 3.1 单一连续分配 3.2 固定分区分配 …

田忌赛马【洛谷P1650】

P1650 田忌赛马 - 洛谷 | 计算机科学教育新生态 (luogu.com.cn) #include<iostream> #include <algorithm> #include<cstdio> #include <map> using namespace std; const int N1e5100; int n; map<int,int>a,b;//映射&#xff0c;速度->数量…

程序员缓解工作压力小技巧

文章目录 1. 规划时间和任务2. 学会放松和调节情绪3. 培养兴趣爱好4. 保持健康的生活方式总结 当面对程序员这样需要高度精神集中和持续创新的工作时&#xff0c;缓解工作压力是至关重要的。下面分享一些我个人的经验和方法&#xff0c;希望能对大家有所帮助&#xff1a; 1. 规…

陆金所控股一季报到底是利好还是利空?

3月底&#xff0c;陆金所控股&#xff08;LU.N;06623.HK&#xff09;因其特别分红方案受到市场高度关注。但在4月23日发布的2024年一季度财报中&#xff0c;陆金所控股营收同比下降30.9%&#xff0c;净亏损8.3亿元。 两者对比&#xff0c;外界不由得对公司的经营状况产生疑惑。…

【python】Python学生信息管理系统(源码+报告+本地存储)【独一无二】

&#x1f449;博__主&#x1f448;&#xff1a;米码收割机 &#x1f449;技__能&#x1f448;&#xff1a;C/Python语言 &#x1f449;公众号&#x1f448;&#xff1a;测试开发自动化【获取源码商业合作】 &#x1f449;荣__誉&#x1f448;&#xff1a;阿里云博客专家博主、5…

自己手动在Linux上实现一个简易的端口扫描器

背景 常常听到网络攻击有一个东西叫做端口扫描器&#xff0c;可以扫描指定服务器开放的端口&#xff0c;然后尝试连接&#xff0c;并寻找漏洞&#xff0c;最终攻破服务器。而那些使用的端口扫描器都是一个个现成的程序&#xff0c;看上去很厉害的样子。而实际上这些东西对于懂…

【进收藏夹吃灰系列】数据库学习指南

文章目录 [toc]MySQLRedisMongoDB 个人主页&#xff1a;丷从心 系列专栏&#xff1a;进收藏夹吃灰系列 MySQL 博客标题博客url【Python基础】MySQLhttps://blog.csdn.net/from__2024_04_11/article/details/137888646?spm1001.2014.3001.5502 Redis 博客标题博客url【Pytho…

基于 SpringCloud 的在线交易平台商城的设计与实现

摘 要 随着互联网的快速发展&#xff0c;人们对商品经济的消费和思考不再停留在传统 的经济模式上&#xff0c;网上购物商城是企业与企业进行、企业与消费者进行电子商 务交易的一个很好平台。网上购物商城极大地降低了企业商家的交易成本&#xff0c; 缩短企业供应链周期&…

混合现实(MR)技术的应用场景

混合现实&#xff08;MR&#xff09;技术将虚拟世界和现实世界融合在一起&#xff0c;用户可以在现实世界中看到和与虚拟物体进行交互&#xff0c;同时还可以感知周围的真实环境。MR技术具有广阔的应用前景&#xff0c;可以应用于各行各业。以下是一些MR的应用场景。北京木奇移…

【科学研究】饭圈女孩的流量战争

::: block-1 “时问桫椤”是一个致力于为本科生到研究生教育阶段提供帮助的不太正式的公众号。我们旨在在大家感到困惑、痛苦或面临困难时伸出援手。通过总结广大研究生的经验&#xff0c;帮助大家尽早适应研究生生活&#xff0c;尽快了解科研的本质。祝一切顺利&#xff01;—…

【C/C++笔试练习】线程作用、磁盘的固定块、多进程、进行调度、cache、内存抖动、非抢占CPU调度、inode描述、文件操作、进制、最难的问题、因子个数

文章目录 C/C笔试练习选择部分&#xff08;1&#xff09;线程作用&#xff08;2&#xff09;磁盘的固定块&#xff08;3&#xff09;多进程&#xff08;4&#xff09;进行调度&#xff08;5&#xff09;cache&#xff08;6&#xff09;内存抖动&#xff08;7&#xff09;非抢占…

【Linux高性能服务器编程】两种高性能并发模式剖析——半同步/半异步模式

hello &#xff01;大家好呀&#xff01; 欢迎大家来到我的Linux高性能服务器编程系列之两种高性能并发模式介绍&#xff0c;在这篇文章中&#xff0c;你将会学习到高效的创建自己的高性能服务器&#xff0c;并且我会给出源码进行剖析&#xff0c;以及手绘UML图来帮助大家来理解…

232 基于matlab的MIMO雷达模型下一种子空间谱估计方法

基于matlab的MIMO雷达模型下一种子空间谱估计方法&#xff0c;采用过估计的方法&#xff0c;避免了信源数估计的问题&#xff0c;对数据协方差矩阵进行变换&#xff0c;构造信号子空间投影矩阵和噪声子空间投影矩阵&#xff0c;不需要像经典的MUSIC一样对其进行特征分解&#x…