轻松学会网络编程

目录

一、UDP 和 TCP 特点对比

1、有连接和无连接

2、可靠传输和不可靠传输

3、面向字节流和面向数据报

4、全双工和半双工

二、UDP 的 socket.api

1、DatagramSocket

2、DatagramPacket

回显服务器的实现

(1)服务器代码

(2)客户端代码

翻译服务器的实现

三、TCP

1、ServerSocket   

​编辑 2、Socket

回显服务器

(1)服务器代码

(2)客户端代码


网络编程其实就是写一个应用程序,让这个程序可以使用网络通信

这里就需要调用传输层提供的 api

传输层提供的协议一共是两个:

1、UDP 

2、TCP

这两个协议提供了两套不同的 api


一、UDP 和 TCP 特点对比

UDP:无连接,不可靠传输,面向数据报,全双工

TCP:有连接,可靠传输,面向字节流,全双工


1、有连接和无连接

我们在之前学习 JDBC 的时候,有这么一个步骤:

先创建一个 DtaSourse ,再通过 DataSourse 创建 Connection

而这里的 Connection ,就是我们所说的连接

更直观的理解:

打电话的时候,拨号,按下拨号键,知道对方接通,才算完成连接建立

TCP 进行编程的时候,也是存在类似的建立连接的过程

无连接就类似于 发微信 / 短信,不需要建立连接就能进行通信

这里的 ”连接“ 是一个抽象的概念

客户端和服务器之间,使用内存保存对端的信息

双方都保存这个信息,此时 “连接” 就出现了

一个客户端可以连接多个服务器,一个服务器也可以对应多个客户端的连接


2、可靠传输和不可靠传输

可靠传输,并不是说 A 给 B 发的消息100% 能传到

而是,A 尽可能地把消息传给 B,并且在传输失败的时候,A 能感知到,或者在传输成功的时候,也能知道自己传输成功了

TCP 是可靠传输,传输效率就低了

UDP 是不可靠传输,传输效率更高

虽然 TCP 是可靠传输,UDP 是不可靠传输,但是并不能说 TCP 就是比 UDP 安全

“网络安全” 指的是,如果你传输的数据是否容易被黑客截获,以及如果被截获后是否会泄露一些重要信息


3、面向字节流和面向数据报

TCP 和文件操作类似,都是 “流” 式的(由于这里传输的单位都是字节 ,称为字节流)

UDP 是面向数据报,读写的基本单位是一个 UDP 数据报(包含了一系列的数据 / 属性)


4、全双工和半双工

全双工 :一个通道,可以双向通信

半双工: 一个通道,只能单向通信

网线,就是全双工的

网线一共有 8 根铜缆,4 4 一组,有的负责一个方向,有的负责另一个方向


二、UDP 的 socket.api

1、DatagramSocket

是一个 Socket 对象,

操作系统使用文件这样的概念,来管理一些软硬件资源,操作系统也是使用文件的方式来管理网卡的,表示网卡的这类的文件,称为 Socket 文件

Java 中的 Socket 对象,就对应系统里的 Socket 文件(最终还是要落到网卡上)

要进行网络通信,就必须得有 Socket 对象

构造方法:

第一个往往在客户端使用(客户端使用哪个端口是系统自动分配的)

第二个往往在服务器这边使用(服务器使用哪个端口是手动指定的)

一个客户端的主机,上面运行的结果很多,天知道你手动选定的端口是不是被别的程序占用了,所以让系统自动分配一个端口是更加明智的选择

服务器是完全在程序员手里控制的,程序员可以把服务器上的多个程序安排好,让他们使用不同的端口

其它方法:


2、DatagramPacket

表示了一个 UDP 数据报,代表了系统中设定的 UDP 数据报的二进制结构

构造方法:

第一个构造方法:用来接受数据

DatagramPacket 作为一个 UDP 数据报,必然要能够承载一些数据

通过手动指定的 byte[] 作为存储数据的空间

第二个构造方法:用来发送数据

SocketAddress address 指的是 ip 和 端口号

其它方法:

