网络编程(JavaEE初阶系列10)

目录

前言:

1.网络编程的基础

1.1为什么需要网络编程

1.2什么是网络编程

1.3网络编程中的基本概念

1.3.1发送端和接收端

1.3.2请求和响应

1.3.3客户端和服务端

2.Socket套接字

2.1概念

2.2分类

3.UDP数据报套接字编程

3.1DataGramSocket API

3.2DatagramPacket API

3.3基于UDP的回显服务器(echo server)

3.4简单的翻译服务器

4.TCP流套接字

4.1ServerSocket API

4.2Socket API

4.3基于TCP的回显程序

5.再谈协议

结束语:


前言:

在上一节中小编主要是与大家分享了一些有关于网络的基础知识,但是里面的细节和基础的编程还没有给大家来交代,这节中小编就给大家俩交代一下有关于网络基础编程方面的一些基础的编程吧,大家赶快跟上小编的步伐一起来往下看吧。如果还没有看小编网络基础知识的部分的同学建议先去看看这篇博文吧:☞http://t.csdn.cn/aj9ov

1.网络编程的基础

1.1为什么需要网络编程

用户在浏览器中,打开在线视频网站,比如抖音短视频其实质是通过网络,获取到网络上的一个视频资源,与本地打开视频文件类似,只是视频文件这个资源的来源是网络,相比本地资源来说,网络提供了更为丰富的网络资源,所谓的网络资源,其实就是在网络中可以获取的各种数据资源,而所有的网络资源,都是通过网络编程来进行数据传输的。

1.2什么是网络编程

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

当然,我们只要满足进程不同就行,所以即便是同一个主机,只要是不同进程,基于网络来传输数据,也属于网络编程。

1.3网络编程中的基本概念

1.3.1发送端和接收端

在一次网络数据传输时:

  • 发送端:数据的发送方进程,称为发送端。发送端主机即网络通信中的源主机。
  • 接收端:数据的接收方进程,称为接收端。接收端主机即网络通信中的目的主机。
  • 收发端:发送端和接收端两端,也简称Wie收发端。

注意:发送端和接收端只是相对的概念,只是一次网络数据传输产生数据流向后的概念。

1.3.2请求和响应

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

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

就像是在餐厅点饭一样,先发起请求:点一份蛋炒饭。餐厅在给一个响应:提供一份蛋炒饭。

1.3.3客户端和服务端

  • 服务端:在常见的网络数据传输的场景下,把提供服务的这一方进程,称为服务端,可以提供对外服务。
  • 客户端:获取服务的一方进程,称为客户端。

对于服务来说,一般是提供:

  • 客户端获取服务资源。
  • 客户端保存资源在服务端。

就像是我们在银行办事:

  • 银行提供存款服务:用户(客户端)保存现金(资源)在银行(服务端)。
  • 银行提供取款服务:用户(客户端)获取服务端资源(银行替用户保管现金)。

2.Socket套接字

2.1概念

Socket套接字是操作系统提供给应用程序的一组用于网络编程的API。他是基于TCP/IP协议的通信的的基本操作单元。

注意:操作系统原生的Socket API是C语言的但是这里我们学习的是Java封装之后的版本。

2.2分类

Socket套接字主要针对传输层协议划分为如下三类:

  • 数据报套接字:使用传输层UDP协议。

UDP,即User Datagram Protocol(用户数据报协议),传输层协议。它的特点是:无连接、不可靠传输、面向数据报、全双工。

  • 流套接字:使用传输层TCP协议。

TCP,即Transmission Control Protocol(传输控制协议),传输层协议。它的特点是:有连接、可靠传输、面向字节流、全双工。

对于字节流来说,可以简单理解为传输的数据是基于IO流的,流式数据的特征就是在IO流没哟关闭的情况下,是无边界的数据,可以多次发送,也可以分开多次接收。

  • 原始套接字:

原始套接字用于自定义传出层协议,用于读写内核没有处理的IP协议数据,这里我们对此不做过多讨论,我们重点是理解和应用前两个。

TCP特点vsUDP特点:

