回显服务器的制作方法
文章目录
- 客户端和服务器
- TCP和UDP的特点
- UDP socket api的使用
- DatagramSocket
- DatagramPacket
- InetSocketAddress API
- 做一个简单的回显服务器
- UDP版本的回显服务器
- TCP版本的回显服务器
客户端和服务器
在网络中,主动发起通信的一方是客户端,被动接受的这一方叫做服务器。
- 客户端给服务器发送的数据是请求(request)
- 服务器给客户端返回的数据叫响应(response)
客户端和服务器的交互有多种模式
- 一问一答: 一个请求对应一个响应 。多在网站开发中用到
- 一问多答:一个请求对应多个相应。主要涉及“下载”的场景中
- 多问一答:多个请求队对应一个响应 。涉及到“上传”的场景中
- 多问多答:多个请求对应多个响应。用到“远程控制”和“远程桌面”中
TCP和UDP的特点
要想进行网络编程,需要使用系统的api,本质上是传输层提供的。传输层用到的协议主要是TCP和UDP。这两个协议的差别挺大,所提供的api也有一定的差别。先从本质上看看TCP和UDP的特点有哪些差别。
- TCP的特点是 有连接,可靠传输,面向字节流,全双工。
- UDP的特点是 无连接,不可靠传输,面向数据报,全双工。
有连接/无连接
举个例子,打电话,需要对方同意后才能打通电话,打电话的过程需要对方确认接听或者不接听,连接的特点就是双方都能认同,而无连接的规则向发微信,自己只管发送,不用在意对方是否同意接收。同理,计算机中的网络连接,就是通信双方,各自保存对方的信息,客户端就有一些数据结构,记录了谁是自己的服务器,服务器也有一些数据结构,记录了谁是自己的客户端。可靠传输/不可靠传输
网络上存在异常情况是非常多的,无论使用什么硬件技术都无法100%保证数据能从A发送到B。此处所说的可靠传输是指尽可能的完成数据传输,即不管数据有没有传输到,A都能清楚的知道。面向字节流/面向数据报
此处提到的字节流和文件中的字节流是一样的。网络中传输数据的基本单位就是字节。
面向数据报:每次传输的基本单位是一个数据报(有一系列字节构成的)特定的结构。全双工/半双工
一个信道可以双向通信,是全双工。只能单向通信是半双工。
UDP socket api的使用
socket api的中文意思是"网络编程套接字",操作系统中有一类文件叫socket文件,它抽象了网卡这样的硬件设备,而进行网络通信最核心的硬件设备就是网卡,通过网卡发送数据就是写socket文件,通过网卡接收数据就是读socket文件。在UDP中,核心的api有两个类,分别是DatagramSocket和DatagramPacket。下面看看这两个类的使用和注意事项。
DatagramSocket
这个类的作用主要是对soclet文件的读写,也就是借助网卡发送接收数据。接收发送接收数据的单位就是DatagramSocket.。
datagramSocket的构造方法

DatagramSocket的方法

DatagramPacket
UDP是面向数据报的,每次发送接收数据的基本单位,就是一个udp数据报,此时表示了一个UDP数据报
DatagramPacket的构造方法

DatagramPacket方法

InetSocketAddress API

