JavaEE 网络编程

news2024/9/30 4:48:30

JavaEE 网络编程

文章目录

  • JavaEE 网络编程
    • 引子
    • 1. 网络编程-相关概念
      • 1.1 基本概念
      • 1.2 发送端和接收端
      • 1.3 请求和响应
      • 1.4 客户端和服务端
    • 2. Socket 套接字
      • 2.1 数据包套接字通信模型
      • 2.2 流套接字通信模型
      • 2.3 Socket编程注意事项
    • 3. UDP数据报套接字编程
      • 3.1 DatagramSocket
      • 3.2 DatagramPacket
    • 4. TCP流套接字编程
      • 4.1 ServerSocket
      • 4.2 Socket

引子

如今,我们在任意浏览器打开在线视频网站,如b站去看视频,实质上便是通过网络来获取网络上的视频资源:

在这里插入图片描述

与本地打开视频文件类似,只是当前的视频文件资源的来源是网络,相比于本地资源来说,网络提供了更为丰富的网络资源:

在这里插入图片描述

所谓的网络资源,其实就是在网络中可以获取的各种数据资源。

而所有的网络资源,都是通过网络编程来进行数据传输的。

1. 网络编程-相关概念

1.1 基本概念

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

在这里插入图片描述

网络编程的目的便是提供网络上不同的主机基于网络来传输数据资源,如图:

进程A:编程来获取网络资源;进程B:编程来提供网络资源

1.2 发送端和接收端

在一次网络数据传输时:

  • 发送端: 数据的发送方进程,称为发送端。发送端主机即网络通信中的源主机;

  • 接收端: 数据的接受方进程,称为接收端。接收端主机即网络通信中的目的主机;

  • 收发端: 发送端和接收端两端,也简称收发端;

在这里插入图片描述

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

1.3 请求和响应

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

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

好比在饭店点一份炒饭,先要发起请求:点一份炒饭,再有饭店提供对应的响应:提供一份炒饭

在这里插入图片描述

1.4 客户端和服务端

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

一般的客户端服务端模型提供以下操作:

  1. 客户端获取服务资源

    在这里插入图片描述

  2. 客户端保存资源在服务端

    在这里插入图片描述

最常见的模型是,客户端是指给用户使用的程序,服务端是提供用户服务的程序

  1. 客户端先发送请求到服务端;
  2. 服务端根据请求数据,执行相应的业务处理;
  3. 服务端返回响应:发送业务处理结果;
  4. 客户端根据响应数据,展示处理结果(展示获取的资源,或提示保存数据资源的处理结果)

在这里插入图片描述

2. Socket 套接字

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

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

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

UDP,即User Datagram Protocol (用户数据报协议),传输层协议。

特点:

  • 无连接;
  • 不可靠传输;
  • 面向数据报;
  • 有接收缓冲区,无发送缓冲区;
  • 大小受限:一次最多传输64K;

对于数据报来说,可以简单理解为,传输数据是一块一块的,发送一块数据假如要100个字节,必须一次发送,接收也必须一次接收100个字节,而不能分100次,每次接收1个字节。

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

TCP,即Transmission Control Protocol (传输控制协议),传输层协议。

特点:

  • 有连接;
  • 可靠传输;
  • 面向字节流;
  • 有接收缓冲区,也有发送缓冲区;
  • 大小不限;

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

原始套接字: 用于自定义传输层协议,用于读写内核没有处理的IP协议数据(了解)。

2.1 数据包套接字通信模型

对于UDP协议来说,具有无连接,面向数据报的特征,即每次都是没有建立连接,并且一次发送全部数据报,一次接收全部的数据报

Java中使用UDP协议通信,主要基于 DatagramSocket类来创建数据包套接字,并使用 DatagramPacket作为发送或接收的UDP数据报。

一般发送及接收UDP数据报的流程如下:

在这里插入图片描述

2.2 流套接字通信模型

对于TCP协议来说,其具有连接且面向字节流的特征,在进行网络通信前需要通过accept()接收连接请求建立连接后才可进行通信:

在这里插入图片描述

2.3 Socket编程注意事项

在这里插入图片描述

  1. 客户端和服务端:开发时,经常是基于一个主机开启两个进程作为客户端和服务端,但真实的场景一般是不同主机;

  2. 需要注意目的IP和目的端口号,标识了一次数据传输时要发送数据的终点主机和进程

  3. 如果一个进程A已经绑定了一个端口,再启动一个进程B绑定该端口,就会报错,这种情况也叫端口被占用。对于Java进程来说,端口被占用常见报错信息如下:

    在这里插入图片描述

    对此,如果占用端口的进程A不需要运行,可以关闭A后再启动需要绑定该端口的进程B;如果需要运行A进程,则可以修改进程B的绑定端口,换为其它没有使用的端口。