UDP特点TCP特点
无连接:使用UDP通信双方不需要刻意保存对方的相关信息有连接:使用TCP通信双方则需要刻意保存对方的相关信息
不可靠传输:消息发了就发了不关注结果可靠传输:不是说发送之后对方就可以100%能够达到对方,这要求就太高了,只是说尽可能的传输过去。
面向数据报:以UDP数据报为基本单位。面向字节流:以字节为传输的基本单位,读写方式非常灵活
全双工:一条路径,双向通信全双工:一条路径,全向通信。

解释:全双工vs半双工。

  • 全双工:是一条路径,全向通信,你可以理解为,一个双向通道的马路。
  • 半双工:是一条路径,只能由一侧向另一侧通信,你可理解为单向通道的马路。

针对上述的TCP协议和UDP协议也给我们提供了两组不同的API。下面我们来一步一步的了解一下。

3.UDP数据报套接字编程

3.1DataGramSocket API

DataGramSocket 是UDP Socket,用于发送和接收UDP数据报,所谓Socket,是一个特殊的文件,是网卡这个硬件设备的抽象表示,你也可以理解为是一个遥控器,想要进行网络通信就需要有socket文件这样的对象,借助这个socket文件对象,才能够间接的操作网卡。

  • 往这个socket对象里写数据,相当于通过网卡发送消息。
  • 从这个socket对象中读数据,相当于通过网卡接收消息。

DatagramSocket的构造方法,可以绑定一个端口号(服务器),也可以不显示指定客户端。

方法签名方法说明
DatagramSocket()创建一个UDP数据报套接字的Socket,绑定到本机任意一个随机端口(一般用于客户端)
DatagramSocket(int port)创建一个UDP数据报套接字的Socket,绑定到本机指定的端口(一般用于服务端)
  • 服务器这边的socket往往要关联一个具体的端口号。
  • 客户端这边则不需要手动指定,系统会自动分配一个闲置的端口号。

举个例子:

比如现在我开了一家餐厅,要发传单,那么在传单上面我这边可定是要标清楚我的餐厅的具体位置在哪,窗口号是多少,都得事先分配好,此时我开的这家餐馆就相当于是服务器,确定的地址和窗口号就是服务器事先分配好的端口号。那么如果此时客人看到我发的传单就来到我的餐馆吃饭了,那么它点完餐之后,就会随便找一个空着的位置坐下,等饭。此时客人就相当于是客户端,随便找的位置就是系统给随机分配的一个空闲的端口号。

 DatagramSocket方法

方法签名方法说明
void receive(DatagramPacket p)从此套接字接收数据报(如果没有接收到数据报,该方法会阻塞等待)
void send(DatagramPacket p)从此套接字发送数据报(不会阻塞等待,直接发送)
void close()关闭数据报套接字(释放资源)

注意:

  • DatagramPacket表示一个UDP数据报。
  • 在close的时候,到底啥时候调用close,一定是要socket/文件,确定一定以及肯定不再使用,此时才能调用close。

3.2DatagramPacket API

DatagramPacket是UDPSocket发送和接收的数据报。

DatagramPacket构造方法

方法签名方法说明
DatagramPacket(byte[] buf, int length)构造一个DatagramPacket以用来接收数据报,接收的数据保存在字节数组(第一个参数buf)中,接收指定长度(第二个参数length)
DatagramPacket(byte[] buf, int offset, int lenght, SocketAddress address)构造一个DatagramPacket以用来发送数据报,发送的数据为字节数组(第一个参数buf)中,从0到指定长度(第二个参数length),address指定目的的主机的IP和端口号。

 DatagramPacket方法

方法签名方法说明
InetAddress getAddress()从接收的数据报中,获取发送端主机IP地址,或从发送的数据报中,获取接收端主机IP地址。
int getPort()从接收的数据报中,获取发送端主机的端口号,或从发送的数据报中,获取接收端主机端口号。
byte[] getData()获取数据报中的数据。

3.3基于UDP的回显服务器(echo server)

介绍了DatagramSocket 和 DatagramPacket API之后,我们基于UDP socket写一个简单的客户端服务器程序。 也就是让客户端发一个请求,在服务器上返回一个一模一样的响应。