getData 是指获取 UDP 数据报载荷部分(完整的应用层数据报)


回显服务器的实现

接下来我们开始手写 UDP 客户端服务器

最简单的 UDP 服务器:回显服务器(echo server),客户端发啥,服务器返回啥

(1)服务器代码

编写网络程序的时候,经常见到这种异常,意思就是说 socket 是有可能失败的

最典型的情况,就是端口号被占用

端口号是用来区分主机上的应用程序,一个应用程序可以占用主机上的多个端口,一个端口只能被一个进程占用(有特例,此处不讨论) 

换句话说,当端口已经被其它进程占用了,此时你再尝试创建这个 socket 对象,占用此端口,就会报错

一个服务器,要给很多客户端提供服务,服务器也不知道客户端什么时候来,服务器只能 “时刻准备着” ,随时客户端来了,随时提供服务

一个服务器,运行过程中,要做的事情,主要是三个核心环节

1、读取请求,并解析
2、根据请求,计算出响应
3、把响应写回给客户端

对于回显服务器来说,则不关心第二个流程,请求是啥,就返回什么响应

但是一个商业级的服务器,主要的代码都是在完成第二个步骤

这个方法中,参数的 DatagramPacket 是一个 “输出型参数”

传入 receive 的是一个空的对象,receive 内部就会把这个空的对象的内容给填成上,当 receive 执行结束,于是就得到了一个装满内容的 DatagramPacket

这个对象用来保存数据的内存空间,是需要手动指定的,不像学过的集合类,内部是有自己管理内存的能力的(能够申请内存,释放内存,内存扩容 等功能)

4096 这个数字是随便写出来的,但是也不能写的太大,不能超过 64kb

服务器程序一启动,就会立即执行到循环,立即执行到 receive 了

如果此时客户端的请求还没来,receive 方法就会阻塞等待,阻塞到真正有客户端发起请求过来了

这段代码中,就要构造一个 DatagramPacket 对象,把响应发回给客户端

注意:第二个参数不能写 response,length()

这是因为, response,length() 是按照字符计算长度,而 response.getBytes().length 是按照字节计算长度

如果字符串是全英文的,此时字节数和字符数是一样的,但是如果字符串中含有中文,此时两者计算出的结果就不一样了

socket api 本身,就是按照字节来处理的

requestPacket.getSocketAddress()  这个部分是把数据报,发送给客户端,就需要知道客户端的 ip 和端口

DatagramPacket 这个对象就包含着通信双方的 ip 和 port 

服务器部分代码:

import java.io.IOException;
import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.net.SocketException;//UDP 的回显服务器
//客户端发的请求是啥,服务器的响应就是啥
public class UdpEchoServer {private DatagramSocket socket = null;public UdpEchoServer(int port) throws SocketException {socket = new DatagramSocket(port);}//使用这个方法启动服务器public void start() throws IOException {System.out.println("服务器启动");while (true){//反复的,长期的执行针对客户端请求处理的逻辑//一个服务器,运行过程中,要做的事情,主要是三个核心环节//1、读取请求,并解析DatagramPacket requestPacket = new DatagramPacket(new byte[4096],4096);socket.receive(requestPacket);//这样的转字符串的前提是,后续客户端发的数据就是一个文本的字符串String request = new String(requestPacket.getData(),0,requestPacket.getLength());//2、根据请求,计算出响应String response = process(request);//3、把响应写回给客户端//此时需要告知网卡,要发的内容是啥,要发给谁DatagramPacket responsePacket = new DatagramPacket(response.getBytes(),response.getBytes().length,requestPacket.getSocketAddress());socket.send(responsePacket);//记录日志,方便观察程序执行效果System.out.printf("[%s:%d] req: %s, resp: %s\n",responsePacket.getAddress().toString(),responsePacket.getPort(),request,response);}}//根据请求计算响应public String process(String request){return request;}public static void main(String[] args) throws IOException {UdpEchoServer server = new UdpEchoServer(9090);server.start();}
}

这个是否关闭了呢?

对于我们这个服务器程序来说,DatagramSocket 不关闭,问题不大,整个程序中只有这一个 socket 对象,不是频繁创建的