3. UDP数据报套接字编程

以下是UDP数据报编程相关的API:

3.1 DatagramSocket

DatagramSocket是UDP Socket 用于发送和接收UDP的数据报。

DatagramSocket构造方法

方法签名方法说明
DatagramSocket()创建一个UDP数据报套接字的Socket,绑定到本机任意一个随机端口(一般用于客户端
DatagramSocket(int port)创建一个UDP数据报套接字的Socket,绑定到本机指定的端口(一般用于服务端

DatagramSocket方法

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

3.2 DatagramPacket

DatagramPacket是UDP Socket发送和接收的数据报,传递的数据内容参数都存储在由它构建的对象中。

DatagramPacket构造方法

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

DatagramPacket方法

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

在UDP客户端发送数据报时,需要传入SocketAddress,该对象可以使用InetSocketAddress来创建

方法签名方法说明
InetSocketAddress(InetAddress addr, int port)创建一个Socket地址,包含IP地址和端口号

代码示例

UDP Echo Server(服务器)

package netWork;

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 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();
    }
}

在这里插入图片描述

UDP Echo Client(客户端)

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;

    // 此处ip使用的字符串,点分十进制风格 "192.168.2.100"
    public UdpEchoClient(String serverIp, int serverPort) throws SocketException {
        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("->"); // 提示用户接下来要输入内容
            // 1. 从控制台读取要发送的请求数据
            if (!scanner.hasNext()) {
                break;
            }
            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);
            // 4. 把响应显示到控制台上
            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();
    }
}

在这里插入图片描述

UDP Dict Server(字典服务器)

编写一个英译汉的服务器,只需要重写process

package netWork;

import java.io.IOException;
import java.net.SocketException;
import java.util.HashMap;

public class UdpDictServer extends UdpEchoServer{

    HashMap<String, String> map = new HashMap<>();
    public UdpDictServer(int port) throws SocketException {
        super(port);
        map.put("cat", "猫");
        map.put("dog", "狗");
        map.put("apple", "苹果");
    }

    @Override
    public String process(String request) {
        return map.getOrDefault(request, "所查单词不存在!");
    }

    public static void main(String[] args) throws IOException {
        UdpDictServer server = new UdpDictServer(9090);
        server.start();
    }
}

在这里插入图片描述

:因为此处使用的字典服务器与上述回显服务器端口号相同,如果同时启动会报错,因此需要关闭其中一个服务器才能使用!

4. TCP流套接字编程

以下是TCP流编程相关的API:

4.1 ServerSocket

ServerSocket是创建TCP服务端Socket的API

ServerSocket构造方法

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

ServerSocket方法

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

4.2 Socket

Socket有两种存在形式:

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

Socket构造方法

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

Socket方法

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

代码示例

TCP Echo Server(服务器)

package netWork;

import java.io.*;
import java.net.ServerSocket;
import java.net.Socket;
import java.util.Scanner;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

/*TCP 回显服务器*/
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) {
            // 通过 accept 方法来“接听电话”,然后才能进行通信
            Socket clientSocket = serverSocket.accept(); // 在未接收到客户端连接请求前到此处将处于阻塞状态
            processConnection(clientSocket);
            
        }
    }

    // 通过这个方法来处理一次连接,连接建立的过程中就会涉及到多次的请求响应交互
    public 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();
    }
}

在这里插入图片描述