首先来明确一点,一个服务器主要做的三个核心的工作:

  1. 读取请求并解析。
  2. 根据请求并计算响应。(代码中省略了)
  3. 把响应返回给客户端。

服务端代码:

package network;
//服务端
import java.io.IOException;
import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.net.SocketException;public class UdpEchoServer{//需要先定义一个Socket对象//通过网络通信,必须要使用socket对象private DatagramSocket socket = null;//绑定一个端口,不一定能成功//如果某个端口已经被别的进程占用了,此时这里的绑定操作就会出错。//同一个主机上,一个端口,同一个时刻,只能被一个进程绑定public UdpEchoServer(int port) throws SocketException {//构造socket的同时,指定要关联/绑定的端口。socket = new DatagramSocket(port);}//启动服务器的主逻辑public void start() throws IOException {System.out.println("服务器启动成功!");while (true) {//每次循环,要做三件事//1.读取请求并解析//  构造空饭盒DatagramPacket requestPacket = new DatagramPacket(new byte[4096], 4096);// 食堂大妈给饭盒里打菜(饭从网卡上来)//这里的receive会阻塞等待,等到客户端那边发送数据过来socket.receive(requestPacket);//为了方便处理这个请求,需要把数据报转换成StringString request = new String(requestPacket.getData(), 0, requestPacket.getLength());//2.根据请求计算响应(此处省略这个步骤)String response = process(request);//3.把响应结果写回到客户端// 根据response 字符串,构造一个DatagramPacket// 和请求packet 不同,此处构造响应的时候,需要指定这个包要发给谁,这里调用requestPacket.getSocketAddress()就可以得知了DatagramPacket responsePacket = new DatagramPacket(response.getBytes(), response.getBytes().length,requestPacket.getSocketAddress());socket.send(responsePacket);//打印一下请求的地址和请求的端口号,以及请求的内容和响应的内容System.out.printf("[%s:%d] req: %s, resp: %s\n", requestPacket.getAddress().toString(),requestPacket.getPort(), request, response);}}//这个方法是希望我们根据请求计算响应。//由于咱们写的是个回显程序,请求是啥,响应就是啥//如果后续写一个别的服务器,不再回显了,而是具有具体的业务了,就可以修改process方法//根据需求来重新构造响应//之所以单独列成一个方法,就是想让大家知道这个是一个关键的环节。private String process(String request) {return request;}public static void main(String[] args) throws IOException {UdpEchoServer udpEchoServer = new UdpEchoServer(9090);udpEchoServer.start();}
}

客户端代码:

package network;
//客户端
import java.io.IOException;
import java.net.*;
import java.util.Scanner;public class UdpEchoClient {private DatagramSocket socket = null;private String serverIP;//服务器的地址private int serverPort;//服务器的端口//客户端启动,需要知道服务器在哪里public UdpEchoClient(String serverIP, int serverPort) throws SocketException {//对于客户端来说,不需要显示关联空闲的端口//不代表没有端口,而是系统自动分配了一个空闲的端口socket = new DatagramSocket();this.serverIP = serverIP;this.serverPort = serverPort;}public void start() throws IOException {//通过这个客户端可以多次和服务器进行交互Scanner scanner = new Scanner(System.in);while (true) {//1.先从控制台,读取一个字符串过来//先打印一个提示符,提示用户要输入内容System.out.println("->");String request = scanner.next();//2.把字符串构造成UDP packet,并进行发送DatagramPacket requestPacket = new DatagramPacket(request.getBytes(), request.getBytes().length,InetAddress.getByName(serverIP), serverPort);socket.send(requestPacket);//3.客户端尝试读取服务器返回的响应DatagramPacket responsePacket = new DatagramPacket(new byte[4096], 4096);socket.receive(responsePacket);//4.把响应数据转换成String显示出来String response = new String(responsePacket.getData(), 0, responsePacket.getLength());System.out.printf("req: %s, resp: %s\n", request, response);}}public static void main(String[] args) throws IOException {UdpEchoClient udpEchoClient = new UdpEchoClient("127.0.0.1", 9090);udpEchoClient.start();}
}