这个对象的生命周期非常长,跟随整个程序的,此时,socket 就需要保持打开的状态

socket 对象,对应到了 系统中的 socket 文件,又对于到了文件描述符(最主要的目的是为了是否文件描述符,才要关闭 socket 对象的

进程结束,就把 pcb 回收,里面的文件描述符表也就都销毁了

但是这一点仅限于:只有一个 socket 对象,并且生命周期是跟随进程的,此时就可以不用释放

但是如果有多个 socket 对象,socket 对象生命周期更短,需要频繁创建释放,此时一定要记得去 close


(2)客户端代码

编写客户端,后面的参数指定了发给哪个 IP,发给哪个端口

此处需要的是 InetAdress 对象,所以使用 InetAdress 的静态方法,getByName 来进行构造(工厂模式 / 工厂方法)

 

服务器启动要自动绑定到 9090

客户端接下来就要访问 9090 这个窗口

 客户端代码:

import java.io.IOException;
import java.net.*;
import java.util.Scanner;public class UdpEchoClient {private DatagramSocket socket = null;private String serverIp;private int serverPort;//服务器的 ip 和 服务器窗口public UdpEchoClient(String ip ,int port) throws SocketException {serverIp = ip;serverPort = port;//这个 new 操作,就不再指定端口了,让系统自动分配一个空闲端口socket = new DatagramSocket();}//客户端启动,让这个客户端反复的从控制台读取用户输入的内容,把这个内容构造成 UPD 请求,发给服务器,再读取服务器返回的 UDP响应//最终再显示在客户端的屏幕上public void start() throws IOException {System.out.println("客户端启动!");Scanner scanner = new Scanner(System.in);while (true){//1、从控制台读取用户输入的内容System.out.println("-> ");//命令提示符,提示用户输入字符串String request = scanner.next();//2、构造请求对象,并发给服务器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);String response = new String(responsePacket.getData(),0,responsePacket.getLength());//4、显示结果System.out.println(request);}}public static void main(String[] args) throws IOException {UdpEchoClient client = new UdpEchoClient("127.0.1",9090);client.start();}
}

此时,先运行服务器,再运行客户端,然后输入内容,就可以看到执行结果了

 如果启动多个客户端,多个客户端也是可以被服务器应对的


翻译服务器的实现

翻译服务器,请求的是一些英文单词,响应则是对应得中文翻译

这个服务器和之前的回显服务器的到部分代码是相似的,所以我们让它直接继承之前的服务器

继承,本身就是为了更好的“复用现有代码”

如果在这里,不加@Override ,万一方法名字 / 参数类型 / 参数个数 / 访问权限 万一搞错了,此时就无法构成重写了,并且也不好发现

import java.io.IOException;
import java.net.SocketException;
import java.util.HashMap;
import java.util.Map;public class UdpDicSever extends UdpEchoServer{private Map<String,String> dict = new HashMap<>();public UdpDicSever(int port) throws SocketException {super(port);dict.put("cat","小猫");dict.put("dog","小狗");dict.put("duck","小鸭");//可以在这里继续添加千千万万个单词,每个单词都有一个对应的翻译}//是要复用之前的代码,但是又要做出调整@Overridepublic String process(String request){//把请求对应单词的翻译给返回回去return dict.getOrDefault(request,"该词没有查询到");}public static void main(String[] args) throws IOException {UdpDicSever server = new UdpDicSever(9090);//start 就不需要在写一遍了,就直接复用了之前的 startserver.start();}
}

三、TCP

TCP 是字节流,一个字节一个字节的进行传输的

换句话说,一个 TCP 数据报,就是一个字节数组 byte[ ] 

TCP 提供的 api 也是两个类

1、ServerSocket   

给服务器使用的 Socket

构造方法:

其它方法:

 2、Socket

既会给服务器使用,也会给客户端使用

构造方法:

其它方法:


回显服务器

(1)服务器代码

我们现在来尝试写一个 TCP 版本的回显服务器

这里会和  UDP 有一些差别:

进入循环之后,要做的事情不是读取客户端的请求,而是先处理客户端的 “连接”

虽然内核中的连接很多,但是在应用程序中,还是得一个一个处理的

内核中的连接就像一个一个待办事项,这些待办事项在一个队列 数据结构中,应用程序就需要一个一个完成这些任务

要完成任务,就需要先取任务,用到 serverSocker.accept()

把内核中的连接获取到应用程序中了,这个过程类似于 “生产者消费者模型”

程序启动,就会立即执行到 accept

当服务器执行到 accept 的时候,此时客户端可能还没来,accpet 就会阻塞

一直阻塞到有客户端连接成功

accept 是把内核中已经建立好的连接,给拿到应用程序中

但是这里的返回值并非是一个 “Connection” 这样的对象,而只是一个 Socket 对象

而这个 Socket 对象就像是一个 耳麦 一样,就可以说话,也能听到对方的声音

通过 Socket 对象,和对方进行网络通信

TCP server 中涉及到两种 socket,serverSocket 和 clientSocket

serverSocket 可以理解成售楼部负责拉客的人,clientSocket 可以理解成售楼部负责介绍详情的人 

IO 操作是比较有开销的

相比于访问内存,进行 IO 的次数越多,程序的速度就越慢

使用一块内存作为缓冲区,写数据的时候,先写到缓冲区里,攒一波数据,统一进行 IO  

PrintWriter 就内置了缓冲区,手动刷新,确保这里的数据是真的通过网卡发出去了,而不是残留在缓冲区中的

这里加上 flash 更稳妥,但是不加也不一定会出错

缓冲区是内置了一定的刷新策略,因此更建议把 flash 给加上

在这个程序中,涉及到两类 socket 

1、ServerSocket (只有一个,生命周期跟随程序,不关闭也没事)

2、Socket

此处的 socket 是在被反复创建的

我们要确保,连接断开之后,socket 能够被关闭,所以在最后 finally 中加上关闭 socket 的代码

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;public class TcpEchoServer {private ServerSocket serverSocket = null;//这个操作就会绑定端口号public TcpEchoServer(int port) throws IOException {serverSocket = new ServerSocket(port);}//启动服务器public void start() throws IOException {System.out.println("启动服务器!");while (true){Socket clientSocket = serverSocket.accept();processConnection(clientSocket);}}//通过这个方法处理一个连接的逻辑private void processConnection(Socket clientSocket) throws IOException {System.out.printf("[%s,%d] 客户端上线!\n,", clientSocket.getInetAddress().toString(),clientSocket.getPort());//接下来就可以读取请求,根据请求计算响应,返回响应//Socket 对象内部,包含两个字节流对象,可以把这两个字节流对象获取到,完成后续的读写工作try(InputStream inputStream = clientSocket.getInputStream();OutputStream outputStream = clientSocket.getOutputStream()){//一次连接中,可能会涉及到多次请求 / 响应while (true){//1、读取请求,并解析,为了读取方便,直接使用ScannerScanner scanner = new Scanner(inputStream);if (!scanner.hasNext()){//读取完毕,客户端下线System.out.printf("[%s:%d] 客户端下线! \n",clientSocket.getInetAddress().toString(),clientSocket.getPort());break;}//这个代码暗含一个约定:客户端发过来的请求,得是文本数据,同时,还得带有空白符作为分割String request = scanner.next();//2、根据请求,计算响应String response = process(request);//3、把响应写回给客户端,把 OutputStream 使用 PrinterWriter 包裹一下,方便进行发数据PrintWriter writer = new PrintWriter(outputStream);//使用 PrintWriter 的 println 方法,把响应返回给客户端//此处使用 println 而不是 print 就是为了在结尾加上换行符,方便客户端读取响应,使用 Scanner.next 读取writer.println(response);//这里还需要加一个 “刷新缓冲区” 的操作writer.flush();//日志,打印一下当前的请求详情System.out.printf("[%s:%d] req: %s,resp: %s\n",clientSocket.getInetAddress().toString(),clientSocket.getPort(),request,response);}}finally {//在 finally 中,加上 close 操作,确保当前 socket 能够被及时关闭clientSocket.close();}}public String process(String request){return  request;}public static void main(String[] args) throws IOException {TcpEchoServer server = new TcpEchoServer(9090);server.start();}
}

(2)客户端代码

客户端要做的事情:

1、从控制台读取用户的输入

2、把输入的内容构造成请求并发送给服务器

3、从服务器读取响应

4、把响应显示到控制台上

当前代码中,对于 Scanner 和 PrintWriter 没有进行 close,是否会发生文件泄露呢?

不会!!!

流对象中持有的资源,有两个部分:

1、内存(对象销毁了,内存就回收了),while 循环一圈,内存自然销毁

2、文件描述符

Scanner 和 printWriter 没有持有文件描述符,持有的是 inputstream 和 outputstream 的引用,而这俩个对象已经进行了关闭

更准确的说,是 socket 对象持有的,所以把 socket 对象关闭就可以了

不是每个流对象都持有文件描述符,持有文件描述符,是要调用操作系统提供的 open方法

hasNext 在客户端没有发请求的时候,也会阻塞,一直阻塞到客户端真的发了数据,或者客户端退出,hasNext 就返回了

当前代码,还存在一个很大的问题:当我们启动两个客户端,会怎么样呢?

现象:

当第一个客户端连接好了之后,第二个客户端不能被正确处理

服务器看不到客户端上线,同时客户端发来的请求也无法被处理

当第一个客户端退出之后,之前第二个客户端发的请求,就都能响应了

当一个客户端来了,accept 就会返回,进入 processConnection

循环会处理该客户端的请求,一直到这个客户端结束了,方法才结束,才回到第一层这里

问题关键在于,处理一个客户端的请求过程中,无法第二次调用 accept ,也就是说即使第二个客户端来了,也没法处理

此处,客户端处理 processConnection 本身就是要长时间执行的,因为不知道客户端什么时候结束,也不知道客户端要发多少请求

那么我们期望在执行这个方法的同时,也能够调用到 accept,此时可以使用多线程

我们可以在主线程里,专门负责拉客,拉到客人之后,创建新的线程,让新的线程负责处理客户端的各种请求

经过上述改进,只要服务器资源足够,有几个客户端都是可以的

其实,如果我们刚才的代码,不写成这个样子,比如要求每个客户端只能发一次请求,发完就断开,上述情况就能得到一定的缓解,但是还是会有类似的问题的

处理多个消息,自然就会延长 proessConnection 的执行时间,就让这个问题更加严重了

TCP 程序的时候,涉及到两种写法:

1、一个连接中只传输一次请求和响应(短连接)

2、一个请求可以传输多次请求和响应(长连接) 

现在我们是,有一个连接,就有一个新的线程

如果有很多客户端,频繁的来 连接 / 断开,服务器就涉及到频繁 创建 / 释放 线程了

使用线程池是更好的方案

这样写是不对的!!!

processConnection 和主线程是不同的线程了,执行 processConnecton 过程中,主线程 try 就执行完毕了,这就会导致 clientSocket 还没有用完,就关闭了 

因此,还是需要把 clientSocket 交给 processConnection 里面来关闭,所以应该这样写:

虽然使用线程池,避免了频繁创建销毁线程,但是毕竟是每个客户端对应一个线程

如果服务器对应的客户端很多,服务器就需要创建出大量的线程,,对于服务器的开销是很大的

当客户端进一步增加,线程数目进一步增加,系统的负担就越来越重,响应的速度也会大打折扣

是否有办法,使用一个线程(或者最多三四个线程),能够高效处理很多个客户端的并发请求

这个也叫做 C10M 问题:同一时刻,有 1kw 的客户端并发请求

引入了很多技术手段,其中一个非常有效 / 必要的手段:IO多路复用 / IO 多路转接

这个东西是解决高并发(C10M)的重要手段之一

解决高并发,说白了就是四个字:

1、开源:引入更多的硬件资源

2、节流:提高单位硬件资源能够处理的请求

IO 多路复用,就是节流的方式,同样的请求,消耗的硬件资源更少了

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

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

相关文章

如何应用项目管理软件进行敏捷开发管理

敏捷开发&#xff08;Agile Development&#xff09;是一种软件开发方法论&#xff0c;强调在不断变化的需求和环境下&#xff0c;通过迭代、协作和自适应的方式来开发软件。敏捷方法的目标是提供更快、更灵活、更高质量的软件交付&#xff0c;以满足客户需求并实现项目成功。 …

LeetCode150道面试经典题-- 汇总区间(简单)

1.题目 给定一个 无重复元素 的 有序 整数数组 nums 。 返回 恰好覆盖数组中所有数字 的 最小有序 区间范围列表 。也就是说&#xff0c;nums 的每个元素都恰好被某个区间范围所覆盖&#xff0c;并且不存在属于某个范围但不属于 nums 的数字 x 。 列表中的每个区间范围 [a,…

力扣 198. 打家劫舍

题目来源&#xff1a;https://leetcode.cn/problems/house-robber/description/ C题解&#xff1a;因为是间接偷窃&#xff0c;所以偷nums[i]家前&#xff0c;一定偷过第i-2或者i-3家&#xff0c;因为i-1不能偷。 例如12345共5家&#xff0c;先偷第1家&#xff0c;那么2不能偷…

数据结构-栈的实现(C语言版)

前言 栈是一种特殊的线性表&#xff0c;只允许在固定的一端进行插入和删除的操作&#xff0c;进行数据插入和删除的一端叫做栈顶&#xff0c;另一端叫做栈底。 栈中的数据元素遵循后进先出的的原则。 目录 1.压栈和出栈 2. 栈的实现 3.测试代码 1.压栈和出栈 压栈&#xff…

vue 使用indexDB 简单完整逻辑

1 npm npm install idb 2 代码 <template><div><p>Data: {{ data }}</p><button click"fetchData">Fetch Data</button></div> </template><script> import { openDB } from idb;export default {data() {…

【C++学习手札】一文带你认识C++虚函数(内层剖析)

食用指南&#xff1a;本文在有C基础的情况下食用更佳 &#x1f340;本文前置知识&#xff1a; C初识继承 ♈️今日夜电波&#xff1a;No title —REOL 1:02 ━━━━━━️&#x1f49f;──────── 4:03 …

PHP手术麻醉系统源码,自动生成麻醉和护理医疗文书

一套手术麻醉系统源码&#xff0c;可二次开发 手术室麻醉临床信息系统&#xff08;AIMS&#xff09;是应用于医院手术室、麻醉科室的计算机软件系统。该系统针对整个围术期&#xff0c;对病人进行全程跟踪与信息管理&#xff0c;自动集成病人HIS、LIS、RIS、PACS信息&#xff0…

ts与vue

ts与Vue 如果你已经学习了typeScript,但不知道如何在vue项目中使用&#xff0c;那么这篇文章将会很适合你。参考千峰教育 kerwin视频 1.会自动推导&#xff0c;隐士推导。提示 类型系统。 独立模块。 isolatedModules选项&#xff1a;是否配置为独立的模块。 减少报错 let …

数字化时代,数据仓库和商业智能BI系统演进的五个阶段

数字化在逐渐成熟的同时&#xff0c;社会上也对数字化的性质有了进一步认识。当下&#xff0c;数字化除了前边提到的将复杂的信息、知识转化为可以度量的数字、数据&#xff0c;在将其转化为二进制代码&#xff0c;引入计算机内部&#xff0c;建立数据模型&#xff0c;统一进行…

FPGA芯片IO口上下拉电阻的使用

FPGA芯片IO口上下拉电阻的使用 为什么要设置上下拉电阻一、如何设置下拉电阻二、如何设置上拉电阻为什么要设置上下拉电阻 这里以高云FPGA的GW1N-UV2QN48C6/I5来举例,这个芯片的上电默认初始化阶段,引脚是弱上来模式,且模式固定不能通过软件的配置来改变。如下图所示: 上…

华为OD机试 - 查找接口成功率最优时间段 - 回溯(Java 2023 B卷 100分)

目录 专栏导读一、题目描述二、输入描述三、输出描述四、解题思路五、Java算法源码六、效果展示1、输入2、输出3、说明 华为OD机试 2023B卷题库疯狂收录中&#xff0c;刷题点这里 专栏导读 本专栏收录于《华为OD机试&#xff08;JAVA&#xff09;真题&#xff08;A卷B卷&#…

Viobot开机指南

0.前言 本篇旨在让每个拿到Viobot设备的用户都能够第一时间测试它的效果&#xff0c;以及将设备配置到自己的环境下面。 1.上电 首先&#xff0c;我们先要把设备接上电源线和网线&#xff0c;最简单的方式就是网线直连电脑。 电源选用12V1.5A设备自带的电源即可。 2.配置网…

Android 网络编程-网络请求

Android 网络编程-网络请求 文章目录 Android 网络编程-网络请求一、主要内容二、开发网络请求前的基本准备1、查看需要请求的网址是否有效&#xff08;1&#xff09;通过网页在线验证&#xff08;2&#xff09;使用专用window网咯请求工具&#xff08;3&#xff09;编写app代码…

《游戏编程模式》学习笔记(四) 观察者模式 Observer Pattern

定义 观察者模式定义了对象间的一种一对多的依赖关系&#xff0c;当一个对象的状态发生改变时&#xff0c;所有依赖于它的对象都得到通知并被自动更新。 这是定义&#xff0c;看不懂就看不懂吧&#xff0c;我接下来举个例子慢慢说 为什么我们需要观察者模式 我们看一个很简…

ubuntu部署haproxy

HAProxy是可提供高可用性、负载均衡以及基于TCP和HTTP应用的代理. 1、更新系统报 通过在终端中运行以下命令&#xff0c;确保所有系统包都是最新的 sudo apt updatesudo apt upgrade2、安装Haproxy sudo apt install haproxy设置开机自动启动haproxy服务 sudo systemctl en…

微服务-Nacos(注册中心)

Nacos是SpringCloud的一个功能非常强大的组件&#xff0c;想比eureka的功能更加丰富 官方的nacos简介 Nacos&#xff08;全称&#xff1a;Naming and Configuration Service&#xff09;是一个开源的动态服务发现、配置管理和服务管理平台。它由阿里巴巴集团开发并贡献给开源…

SpringBoot复习:(51)默认情况下DataSource是怎么创建出来的,是什么类型的?

DataSource是通过DataSourceAutoConfiguration创建的&#xff0c;这个类代码如下&#xff1a; 可以看到DataSourceAutoConfiguration有个静态内部类PooledDataSourceConfiguration,在这个类上有个Import注解&#xff0c;导入了DataSourceConfiguration.Hikari这个类&#xff0…

【学习日记】【FreeRTOS】任务调度时如何考虑任务优先级——任务的自动切换

写在前面 本文开始为 RTOS 加入考虑任务优先级的自动调度算法&#xff0c;代码大部分参考野火。 本文主要是一篇学习笔记&#xff0c;加入了笔者自己对野火代码的梳理和理解。 一、基本思路 首先我们要知道&#xff0c;在 RTOS 中&#xff0c;优先级越高、越需要被先执行的的…

Jmeter对websocket进行测试

JMeterWebSocketSampler-1.0.2-SNAPSHOT.jar下载 公司使用websocket比较奇怪&#xff0c;需要带认证信息进行长连接&#xff0c;通过websocket插件是请求失败&#xff0c;如下图&#xff0c;后面通过代码实现随再打包jar包完成websocket测试 本地实现代码如下&#xff1a; pa…

Maven基础之仓库、命令、插件机制

文章目录 Maven 仓库中央仓库和本地仓库中央仓库本地仓库 Maven 命令generate 命令compile 命令clean 命令test 命令package 命令install 命令 Maven 插件机制官方插件&#xff1a;Compile 插件Tomcat 7 插件 Maven 仓库 中央仓库和本地仓库 [✎] 简单一点说 中央仓库是一个网…