做一个简单的回显服务器
UDP版本的回显服务器
回显服务器是客户端发送什么请求,服务器就返回什么响应。做这个服务器的目的是学习UDP socket
api的使用和理解网络编程中客户端和服务器的基本工作流程
服务器的基本逻辑
import java.io.IOException;
import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.net.SocketException;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 方便后续的逻辑处理.String request = new String(requestPacket.getData(), 0, requestPacket.getLength());// 2. 根据请求计算响应 (对于 回显服务器来说, 这一步啥都不用做)String response = process(request);// 3. 把响应返回到客户端.// 构造一个 DatagramPacket 作为响应对象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);}}public String process(String request) {return request;}public static void main(String[] args) throws IOException {UdpEchoServer server = new UdpEchoServer(9090);server.start();}
}
首先创建DatagramSocket对象,接下来要使用socket对象来操作网卡。在创建对象的时候需要手动指定端口。在网络编程中,服务器一般需要手动指定端口,而客户端一般不需要。一个主机的一个端口只能被一个进程绑定,而一个进程可以绑定多个端口。
一个服务器一般都是24时运行的,直接使用while(true)可以不用退出。很多时候要重启一个服务器可以直接杀死进程。
此处 receive就从网卡能读取到一个 UDP 数据报就被放到了 requestPacket 对象中其中 UDP 数据报的载荷部分就被放到 requestPacket 内置的字节数组中了。另外报头部分,也会被 requestPacket 的其他属性保存。除了 UDP 报头之外,还有其他信息,比如收到的数据源 IP 是啥
通过 requestPacket 还能知道数据从哪里来的(源 ip 源端口)基于字节数组构造出 String字节数组里面保存的内容也不一定就是二进制数据,也可能是文本数据把文本数据交给 String 来保存,恰到好处~~
这里得到的长度是 requestPacket 中的有效长度,不一定是 40964096 是最大长度。一定是要使用有效长度来构造这里的 String使用最大长度就会生成一个非常长的 String 后半部分都是空白。通过process()方法构造响应,这是一个回显服务器,直接返回请求就可以。
通过requestPacket.getSocketAddress())获得对应客户端的ip和端口。是把请求的源ip和源端口作为响应的目的ip和目的端口。
总结
- 上述代码中,可以看到,UDP 是无连接的通信 UDP socket 自身不保存对端的IP 和端口.而是在每个数据报中有一个~.另外代码中也没有“建立连接”"接受连接”操作
- 不可靠传输,代码中体现不到的.
- 面向数据报,send和receive 都是以 DatagramPacket 为单位
- 全双工:一个 socket 既可以发送又可以接收
客户端的基本逻辑
import java.io.IOException;
import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.net.InetAddress;
import java.net.SocketException;
import java.util.Scanner;public class UdpEchoClient {private DatagramSocket socket = null;private String serverIp;private int serverPort;// 此处 ip 使用的字符串, 点分十进制风格. "192.168.2.100"public UdpEchoClient(String serverIp, int serverPort) throws SocketException {//请求的目的IP和目的端口this.serverIp = serverIp;this.serverPort = serverPort;socket = new DatagramSocket();}public void start() throws IOException {System.out.println("客户端启动");Scanner scanner = new Scanner(System.in);while (true) {// 要做四个事情System.out.print("-> "); // 表示提示用户接下来要输入内容.// 2. 从控制台读取要发送的请求数据.if (!scanner.hasNext()) {break;}String request = scanner.next();// 3. 构造请求并发送.DatagramPacket requestPacket = new DatagramPacket(request.getBytes(), request.getBytes().length,InetAddress.getByName(serverIp), serverPort);socket.send(requestPacket);// 4. 读取服务器的响应.DatagramPacket responsePacket = new DatagramPacket(new byte[4096], 4096);socket.receive(responsePacket);// 5. 把响应显示到控制台上.String response = new String(responsePacket.getData(), 0, responsePacket.getLength());System.out.println(response);}}public static void main(String[] args) throws IOException {UdpEchoClient client = new UdpEchoClient("127.0.0.1", 9090);client.start();}
}
三种DatagramSocket对象的构造方法