启动服务器和客户端结果展示:

 

注意:这里一定是先启动服务器,再启动客户端!!!

执行流程如下所示:

 

注意:在上述过程中我们是客户端和服务器在同一个主机上,使用的是127这个IP,不同主机则就写实际的IP即可。

在上述通信过程中,站在客户端发送数据的角度:

  • 源IP是:127.0.0.1
  • 源端口是:64982,他是系统自动分配的空闲端口。
  • 目的IP是:127.0.0.1
  • 目的端口是:9090

在上述过程中就有同学好奇了不是说是要使用close来关闭资源的吗?为什么在代码中好像没有看到释放资源这一步,其实对于UdpEchoServer来说,这个socket对象是出了循环就不用了,但是循环结束,意味着start结束,意味着main方法结束,同时意味着进程结束,那么此时进程都结束了所以的资源也就自然释放了,所以就不必显示释放资源了。

3.4简单的翻译服务器

在上述中我们编写的是一个回显服务器,它是没有实际意义的。那么如何写一个提供实在价值的服务器呢?当响应和请求不一样了,响应是根据不同的请求计算得到的,这里就需要我们对上述过程没有写的process方法来进行编写,那么下来我们就具体来实现一下。我们就来编写一个简单的英文单词翻译服务器,请求是一个英文单词,响应是这个单词的中文翻译。

服务端代码展示:

package network;
//词典查询服务端
import java.io.IOException;
import java.net.SocketException;
import java.util.HashMap;
import java.util.Map;//使用继承,是为了复用之前的代码
public class UdpDicServer extends UdpEchoServer{private Map<String, String> dict = new HashMap<>();public UdpDicServer(int port) throws SocketException {super(port);dict.put("dog", "小狗");dict.put("cat", "小猫");dict.put("tiger", "老虎");//注意:这里可以无限添加很多个数据}@Overridepublic String process(String request) {return dict.getOrDefault(request,"该单词没有查到!");}public static void main(String[] args) throws IOException {UdpDicServer udpDicServer = new UdpDicServer(9090);udpDicServer.start();}
}


客户端代码展示:

package network;
//客户端
import java.io.IOException;
import java.net.*;
import java.util.Scanner;public class UdpEchoClient {private DatagramSocket socket = null;private String serverIP;//服务器的地址private int serverPort;//服务器的端口//客户端启动,需要知道服务器在哪里public UdpEchoClient(String serverIP, int serverPort) throws SocketException {//对于客户端来说,不需要显示关联空闲的端口//不代表没有端口,而是系统自动分配了一个空闲的端口socket = new DatagramSocket();this.serverIP = serverIP;this.serverPort = serverPort;}public void start() throws IOException {//通过这个客户端可以多次和服务器进行交互Scanner scanner = new Scanner(System.in);while (true) {//1.先从控制台,读取一个字符串过来//先打印一个提示符,提示用户要输入内容System.out.println("->");String request = scanner.next();//2.把字符串构造成UDP packet,并进行发送DatagramPacket requestPacket = new DatagramPacket(request.getBytes(), request.getBytes().length,InetAddress.getByName(serverIP), serverPort);socket.send(requestPacket);//3.客户端尝试读取服务器返回的响应DatagramPacket responsePacket = new DatagramPacket(new byte[4096], 4096);socket.receive(responsePacket);//4.把响应数据转换成String显示出来String response = new String(responsePacket.getData(), 0, responsePacket.getLength());System.out.printf("req: %s, resp: %s\n", request, response);}}public static void main(String[] args) throws IOException {UdpEchoClient udpEchoClient = new UdpEchoClient("127.0.0.1", 9090);udpEchoClient.start();}
}


运行结果展示:

 

注意:在上述编写服务端代码时我们是直接使用了继承,重写了父类的process方法。这样就减少了我们的工作。

4.TCP流套接字

在TCP中有两个核心的类:

