网络编程(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写一个简单的客户端服务器程序。 也就是让客户端发一个请求,在服务器上返回一个一模一样的响应。
首先来明确一点,一个服务器主要做的三个核心的工作:
- 读取请求并解析。
- 根据请求并计算响应。(代码中省略了)
- 把响应返回给客户端。
服务端代码:
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,后期小编还会继续出有关于网络方面的知识的,希望这节对大家了解网络有一定帮助,想要学习的同学记得关注小编和小编一起学习吧!如果文章中有任何错误也欢迎各位大佬及时为小编指点迷津(在此小编先谢过各位大佬啦!)
相关文章:

网络编程(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.2Datagr…...
Git常用的指令
Git常用的指令 OMMP提交代码的流程 0、配置: git config --list 查看当前配置 git congig --global user.name user 这个会显示你的提交到git的名字 格式:git config [–local|–global|–system] –unset section.key 格式:git config [–l…...

LoadRunner(2)
一、Controller 1.1场景设计 1.通过VUG打开 施压机器:发起请求的角色(用户本地电脑) 被压机器:处理请求的角色(服务器) 2.直接双击Controller 场景设计:需要关注三个部分 第一部分: 第二部分: 2.1运行场景…...

CTF之逆向之阿里巴巴
题目地址:http://www.shiyanbar.com/ctf/13 题目预览: 解题过程: 1、下载附件发现是exe文件 2、使用PEid和Detect It Easy查壳 和 开发语言,发现没有加壳,都是用C#开发的 3、C#和Java Python属于解释型语言ÿ…...

Labview控制APx(Audio Precision)进行测试测量(五)
驱动程序 VIs如何处理配置设置中的单元 APx500 应用程序具有复杂的控件,具有以下功能: 数值和单位组合在一个控制中(例如,1.000 Vrms ) •值转换为 SI 格式(例如,1.000 mVrms 或 1.000 μVrms) •单位之间的转换发生在控制(例如,V…...

在单元测试中使用Jest模拟VS Code extension API
对VS Code extension进行单元测试时通常会遇到一个问题,代码中所使用的VS Code编辑器的功能都依赖于vscode库,但是我们在单元测试中并没有添加对vscode库的依赖,所以导致运行单元测试时出错。由于vscode库是作为第三方依赖被引入到我们的VS C…...

django boostrap html实现可拖拽的左右布局,鼠标拖动调整左右布局的大小或占比
一、实现的效果 最近需要在Django项目中,实现一个左右布局的html页面,页面框架使用的是boostrap。但这个布局不是简单的左右分栏布局,而是需要实现可以通过鼠标拖拽的方式动态调整左右两侧布局的大小和占比。效果大致如下: 一开始,页面分为左右两块布局: 鼠标放到中间的…...

谈谈闭包和闭包使用场景
一、什么是闭包 概念:闭包还是作用域的一种特殊应用 二、触发闭包的情况 1.函数当做返回值被返回 2.函数当做参数被传递 3.自执行匿名函数 //情况1:函数当做返回值被返回 function fn(){const a 1;return function(){console.log(a) //1}; } const a …...
MATLAB算法实战应用案例精讲-【图像处理】边界框锚框
目录 目标检测 应用场景 目标检测发展历程 常用数据集 边界框(bounding box)...
04什么场景要用到微服务
一句话导读 根据微服务的特点,可以总结为在构建复杂的、大型的、分布式的、高可用、高并发、高性能的应用时可以使用微服务架构。 目录 一句话导读 一、微服务适用场景 1.业务复杂,模块多且相对独立 2.团队多,管理隔离 3.应用规模大&#…...

.NET SqlSuger 简单介绍,超快开发数据库
文章目录 前言SqlSugar使用我的环境Nuget 安装新建连接串DB First 和 Code First使用增删改查 总结 前言 我之前介绍过EFCore 怎么使用Nuget快速创建数据库,我之后发现SqlSugar更快。这里简单再说一下SqlSugar如何使用 .NET Core 数据库DB First自动生成࿰…...
SpringBoot复习:(28)【前后端不分离】自定义View
一、自定义View package cn.edu.tju.view;import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.ComponentScan; import org.springframework.context.annotation.Configuration; import org.springframework.stereotype.Comp…...

springcloud3 springcloud stream的学习以及案例(了解)
一 springcloud stream的作用 1.1 springcloud stream作用 stream屏蔽底层消息中间件的差异,降低切换成本,统一消息的编程模型。 stream中的消息通信模式遵循了“发布-订阅”模式。 1.2 Binder作用 通过定义绑定器Binder作为中间层,实现…...
Kotlin理解内置函数
目录 一 内置函数1.1 apply 函数1.2 let 函数1.3 run函数1.4 with函数1.5 also函数1.6 takeIf函数1.7 takeUnless函数1.8 总结 Kotlin内置函数包括:let、run、with、apply、also,这些函数都是在Any类中定义的扩展函数,所以任何对象都可以调用…...

手机app测试
一、安装、卸载、更新、运行 1.安装、卸载 应用是否可以正常安装(命令行安装;apk/ipa安装包安装)(有网,无网是否都正常)卸载过程中出现死机,断电,重启等意外的情况&…...
Centos部署Git
Centos部署Git 文章目录 Centos部署Git部署步骤初始化配置免登录 部署步骤 初始化 -- 安装git yum install git配置免登录 配置git下载代码时 每次都需要输入密码的事情 -- 生成 gitconfig 文件 git config --global credential.helper store -- 配置登录邮箱 git config …...
k8s 控制器
Kubernetes(K8S)是一种开源的容器编排平台,它可以自动化地管理容器化应用程序的部署、扩展和运行。K8S中的控制器是一种重要的组件,它可以确保应用程序的状态与期望的状态一致。在K8S中,有五种常见的控制器,…...

谷歌关闭跨域限制.(生成一个开发浏览器),Chrome关闭跨域
(一)、首先找到浏览器在电脑磁盘中的位置,并复制 (二)、复制一个浏览器的快捷方式到桌面(不影响正常浏览器) (三)、chrom鼠标右键属性,修改快捷方式的目标 (四)chrome.exe 后面添加 --disable-web-security --user-data-dir 复制的Chrome浏览…...

实践指南-前端性能提升 270% | 京东云技术团队
一、背景 当我们疲于开发一个接一个的需求时,很容易忘记去关注网站的性能,到了某一个节点,猛地发现,随着越来越多代码的堆积,网站变得越来越慢。 本文就是从这样的一个背景出发,着手优化网站的前端性能&a…...

8月11日上课内容 nginx的多实例和动静分离
多实例部署 在一台服务器上有多个tomcat的服务。 配置多实例之前,看单个实例是否访问正常。 1.安装好 jdk 2.安装 tomcat cd /opt tar zxvf apache-tomcat-9.0.16.tar.gz mkdir /usr/local/tomcat mv apache-tomcat-9.0.16 /usr/local/tomcat/tomcat1 cp -a /usr…...

【java面试】微服务篇
【java面试】微服务篇 一、总体框架二、Springcloud(一)Springcloud五大组件(二)服务注册和发现1、Eureka2、Nacos (三)负载均衡1、Ribbon负载均衡流程2、Ribbon负载均衡策略3、自定义负载均衡策略4、总结 …...
AWS vs 阿里云:功能、服务与性能对比指南
在云计算领域,Amazon Web Services (AWS) 和阿里云 (Alibaba Cloud) 是全球领先的提供商,各自在功能范围、服务生态系统、性能表现和适用场景上具有独特优势。基于提供的引用[1]-[5],我将从功能、服务和性能三个方面进行结构化对比分析&#…...

【工具教程】多个条形码识别用条码内容对图片重命名,批量PDF条形码识别后用条码内容批量改名,使用教程及注意事项
一、条形码识别改名使用教程 打开软件并选择处理模式:打开软件后,根据要处理的文件类型,选择 “图片识别模式” 或 “PDF 识别模式”。如果是处理包含条形码的 PDF 文件,就选择 “PDF 识别模式”;若是处理图片文件&…...
CentOS 7.9安装Nginx1.24.0时报 checking for LuaJIT 2.x ... not found
Nginx1.24编译时,报LuaJIT2.x错误, configuring additional modules adding module in /www/server/nginx/src/ngx_devel_kit ngx_devel_kit was configured adding module in /www/server/nginx/src/lua_nginx_module checking for LuaJIT 2.x ... not…...

虚拟机网络不通的问题(这里以win10的问题为主,模式NAT)
当我们网关配置好了,DNS也配置好了,最后在虚拟机里还是无法访问百度的网址。 第一种情况: 我们先考虑一下,网关的IP是否和虚拟机编辑器里的IP一样不,如果不一样需要更改一下,因为我们访问百度需要从物理机…...
Caliper 配置文件解析:config.yaml 和 fisco-bcos.json 附加在caliper中执行不同的合约方法
Caliper 配置文件解析:config.yaml 和 fisco-bcos.json Caliper 是一个区块链性能基准测试工具,用于评估不同区块链平台的性能。下面我将详细解释你提供的 fisco-bcos.json 文件结构,并说明它与 config.yaml 文件的关系。 fisco-bcos.json 文件解析 这个文件是针对 FISCO…...
git删除本地分支和远程分支
删除本地分支 git branch -d 分支名删除远程分支 git push origin --delete 分支名...

Gerrit+repo管理git仓库,如果本地有新分支不能执行repo sync来同步远程所有修改,会报错
问题:创建一个本地分支TEST 来关联远程已有分支origin/TEST,直接执行repo sync可能会出现问题:比如,本地分支TES会错乱关联到origin/master,或者拉不下最新代码等问题。 // git checkout -b 新分支名 远程分支名字 git…...

豆瓣图书评论数据分析与可视化
【题目描述】豆瓣图书评论数据爬取。以《平凡的世界》、《都挺好》等为分析对象,编写程序爬取豆瓣读书上针对该图书的短评信息,要求: (1)对前3页短评信息进行跨页连续爬取; (2)爬取…...

银行卡二三四要素实名接口如何用PHP实现调用?
一、什么是银行卡二三四要素实名接口 输入银行卡卡号、姓名、身份证号码、手机号,验证此二三四要素是否一致。 二、核心价值 1. 提升风控效率 通过实时拦截冒用身份开户,银行卡二三四要素实名接口显著降低了人工审核成本,效率提升50%以上…...