TCP Echo Client(客户端)

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 serverPort) throws IOException {
        // 此处可以把这里的 ip 和 port 直接传给socket对象
        // 因为此处 tcp 是有连接的,因此 socket 里面就会保存好这两信息
        // 因此此处 TcpEchoClient 类就不必保存
        socket = new Socket(serverIp, serverPort);
    }

    public void start() throws IOException {
        System.out.println("客户端启动!");
        try (InputStream inputStream = socket.getInputStream();OutputStream outputStream = socket.getOutputStream()) {
            Scanner scannerConsole = new Scanner(System.in);
            Scanner scannerNetWork = new Scanner(inputStream);
            PrintWriter printWriter = new PrintWriter(outputStream);
            while (true) {

                System.out.print("->");
                // 1. 从控制台读取输入的字符串
                if (!scannerConsole.hasNext()) {
                    break;
                }
                String request = scannerConsole.next();
                // 2. 将请求发送给服务器
                // 这里需要使用 println 来发送,为了让发送的请求末尾带有 \n , 这里与服务器的 scanner.next 相呼应
                printWriter.println(request);
                // 通过这个 flush 主动刷新缓冲区,确保数据真的发出去了
                printWriter.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.7", 9090);
        client.start();
    }
}

在这里插入图片描述

为了能够响应多个客户端请求,可以进行以下修改:

注:如果想在IDEA中同时打开多个相同的客户端,可以按一下步骤修改设置:

在这里插入图片描述

  • 给每个客户端都分配一个线程

    public void start() throws IOException {
            System.out.println("服务器启动!");
            
            while(true) {
                // 通过 accept 方法来“接听电话”,然后才能进行通信
                Socket clientSocket = serverSocket.accept();
                // processConnection(clientSocket);
                Thread t = new Thread(() -> {
                    processConnection(clientSocket);
                });
                t.start();
            }
        }
    

    在这里插入图片描述

  • 为了避免频繁创建销毁线程,可以引入线程池

     public void start() throws IOException {
            System.out.println("服务器启动!");
            // 可通过调用线程池的方式完成多个客户端的同时连接
            ExecutorService pool = Executors.newCachedThreadPool();
            while(true) {
                // 通过 accept 方法来“接听电话”,然后才能进行通信
                Socket clientSocket = serverSocket.accept();
    
                pool.submit(new Runnable() {
                    @Override
                    public void run() {
                        processConnection(clientSocket);
                    }
                });
            }
        }
    

    在这里插入图片描述

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

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

相关文章

pip 安装出现报错 SSLError(SSLError(“bad handshake

即使设置了清华源&#xff1a; pip config set global.index-url https://pypi.tuna.tsinghua.edu.cn/simplepip 安装包不能配置清华源&#xff0c;出现报错: Retrying (Retry(total2, connectNone, readNone, redirectNone, statusNone)) after connection broken by ‘SSLE…

适用于 Windows 的 10 款免费 MP4 转 MP3 转换神器

每当我们观看歌曲或视频剪辑时&#xff0c;我们经常会想到将其转换为 MP3 格式&#xff0c;以便我们可以将其保存在设备上&#xff0c;因为它占用的空间更少。在将 MP4 转换为 MP3 的过程中&#xff0c;第一步也是最重要的一步是选择正确的工具来转换它&#xff0c;如果您想添加…

API网关-Apisix RPM包方式自动化安装配置教程

文章目录 前言一、简介1. etcd简介2. APISIX简介3. apisix-dashboard简介 二、Apisix安装教程1. 复制脚本2. 增加执行权限3. 执行脚本4. 浏览器访问5. 卸载Apisix 三、命令1. Apisix命令1.1 启动apisix服务1.2 停止apisix服务1.3 优雅地停止apisix服务1.4 重启apisix服务1.5 重…

SG-8506CA 可编程晶体振荡器 (SPXO)

输出: LV-PECL频率范围: 50MHz ~ 800MHz电源电压: 2.5V to 3.3V外部尺寸规格: 7.0 5.0 1.5mm (8引脚)特性:用户指定一个起始频率, 7-bit I2C 地址:用户可编程: I2C 接口:基频的高频晶体:低抖动PLL技术应用:OTN, BTS, 测试设备 规格&#xff08;特征&#xff09; *1 这包括初…

链表--543. 二叉树的直径/medium 理解度C

543. 二叉树的直径 1、题目2、题目分析3、复杂度最优解代码示例4、适用场景 1、题目 给你一棵二叉树的根节点&#xff0c;返回该树的 直径 。 二叉树的 直径 是指树中任意两个节点之间最长路径的 长度 。这条路径可能经过也可能不经过根节点 root 。 两节点之间路径的 长度 …

Python Flask与APScheduler构建简易任务监控

1. Flask Web Flask诞生于2010年&#xff0c;是用Python语言&#xff0c;基于Werkzeug工具箱编写的轻量级、灵活的Web开发框架&#xff0c;非常适合初学者或小型到中型的 Web 项目。 Flask本身相当于一个内核&#xff0c;其他几乎所有的功能都要用到扩展&#xff08;邮件扩展…

案例分享 | 助力数字化转型:嘉为科技项目管理平台上线

嘉为科技项目管理平台&#xff08;一期&#xff09;基于易趋&#xff08;EasyTrack&#xff09;进行实施&#xff0c;通过近一年的开发及试运行&#xff0c;现已成功交付上线、推广使用&#xff0c;取得了良好的应用效果。 1.关于广州嘉为科技有限公司&#xff08;以下简称嘉为…

外卖跑腿系统开发:构建高效、安全的服务平台

在当今快节奏的生活中&#xff0c;外卖跑腿系统的开发已成为技术领域的一个重要课题。本文将介绍如何使用一些常见的编程语言和技术框架&#xff0c;构建一个高效、安全的外卖跑腿系统。 1. 技术选择 在开始开发之前&#xff0c;我们需要选择适合的技术栈。常用的技术包括&a…

idea使用注释时如何不从行首开始

1、File—>setting 2、找到Editor&#xff0c;点Code Style 1.对于java注释设置 点java&#xff0c;然后选择Code Generation,去掉Line comment at first column,选择Add a space at comment start 2.对于xml注释设置 点XML&#xff0c;然后选择Code Generation,去掉Line c…

java-数组(以及jvm的内存分布)

文章目录 数组的基本概念数组的作用数组的创建以及初始化数组的创建数组的初始化 数组的使用数组中元素的访问遍历打印数组 数组是引用类型初始jvm的内存分布基本类型变量和引用类型变量的区别引用变量 认识null 数组的基本概念 数组可以看作是一种类型的集合我们在内存空间上…

Go 命令行解析 flag 包之快速上手

本篇文章是 Go 标准库 flag 包的快速上手篇。 概述 开发一个命令行工具&#xff0c;视复杂程度&#xff0c;一般要选择一个合适的命令行解析库&#xff0c;简单的需求用 Go 标准库 flag 就够了&#xff0c;flag 的使用非常简单。 当然&#xff0c;除了标准库 flag 外&#x…

Mac网线上网绿联扩展坞连接网线直接上网-无脑操作

声明&#xff1a;博主使用的绿联扩展坞 以下为绿联扩展坞Mac网线使用方法 1.首先需要下载电脑对应版本的驱动 直接点击即可下载 2. 下载好以后 解压 点进去 对应版本 博主直接使用最新的12-14 3. 安装包好了以后 会提示重启电脑 此时拔掉扩展坞 再重启动 拔掉扩展坞 再重启…

【Tomcat与网络1】史前时代—没有Spring该如何写Web服务

在前面我们介绍了网络与Java相关的问题&#xff0c; 最近在调研的时候发现这块内容其实非常复杂&#xff0c;涉及的内容多而且零碎&#xff0c;想短时间梳理出整个体系是不太可能的&#xff0c;所以我们还是继续看Tomcat的问题&#xff0c;后面有网络的内容继续补充吧。 目录 …

【python爬虫】爬虫编程技术的解密与实战

​&#x1f308;个人主页&#xff1a;Sarapines Programmer&#x1f525; 系列专栏&#xff1a; 爬虫】网络爬虫探秘⏰诗赋清音&#xff1a;云生高巅梦远游&#xff0c; 星光点缀碧海愁。 山川深邃情难晤&#xff0c; 剑气凌云志自修。 目录 &#x1f33c;实验目的 &#x1f…

Redis客户端之Jedis(一)介绍

目录 一、Jedis介绍&#xff1a; 1、背景&#xff1a; 2、Jedis连接池介绍&#xff1a; 二、Jedis API&#xff1a; 1、连接池API 2、其他常用API&#xff1a; 三、SpringBoot集成Jedis&#xff1a; 1、Redis集群模式&#xff1a; &#xff08;1&#xff09;配置文件…

如何用甘特图跟踪项目进度

甘特图是一个简单但是极其强大的项目管理工具,能够清晰可视化复杂项目的进度,在项目跟踪和控制上发挥重要作用。任何一个严肃的项目组织者都会使用甘特图来规划和管理项目中的任务。 甘特图的纵坐标表示项目的各项活动或任务,横坐标表示项目的时间进度。每个任务用一条横条表示…

杰理方案——WIFI连接物联网配置阿里云操作步骤

demo——DevKitBoard 注意:最好用这个Demo,其它Demo可能会有莫名其妙的错误问题。 wifi配置 需要在app_config.h文件中定义USE_DEMO_WIFI_TEST,工程会在wifi_demo_task.c文件中自动启动wifi相关的任务, 我们将工程配置为连接外部网络STA模式 默认工程会使用如下账号密码 这…

微信小程序 仿微信聊天界面

1. 需求效果图 2. 方案 为实现这样的效果&#xff0c;首先要解决两个问题&#xff1a; 2.1.点击输入框弹出软键盘后&#xff0c;将已有的少许聊天内容弹出&#xff0c;导致看不到的问题 点击输入框弹出软键盘后&#xff0c;将已有的少许聊天内容弹出&#xff0c;导致看不到的问…

2024新版68套Axure RP大数据可视化大屏模板及通用组件+PSD源文件

Axure RP数据可视化大屏模板及通用组件库2024新版重新制作了这套新的数据可视化大屏模板及通用组件库V2版。新版本相比于V1版内容更加丰富和全面&#xff0c;但依然秉承“敏捷易用”的制作理念&#xff0c;这套作品也同样延续着我们对细节的完美追求&#xff0c;整个设计制作过…

uniapp安卓android离线打包本地打包整理

离线打包准备 下载Android studio 1.准备资源hbuilder 2.准备离线SDK 最新android平台SDK下载最新android平台SDK下载 3.离线打包key申请 4.直接导入HBuilder-Integrate-AS工程,直接运行simpleDemo项目即可 5.安装java 1.8 jdk-8u151-windows-x64 6.遇到这个报错报错Caus…