  • ServerSocket:是给服务器使用的。
  • Socket:即会给客户端使用,又会给服务器端使用。

下面我们就来分别看看ServerSocket和Socket的具体使用方法。

4.1ServerSocket API

他是创建服务端使用的API。

SocketSocket构造方法:

方法签名方法说明
ServerSocket(int port)创建一个服务流套接字Socket,并绑定到指定端口

SocketSocket方法: 

方法签名方法说明
Socket accept()开始监听指定端口(创建时绑定的端口),有客户端连接后,返回一个服务端Socket对象,并基于该Socket建立与客户端的连接,否则阻塞等待。
void close()关闭此套接字

这里的accept意思就是接收,在客户端主动向服务器发起连接请求,服务器就得同意一下,但是实际上的这个accept又和我们上述给大家解释的意思不太一样,这里的accept只是在应用层面的接收,实际的TCP连接的接受是在该内核里已经完成了。这个后面在将TCP的时候会给大家交代的。 

4.2Socket API

Socket是客户端Socket,或服务端中接收到客户端建立连接(accept方法)的请求后,返回的服务端Socket。

不管是客户端还是服务端Socket,都是双方建立连接以后,保存的对端的信息,及用来与对方收发数据的。

Socket的构造方法:

方法签名方法说明
Socket(String host, int port)创建一个客户端流套接字Socket,并与对应IP的主机上,对应端口的进程建立连接。

这里的host和port指的是服务器的IP和端口,TCP是有连接的,在客户端new Socket对象的时候就会尝试和指定IP端口的目标建立连接了。 

Socket的方法:

方法签名方法说明
InetAddress getInetAddress()返回套接字所连接的地址
InputStream getInputStream()返回此套接字的输入流
OutPutStream getOutStream()返回此套接字的输出流

 InputStream getInputStream()和OutPutStream getOutStream()是字节流,就可以通过上述字节流对象,进行数据传输了。