网络通信的基本流程
- 服务器启动.启动之后,立即进入 while 循,执行到 receive,进入阻塞,此时没有任何客户端发来请求呢。
- 客户端启动.启动之后,立即进入 while 循环,执行到 hasNext 这里进入阻塞,此时用户没有在控制台输入任何内容
- 用户在客户端的控制台中输入字符串,按下回车此时 hasNext 阻塞解除,next 会返回刚才输入的内容基于用户输入的内容,构造出一个 DatagramPacket 对象,并进行 send 。send执行完毕之后,继续执行到
reeive 操作,等待服务器返回的响应数据此时服务器还没返回响应呢,这里也会阻塞)- 服务器收到请求之后,就会从 receive 的阻塞中返回返回之后,就会根据读到的 DataqramPacket 对象,构造 String request, 通过 process 方法构造一个 String response再根据 response 构造一个
DatagramPacket表示响应对象, 再通过 send 来进行发送给客户端。执行这个过程中,客户端也始终在阻塞等待- 客户端从 receive 中返回执行.就能够得到服务器返回的响应并且打印倒控制台上于此同时,服务器进入下一次循环,也要进入到第二次的 receive 阳塞等待下个请求了
TCP版本的回显服务器
tcp中的长短连接
TCP发送数据时,需要先建立连接,什么时候关闭连接就决定是短连接还是长连接:
- 短连接:每次接收到数据并返回响应后,都关闭连接,即是短连接。也就是说,短连接只能一次收发数据。
- 长连接:不关闭连接,一直保持连接状态,双方不停的收发数据,即是长连接。也就是说,长连接可以多次收发数据。
对比以上长短连接,两者区别如下:
- 建立连接、关闭连接的耗时:短连接每次请求、响应都需要建立连接,关闭连接;而长连接只需要第一次建立连接,之后的请求、响应都可以直接传输。相对来说建立连接,关闭连接也是要耗时的,长连接效率更高。
- 主动发送请求不同:短连接一般是客户端主动向服务端发送请求;而长连接可以是客户端主动发送请求,也可以是服务端主动发。
- 两者的使用场景有不同:短连接适用于客户端请求频率不高的场景,如浏览网页等。长连接适用于 客户端与服务端通信频繁的场景,如聊天室,实时游戏等服务器的处理逻辑。
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 {private ServerSocket serverSocket = null;public TcpEchoServer(int port) throws IOException {serverSocket = new ServerSocket(port);}public void start() throws IOException {System.out.println("服务器启动!");ExecutorService pool = Executors.newCachedThreadPool();while (true) {// 通过 accept 方法来 "接听电话", 然后才能进行通信Socket clientSocket = serverSocket.accept();
// Thread t = new Thread(() -> {
// processConnection(clientSocket);
// });
// t.start();pool.submit(new Runnable() {@Overridepublic void run() {processConnection(clientSocket);}});}}// 通过这个方法来处理一次连接. 连接建立的过程中就会涉及到多次的请求响应交互.private void processConnection(Socket clientSocket) {System.out.printf("[%s:%d] 客户端上线!\n", clientSocket.getInetAddress(), clientSocket.getPort());// 循环的读取客户端的请求并返回响应.try (InputStream inputStream = clientSocket.getInputStream();OutputStream outputStream = clientSocket.getOutputStream()) {while (true) {Scanner scanner = new Scanner(inputStream);if (!scanner.hasNext()) {// 读取完毕. 客户端断开连接, 就会产生读取完毕.System.out.printf("[%s:%d] 客户端下线!\n", clientSocket.getInetAddress(), clientSocket.getPort());break;}// 1. 读取请求并解析. 这里注意隐藏的约定. next 读的时候要读到空白符才会结束.// 因此就要求客户端发来的请求必须带有空白符结尾. 比如 \n 或者空格.String request = scanner.next();// 2. 根据请求计算响应String response = process(request);// 3. 把响应返回给客户端// 通过这种方式可以写回, 但是这种方式不方便给返回的响应中添加 \n// outputStream.write(response.getBytes(), 0, response.getBytes().length);// 也可以给 outputStream 套上一层, 完成更方便的写入.PrintWriter printWriter = new PrintWriter(outputStream);printWriter.println(response);printWriter.flush();System.out.printf("[%s:%d] req: %s, resp: %s\n", clientSocket.getInetAddress(), clientSocket.getPort(),request, response);}} catch (IOException e) {throw new RuntimeException(e);} finally {try {clientSocket.close();} catch (IOException e) {throw new RuntimeException(e);}}}public String process(String request) {return request;}public static void main(String[] args) throws IOException {TcpEchoServer server = new TcpEchoServer(9090);server.start();}
}


- 在服务器代码中,ServerSocket是创建服务端Socket的api。Socket是客户端Socket,或服务端中接收到客户端建立连接(accept方法)的请求后,返回的服务端Socket.
不管是客户端还是服务端Socket,都是双方建立连接以后,保存的对端信息,及用来与对方收发数据的。- 当客户端发送请求的时候,内核就会发起建立连接的请求,服务器的内核会配合客户端的工作来建立连接,而内核的连接不是决定性的,还需要应用程序把这个连接接受,通过accept方法来接受连接。accept方法方法是会阻塞等待的,当没有客户端发起请求的时候此时就会阻塞。
- 上面的操作也表现出Tcp是有连接的。

- Tcp是面向字节流的。这里的字节流和文件中的字节流完全一致。使用和文件操作一样的类和方法来针对Tcp Socket的读和写。
- InputStream是往网卡上读数据,OutputStream是往网卡上写数据。
客户端的处理逻辑
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 serverPort) throws IOException {// 此处可以把这里的 ip 和 port 直接传给 socket 对象.// 由于 tcp 是有连接的. 因此 socket 里面就会保存好这俩信息.// 因此此处 TcpEchoClient 类就不必保存.socket = new Socket(serverIp, serverPort);}public void start() {System.out.println("客户端启动!");try (InputStream inputStream = socket.getInputStream()) {try (OutputStream outputStream = socket.getOutputStream()) {Scanner scannerConsole = new Scanner(System.in);Scanner scannerNetwork = new Scanner(inputStream);PrintWriter writer = new PrintWriter(outputStream);while (true) {// 这里的流程和 UDP 的客户端类似.// 1. 从控制台读取输入的字符串System.out.print("-> ");if (!scannerConsole.hasNext()) {break;}String request = scannerConsole.next();// 2. 把请求发给服务器. 这里需要使用 println 来发送. 为了让发送的请求末尾带有 \n// 这里是和服务器的 scanner.next 呼应的.writer.println(request);// 通过这个 flush 主动刷新缓冲区, 确保数据真的发出去了.writer.flush();// 3. 从服务器读取响应. 这里也是和服务器返回响应的逻辑对应.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 client = new TcpEchoClient("127.0.0.1", 9090);client.start();}
}
在TCP回显服务器中,需要注意下面几种情况
- 在上面代码中,PrintWriter中内置了缓冲区,IO操作都是很低效的,为了让低效的操作少一些,会引入一个缓冲区,先把写入网卡的数据王道缓冲区中,等达到一定的数量在一次发送出去,但如果发送的数据太少,缓冲区没有满,可能导致数据发送不出去,使用flush方法可以冲刷缓冲区,确保每条消息都能发送出去。

- ServerSocket在整个程序中,只有唯一一个对象,并且这个对象的生命周期伴随着整个程序,这个对象无法提前关闭,只有程序结束,随着进程的销毁一起结束。而clientSocket是每个客户端一个,随着客户端越来越多,如果不释放可能会占满文件描述符表。需要使用close方法关闭。

- 解决多个客户端向一个服务器发送请求的问题

上面的问题核心思路就是使用多线程,单个线程无法及给客户端提供服务,又能快速调用第二次accept,使用多线程,主线程就负责执行accept,其他线程就负责给客户端提供服务。如果客户端比较多就会频繁的创建销毁线程,就可以使用线程池解决频繁创建销毁线程的问题。

相关文章:
回显服务器的制作方法
文章目录 客户端和服务器TCP和UDP的特点UDP socket api的使用DatagramSocketDatagramPacketInetSocketAddress API 做一个简单的回显服务器UDP版本的回显服务器TCP版本的回显服务器 客户端和服务器 在网络中,主动发起通信的一方是客户端,被动接受的这一方…...
w28DVWA-csrf实例
DVWA-csrf实例 low级别 修改密码:修改的密码通过get请求,暴露在url上。 写一个简单的html文件,里面伪装修改密码的文字,代码如下: <html><body><a href"http://dvwa:7001/vulnerabilities/csr…...
子网络划分与互通,上网行为审计
网络环境需求:在办公网络环境中,由于公司部门的划分,以及服务器、电脑、手机等设备类型,一般都需要划分多个网段,便于进行网络管理,并提升网络通信效率。各个子网段管理员控制设备的接入,子网段之间需要进行局域网通信,发送消息和文件,通常使用飞秋。服务器网段,禁止…...
如何快速删除node_module依赖包
利用npm:输入 npm install rimraf -g rimraf node_modules...
async/await 的用法
一、async和await定义 async 是异步的意思,而 await 是等待的意思,await 用于等待一个异步任务执行完成的结果。 1.async/await 是一种编写异步代码的新方法(以前是采用回调和 promise)。 2. async/await 是建立在 promise 的基础…...
JAVA面试汇总总结更新中ing
本人面试积累面试题 基础RocketMQSpring登录技能操作线程事务微服务JVMKAFKAMYSQLRedislinux 基础 1.面向对象的三个特征 封装,继承,多态,有时候也会加上抽象。 2.多态的好处 允许不同类对象对同一消息做出响应,即同一消息可以根…...
vue-利用属性(v-if)控制表单(el-form-item)显示/隐藏
表单控制属性 v-if 示例: 通过switch组件作为开关,控制表单的显示与隐藏 <el-form-item label"创建数据集"><el-switch v-model"selectFormVisible"></el-switch></el-form-item><el-form-item label&…...
数据结构-邻接矩阵
介绍 邻接矩阵,是表示图的一种常见方式,具体表现为一个记录了各顶点连接情况的呈正方形的矩阵。 假设一共有以下顶点,其连接关系如图所示 那么,怎么表示它们之间的连接关系呢? 我们发现,各条边所连接的都…...
基于CNN-GRU-Attention的时间序列回归预测matlab仿真
目录 1.算法运行效果图预览 2.算法运行软件版本 3.部分核心程序 4.算法理论概述 4.1 CNN(卷积神经网络)部分 4.2 GRU(门控循环单元)部分 4.3 Attention机制部分 5.算法完整程序工程 1.算法运行效果图预览 2.算法运行软件版…...
Docker部署Halo容器并结合内网穿透实现公网访问本地个人博客
文章目录 1. Docker部署Halo1.1 检查Docker版本如果未安装Docker可参考已安装Docker步骤:1.2 在Docker中部署Halo 2. Linux安装Cpolar2.1 打开服务器防火墙2.2 安装cpolar内网穿透 3. 配置Halo个人博客公网地址4. 固定Halo公网地址 本文主要介绍如何在CentOS 7系统使…...
纯css实现文字左右循环滚动播放效果
思路:由两个span模块组成,第一个为空的span内容,为的是实现第二个span内容缓慢出现的效果。 代码如下: <div class"scrollingStyle"><span class"first-marquee"></span><span class&q…...
【Java EE初阶二十二】https的简单理解
1. 初识https 当前网络上,主要都是 HTTPS 了,很少能见到 HTTP.实际上 HTTPS 也是基于 HTTP.只不过 HTTPS 在 HTTP 的基础之上, 引入了"加密"机制;引入 HTTPS 防止你的数据被黑客篡改 ; HTTPS 就是一个重要的保护措施.之所以能够安全, 最关键的…...
系统学习Python——装饰器:类装饰器-[跟踪对象接口:基础知识]
分类目录:《系统学习Python》总目录 文章《系统学习Python——装饰器:类装饰器-[单例类:基础知识]》的单例示例阐明了如何使用类装饰器来管理一个类的所有实例。类装饰器的另一个常用场景是为每个生成的实例扩展接口。类装饰器基本上可以在实…...
go-redis 使用 redis 6.0.14 版本错误: consider implementing encoding.BinaryMarshaler
使用方法 err : bp.data.redis.Get(ctx, policyKey).Scan(&result)起初在 redis 5.x.x 版本并没有遇到错误,但是在切换 redis 实例之后就出现了错误(他们之间只是版本不同)。 修复方法 看错误日志的描述,大概含义就是需要我们…...
计网 - 域名解析的工作流程
文章目录 Pre引言1. DNS是什么2. 域名结构3. 域名解析的工作流程4. 常见的DNS记录类型5. DNS安全6. 未来的发展趋势 Pre 计网 - DNS 域名解析系统 引言 在我们日常使用互联网时,经常会输入各种域名来访问网站、发送电子邮件或连接其他网络服务。然而,我…...
普中51单片机学习(EEPROM)
EEPROM IIC串行总线的组成及工作原理 I2C总线的数据传送 数据位的有效性规定 I2C总线进行数据传送时,时钟信号为高电平期间,数据线上的数据必须保持稳定,只有在时钟线上的信号为低电平期间,数据线上的高电平或低电平状态才允许…...
智能风控体系之供应链业务模式
供应链金融是一种针对中小企业的新型融资模式,将资金流有效整合到供应链管理的过程中,既为供应链各环节企业提供贸易资金服务,又为供应链弱势企业提供新型贷款融资服务,以核心客户为依托,以真实贸易背景为前提…...
最少停车数(C 语言)
题目描述 特定大小的停车场,数组cars[]表示,其中1表示有车,0表示没车。车辆大小不一,小车占一个车位(长度1),货车占两个车位(长度2),卡车占三个车位…...
MAC M1安装vmware和centos7虚拟机并配置静态ip
一、下载vmware和centos7镜像 1、VMWare Fusion 官网的下载地址是:下载地址 下载好之后注册需要秘钥,在官网注册后使用免费的个人秘钥 2、centos7 下载地址: https://biosyxh.cn:5001/sharing/pAlcCGNJf 二、虚拟机安装 直接将下…...
java 课程签到管理系统Myeclipse开发mysql数据库web结构jsp编程servlet计算机网页项目
一、源码特点 java 课程签到管理系统是一套完善的java web信息管理系统 采用serlvetdaobean,对理解JSP java编程开发语言有帮助,系统具有完整的源代码和数据库,系统主要采用B/S模式开发。开发环境为TOMCAT7.0,Myeclipse8.5开发࿰…...
Vue记事本应用实现教程
文章目录 1. 项目介绍2. 开发环境准备3. 设计应用界面4. 创建Vue实例和数据模型5. 实现记事本功能5.1 添加新记事项5.2 删除记事项5.3 清空所有记事 6. 添加样式7. 功能扩展:显示创建时间8. 功能扩展:记事项搜索9. 完整代码10. Vue知识点解析10.1 数据绑…...
springboot 百货中心供应链管理系统小程序
一、前言 随着我国经济迅速发展,人们对手机的需求越来越大,各种手机软件也都在被广泛应用,但是对于手机进行数据信息管理,对于手机的各种软件也是备受用户的喜爱,百货中心供应链管理系统被用户普遍使用,为方…...
C++:std::is_convertible
C++标志库中提供is_convertible,可以测试一种类型是否可以转换为另一只类型: template <class From, class To> struct is_convertible; 使用举例: #include <iostream> #include <string>using namespace std;struct A { }; struct B : A { };int main…...
反向工程与模型迁移:打造未来商品详情API的可持续创新体系
在电商行业蓬勃发展的当下,商品详情API作为连接电商平台与开发者、商家及用户的关键纽带,其重要性日益凸显。传统商品详情API主要聚焦于商品基本信息(如名称、价格、库存等)的获取与展示,已难以满足市场对个性化、智能…...
Zustand 状态管理库:极简而强大的解决方案
Zustand 是一个轻量级、快速和可扩展的状态管理库,特别适合 React 应用。它以简洁的 API 和高效的性能解决了 Redux 等状态管理方案中的繁琐问题。 核心优势对比 基本使用指南 1. 创建 Store // store.js import create from zustandconst useStore create((set)…...
多场景 OkHttpClient 管理器 - Android 网络通信解决方案
下面是一个完整的 Android 实现,展示如何创建和管理多个 OkHttpClient 实例,分别用于长连接、普通 HTTP 请求和文件下载场景。 <?xml version"1.0" encoding"utf-8"?> <LinearLayout xmlns:android"http://schemas…...
转转集团旗下首家二手多品类循环仓店“超级转转”开业
6月9日,国内领先的循环经济企业转转集团旗下首家二手多品类循环仓店“超级转转”正式开业。 转转集团创始人兼CEO黄炜、转转循环时尚发起人朱珠、转转集团COO兼红布林CEO胡伟琨、王府井集团副总裁祝捷等出席了开业剪彩仪式。 据「TMT星球」了解,“超级…...
鸿蒙中用HarmonyOS SDK应用服务 HarmonyOS5开发一个生活电费的缴纳和查询小程序
一、项目初始化与配置 1. 创建项目 ohpm init harmony/utility-payment-app 2. 配置权限 // module.json5 {"requestPermissions": [{"name": "ohos.permission.INTERNET"},{"name": "ohos.permission.GET_NETWORK_INFO"…...
零基础设计模式——行为型模式 - 责任链模式
第四部分:行为型模式 - 责任链模式 (Chain of Responsibility Pattern) 欢迎来到行为型模式的学习!行为型模式关注对象之间的职责分配、算法封装和对象间的交互。我们将学习的第一个行为型模式是责任链模式。 核心思想:使多个对象都有机会处…...
20个超级好用的 CSS 动画库
分享 20 个最佳 CSS 动画库。 它们中的大多数将生成纯 CSS 代码,而不需要任何外部库。 1.Animate.css 一个开箱即用型的跨浏览器动画库,可供你在项目中使用。 2.Magic Animations CSS3 一组简单的动画,可以包含在你的网页或应用项目中。 3.An…...