  • 从 InputStream 这里读数据,就相当于是从网卡接收。
  • 往 OutPutStream 这里写数据,就相当于从网卡发送。

注意:

这个Socket和DatagramSocket定位类似,都是构造的时候指定一个具体的端口,让服务器绑定该端口,但是ServerSocket一定要绑定具体的端口。

4.3基于TCP的回显程序

服务端代码展示 :

package network;
//服务端
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;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;public class TcpEchoServer {//serverSocket只有一个,clientSocket会给每一个客户都分配一个private ServerSocket severSocket = null;public TcpEchoServer(int port) throws IOException {severSocket = new ServerSocket(port);}public void start() throws IOException {ExecutorService executorService = Executors.newCachedThreadPool();System.out.println("服务器启动成功!");while (true) {Socket clientSocket = severSocket.accept();//如果直接调用,该方法会影响这个循环的二次执行,导致accept不及时了。//创建新的线程,用新线程来调用processConnection//每次来一个新的客户端都搞一个新的线程即可!
//            Thread t = new Thread(() -> {
//                try {
//                    processConnection(clientSocket);
//                } catch (IOException e) {
//                    e.printStackTrace();
//                }
//            });
//            t.start();executorService.submit(new Runnable() {@Overridepublic void run() {try {processConnection(clientSocket);} catch (IOException e) {e.printStackTrace();}}});}}//读取这个方法来处理一个连接//读取请求//根据请求计算响应//把响应返回给客户端private void processConnection(Socket clientSocket) throws IOException {System.out.printf("[%s:%d] 客户端上线!\n", clientSocket.getInetAddress().toString(), clientSocket.getPort());//使用try()这种写法,()中允许写多个流对象,使用;来分隔try(InputStream inputStream = clientSocket.getInputStream();OutputStream outputStream = clientSocket.getOutputStream()) {//没有这个scanner和printWriter,完全可以,但是代价就是得一个字节一个字节扣,找到哪个是请求结束的标记\n//不是不能做,而是代替代码比较麻烦//为了简单,把字节流包装成了更方便的字符流Scanner scanner = new Scanner(inputStream);PrintWriter printWriter = new PrintWriter(outputStream);while (true) {//1.读取请求//采用hasNext判定接下来还有没有数据了,如果对端关闭了连接(客户端关闭连接),此时hasNext就会返回false,循环就结束if (!scanner.hasNext()) {//读取的流到了结尾了(对端关闭了)System.out.printf("[%s:%d] 客户端下线了!\n", clientSocket.getInetAddress().toString(), clientSocket.getPort());break;}//直接使用scanner读取一段字符串//next会一直往后读,读到空白符结束(空格、换行、制表符、翻页符...都算空白符)//nextLine只是读到换行符结束,所以这里没有使用它String request = scanner.next();//2.根据请求计算响应String response = process(request);//3.把响应写回给客户端,不要忘记了,响应也是要带上换行的//返回响应的时候要把换行符加回来,方便客户端那边来区分从哪里到哪里是一个完整的响应。printWriter.println(response);//flush当数据不够大的时候直接进行强制刷新,将缓冲区中的数据发给客户端printWriter.flush();System.out.printf("[%s:%d] req: %s; resp: %s\n", clientSocket.getInetAddress().toString(), clientSocket.getPort(), request, response);}}catch (IOException e) {e.printStackTrace();}finally {//clientSocket只是一个连接提供服务的,这个还是要进行关闭的clientSocket.close();}}private String process(String request) {return request;}public static void main(String[] args) throws IOException {TcpEchoServer tcpEchoServer = new TcpEchoServer(9090);tcpEchoServer.start();}
}


客户端代码展示:

package network;
//客户端
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.io.PrintWriter;
import java.net.Socket;
import java.util.Scanner;public class TcpEchoClient {private Socket socket = null;public TcpEchoClient(String serverIP, int port) throws IOException {//这个操作相当于让客户端和服务器建立TCP连接//这里的连接连上了,服务器的accept就会返回socket = new Socket(serverIP, port);}public void start() throws IOException {Scanner scanner = new Scanner(System.in);try (InputStream inputStream = socket.getInputStream();OutputStream outputStream = socket.getOutputStream()) {PrintWriter printWriter = new PrintWriter(outputStream);Scanner scannerFromSocket = new Scanner(inputStream);while (true) {//1.从键盘上读取用户输入的内容System.out.println("->");String request = scanner.next();//2.把读取到的内容构造成请求,发送给服务器//注意,这里的发送,是带有换行的。printWriter.println(request);printWriter.flush();//3.从服务器读取响应的内容String response = scannerFromSocket.next();//4.把响应结果显示到控制台上System.out.printf("req: %s; resp: %s\n", request, response);}}}public static void main(String[] args) throws IOException {TcpEchoClient client = new TcpEchoClient("127.0.0.1", 9090);client.start();}
}

结果展示:

执行流程如下所示:

 

那么这里我们只是启动了一个客户端,在实际中不可能是一个服务器只给一个客户端进行服务,那么如何启动多个客户端呢?这里在idea中是默认下只能启动一个的,那么这里我们需要打开idea配置一下。配置过程如下所示:

 

 

此时当我们再次点击上述的三角形就可以再次启动另一个客户端了。

 

 

5.再谈协议

回顾并理解我们为什需要协议

以上我们实现的UDP和TCP数据传输,除了UDP和TCP之外,程序还存在应用层定义协议,可以想想分别都是什么样的协议格式。

对于客户端及服务端应用程序来说,请求和响应,需要约定一致的数据格式:

  • 客户端发送请求和服务端解析请求和要使用相同的数据格式。
  • 服务端返回响应和客户端解析响应也要使用相同的数据格式。
  • 请求格式和响应格式可以相同,也可以不同。
  • 约定相同的数据格式,主要目的是为了让接收端在解析的时候明确如何解析数据中的各个字段。
  • 可以使用知名协议(广泛使用的协议格式),如果想自己约定数据格式,就属于自定义协议。

结束语:

这节中小编主要是和大家分享了网络编程中的两个重要的编程UDP和TCP,后期小编还会继续出有关于网络方面的知识的,希望这节对大家了解网络有一定帮助,想要学习的同学记得关注小编和小编一起学习吧!如果文章中有任何错误也欢迎各位大佬及时为小编指点迷津(在此小编先谢过各位大佬啦!)

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

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

相关文章

坐标转换-使用geotools读取和转换地理空间表的坐标系(sqlserver、postgresql)

前言&#xff1a; 业务上通过GIS软件将空间数据导入到数据库时&#xff0c;因为不同的数据来源和软件设置&#xff0c;可能导入到数据库的空间表坐标系是各种各样的。 如果要把数据库空间表发布到geoserver并且统一坐标系&#xff0c;只是在geoserver单纯的设置坐标系只是改了…

JMeter命令行执行+生成HTML报告

1、为什么用命令行模式 使用GUI方式启动jmeter&#xff0c;运行线程较多的测试时&#xff0c;会造成内存和CPU的大量消耗&#xff0c;导致客户机卡死&#xff1b; 所以一般采用的方式是在GUI模式下调整测试脚本&#xff0c;再用命令行模式执行&#xff1b; 命令行方式支持在…

MyBatis-动态SQL-foreach

目录 标签有以下常用属性&#xff1a; 小结 <froeach> <foreach>标签有以下常用属性&#xff1a; collection&#xff1a;指定要迭代的集合或数组的参数名&#xff08;遍历的对象&#xff09;。item&#xff1a;指定在迭代过程中的每个元素的别名&#xff08;遍历…

OnlyOffice社区版破解最大连接限制部署

onlyoffice社区版部署并且破解最大连接数 docker镜像 docker pull onlyoffice/documentserver:5.3.1.265.4或更高的版本已经解决了此方法的Bug 运行镜像 docker run -d --name onlyoffice --restartalways -p 暴露端口号:80 onlyoffice/documentserver:5.3.1.26进入容器内部…

策略模式——算法的封装与切换

1、简介 1.1、概述 在软件开发中&#xff0c;常常会遇到这种情况&#xff0c;实现某一个功能有多条途径。每一条途径对应一种算法&#xff0c;此时可以使用一种设计模式来实现灵活地选择解决途径&#xff0c;也能够方便地增加新的解决途径。为了适应算法灵活性而产生的设计模…

[机器学习]线性回归模型

线性回归 线性回归&#xff1a;根据数据&#xff0c;确定两种或两种以上变量间相互依赖的定量关系 函数表达式&#xff1a; y f ( x 1 , x 2 . . . x n ) y f(x_1,x_2...x_n) yf(x1​,x2​...xn​) ​ 回归根据变量数分为一元回归[ y f ( x ) yf(x) yf(x)]和多元回归[ y …

【深度学习Week4】MobileNet_ShuffleNet

报错&#xff1a;unsafe legacy renegotiation disabled 解决方案&#xff1a; 尝试了更换cryptography36.0.2版本&#xff0c;以及更换下载链接的方法&#xff0c;都不行&#xff0c;最后采用了手动下载mat文件并上传到colab的方法 高光谱图像分类数据集简介Indian Pines&…

【果树农药喷洒机器人】Part2:机器人变量喷药系统硬件选型

本专栏介绍&#xff1a;免费专栏&#xff0c;持续更新机器人实战项目&#xff0c;欢迎各位订阅关注。 关注我&#xff0c;带你了解更多关于机器人、嵌入式、人工智能等方面的优质文章&#xff01; 文章目录 一、引言二、变量喷药系统总体要求2.1系统功能要求2.2系统技术要求 三…

4.1 Windows终端安全

数据参考&#xff1a;CISP官方 目录 安全安装保护账户安全本地安全策略安全中心系统服务安全其他安全设置软件安全获取 一、安全安装&#xff08;以安装windows系统为例&#xff09; 选择合适的版本 商业版本&#xff1a;家庭版、专业版、专业工作站版、企业版特殊版本&…

pytest-xdist分布式测试原理浅析

目录 pytest-xdist执行流程&#xff1a; pytest-xdist 模块结构&#xff1a; pytest-xdist分布式测试原理&#xff1a; pytest-xdist源码浅读&#xff1a; pytest-xdist执行流程&#xff1a; 解析命令行参数&#xff1a;pytest-xdist 会解析命令行参数&#xff0c;获取用户…

海外媒体发稿:软文写作方法方式?一篇好的软文理应合理规划?

不同种类的软文会有不同的方式&#xff0c;下面小编就来来给大家分析一下&#xff1a; 方法一、要选定文章的突破点&#xff1a; 所说突破点就是这篇文章文章软文理应以什么样的视角、什么样的见解、什么样的语言设计理念、如何文章文章的标题来写。不同种类的传播效果&#…

【JMeter】 使用Synchronizing Timer设置请求集合点,实现绝对并发

目录 布局设置说明 Number of Simulated Users to Group Timeout in milliseconds 使用时需要注意的点 集合点作用域 实际运行 资料获取方法 布局设置说明 参数说明&#xff1a; Number of Simulated Users to Group 每次释放的线程数量。如果设置为0&#xff0c;等同…

以Java的方式将文件上传到阿里云OSS

文章目录 1. 开通对象存储服务2. 创建 AccessKey 密钥3. 通用代码实现 1. 开通对象存储服务 控制台 → 对象存储 OSS → 立即开通 Bucket列表 → 点击创建 Bucket 填写名称、地域&#xff0c;名称创建后不可修改&#xff0c;地域选择最近的&#xff0c;存储类型选择标准存储&…

2023-08-08 LeetCode每日一题(任意子数组和的绝对值的最大值)

2023-08-08每日一题 一、题目编号 1749. 任意子数组和的绝对值的最大值二、题目链接 点击跳转到题目位置 三、题目描述 给你一个整数数组 nums 。一个子数组 [numsl, numsl1, …, numsr-1, numsr] 的 和的绝对值 为 abs(numsl numsl1 … numsr-1 numsr) 。 请你找出 …

STM32基础入门学习笔记:核心板 电路原理与驱动编程

文章目录&#xff1a; 一&#xff1a;LED灯操作 1.LED灯的点亮和熄灭 延迟闪烁 main.c led.c led.h BitAction枚举 2.LED呼吸灯&#xff08;灯的强弱交替变化&#xff09; main.c delay.c 3.按键控制LED灯 key.h key.c main.c 二&#xff1a;FLASH读写程序(有…

LeNet卷积神经网络-笔记

LeNet卷积神经网络-笔记 手写分析LeNet网三卷积运算和两池化加两全连接层计算分析 修正上图中H,W的计算公式为下面格式 基于paddle飞桨框架构建测试代码 #输出结果为&#xff1a; #[validation] accuracy/loss: 0.9530/0.1516 #这里准确率为95.3% #通过运行结果可以看出&am…

Stable Diffusion - Style Editor 和 Easy Prompt Selector 提示词插件配置

欢迎关注我的CSDN&#xff1a;https://spike.blog.csdn.net/ 本文地址&#xff1a;https://spike.blog.csdn.net/article/details/132122450 Stable Diffusion 的 Prompt 的功能&#xff0c;可以用文字来描述想要生成的图像&#xff0c;根据输入来创造出逼真的图像。Prompt 支持…

Python 面试必知必会(一):数据结构

《Python Cookbook》的作者David Beazley的课程PPT开源了&#xff0c;目标用户是希望从编写基础脚本过渡到编写更复杂程序的高级 Python 程序员&#xff0c;课程主题侧重于流行库和框架中使用的编程技术&#xff0c;主要目的是更好地理解 Python 语言本身&#xff0c;以便阅读他…

【深度学习】采用自动编码器生成新图像

一、说明 你知道什么会很酷吗&#xff1f;如果我们不需要所有这些标记的数据来训练 我们的模型。我的意思是标记和分类数据需要太多的工作。 不幸的是&#xff0c;大多数现有模型从支持向量机到卷积神经网&#xff0c;没有它们&#xff0c;卷积神经网络就无法训练。无监督学习不…

中断子系统--硬件层(GICv3)

目录 综述 硬件层--GICV3 中断类型 中断状态 Distributor组件 中断使能配置 中断触发方式配置 中断优先级配置  中断分组标记 GIC处理中断流程 综述 由上面的block图&#xff0c;我们可知linux kernel的中断子系统分成4个部分&#xff1a; 硬件层&#xff1a;最下层…