Socket套接字(网络编程万字总结-附代码)

news2024/11/18 0:19:47

文章目录

  • 前言
  • 一、概念
  • 二、分类(三类)
    • 2.1 流套接字:使用传输层TCP协议
    • 2.2 数据报套接字:使用传输层UDP协议
    • 2.3 原始套接字
  • 三、UDP数据报套接字编程
    • 3.1 Java数据报套接字通信模型
    • 3.2 DatagramSocket API
      • 3.2.1 ` DatagramSocket` 构造方法:
      • 3.2.2 ` DatagramSocket` 普通方法(属于DatagramSocket类):
    • 3.3 DatagramPacket API
      • 3.3.1 ` DatagramPacket ` 构造方法:
      • 3.3.2 ` DatagramPacket` 普通方法:
    • 3.4 InetSocketAddress API
    • 3.5 代码示例(有请求和相应)
  • 四、TCP数据报套接字编程
    • 4.1 Java流套接字通信模型
    • 4.2 ServerSocket API
      • 4.2.1 ` ServerSocket` 构造方法:
      • 4.2. ` ServerSocket` 普通方法
    • 4.3 Socket API
      • 4.3.1 ` Socket` 构造方法:
      • 4.3.2 ` Socket` 普通方法:
    • 4.4 TCP中的长短连接
    • 4.5 代码示例(短连接)
  • 五、 关于输入流和输出流的使用
    • 5.1 关于输入流的使用:
    • 5.2 关于输出流的使用:
  • 六、面向数据报文VS面向字节流
  • 总结


前言

博主个人社区:开发与算法学习社区

博主个人主页:Killing Vibe的博客

欢迎大家加入,一起交流学习~~

一、概念

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

Socket是站在应用层,做网络编程很重要的一个概念

传输层、网络层、数据链路层、物理层 都是通过OS+硬件来提供服务的,而应用层要享受OS提供的网络服务,需要通过OS提供的服务窗口(Socket)来享受服务。

拓展

OS原生的提供的系统调用(Linux上的网络编程):

int fd = socket();
setsocketopt(fd,TCP or UDP)

二、分类(三类)

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

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

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

以下为TCP的特点:

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

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

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

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

以下为UDP的特点:

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

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

2.3 原始套接字

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

三、UDP数据报套接字编程

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

3.1 Java数据报套接字通信模型

java中使用UDP协议通信,主要基于 DatagramSocket 类来创建数据报套接字,并使用
DatagramPacket 作为发送或接收的UDP数据报。对于一次发送及接收UDP数据报的流程如下:
在这里插入图片描述

3.2 DatagramSocket API

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

3.2.1 DatagramSocket 构造方法:

在这里插入图片描述
注意

  1. UDP服务器(Server):采用一个固定端口,方便客户端(Client)进行通信;
    使用 DatagramSocket(int port) ,就可以绑定到本机指定的端口,此方法可能有错误风险,提示该端口已经被其他进程占用。

  2. UDP客户端(Client):不需要采用固定端口(也可以用固定端口),采用随机端口;
    使用 DatagramSocket() ,绑定到本机任意一个随机端口

3.2.2 DatagramSocket 普通方法(属于DatagramSocket类):

在这里插入图片描述
注意

  1. 一旦通信双方逻辑意义上有了通信线路,双方地位就平等了(谁都可以作为发送方和接收方)

  2. 发送方调用的就是 send() 方法,接收方调用的就是 receive() 方法

  3. 通信结束后,双方都应该调用 close() 方法进行资源回收

3.3 DatagramPacket API

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

这个类就是定义的报文包:通信过程中的数据抽象

可以理解为:发送/接受的一个信封(五元组+信件)

3.3.1 DatagramPacket 构造方法:

在这里插入图片描述注意

  1. 作为接收方:只需要提供存放接受数据的位置(byte[] buf + int length)
  2. 作为发送方:需要有要发送的数据(byte[] buf +int offset +int length),要发送给谁(远端ip+远端port) 在这里插入图片描述

3.3.2 DatagramPacket 普通方法:

在这里插入图片描述注意

  1. 一般给服务器使用的是 getAddress() 方法和 getPort() 方法,用来获取客户端的ip地址和端口号port
  2. 一般给接收者(可以是服务器也可是客户端)使用的是 getData() ,用来拿到“信”(对方进程发送的应用层数据)

3.4 InetSocketAddress API

InetSocketAddress ( SocketAddress 的子类 )构造方法:

在这里插入图片描述

3.5 代码示例(有请求和相应)

以下仅展示部分代码,完整代码可以看博主的gitee仓库:

网络开发代码

UDP客户端

public class UserInputLoopClient {
    public static void main(String[] args) throws Exception {
        Scanner scanner = new Scanner(System.in);

        // 1. 创建 UDP socket
        Log.println("准备创建 UDP socket");

        DatagramSocket socket = new DatagramSocket();
        Log.println("UDP socket 创建结束");

        System.out.print("请输入英文单词: ");
        while (scanner.hasNextLine()) {
            // 2. 发送请求
            String engWord = scanner.nextLine();
            Log.println("英文单词是: " + engWord);
            String request = engWord;
            byte[] bytes = request.getBytes("UTF-8");

            // 手动构造服务器的地址
            // 现在,服务器和客户端在同一台主机上,所以,使用 127.0.0.1 (环回地址 loopback address)
            // 端口使用 TranslateServer.PORT(8888)
            InetAddress loopbackAddress = InetAddress.getLoopbackAddress();
            InetAddress remoteAddress = Inet4Address.getByName("182.254.132.183");

            DatagramPacket sentPacket = new DatagramPacket(
                    bytes, 0, bytes.length, // 要发送的数据
                    remoteAddress, TranslateServer.PORT   // 对方的 ip + port
            );
            Log.println("准备发送请求");
            socket.send(sentPacket);
            Log.println("请求发送结束");

            // 3. 接收响应
            byte[] buf = new byte[1024];
            DatagramPacket receivedPacket = new DatagramPacket(buf, buf.length);
            Log.println("准备接收响应");
            socket.receive(receivedPacket);
            Log.println("响应接收接收");
            byte[] data = receivedPacket.getData();
            int len = receivedPacket.getLength();

            String response = new String(data, 0, len, "UTF-8");
            String chiWord = response;
            Log.println("翻译结果: " + chiWord);

            System.out.print("请输入英文单词: ");
        }

        // 4. 关闭 socket
        socket.close();
    }
}

UDP服务端

// 提供翻译的服务器
public class TranslateServer {
    // 公开的 ip 地址:就看进程工作在哪个 ip 上
    // 公开的 port:需要程序中指定
    public static final int PORT = 8888;

    // SocketException -> IOException -> Exception
    public static void main(String[] args) throws Exception {
        Log.println("准备进行字典的初始化");
        initMap();
        Log.println("完成字典的初始化");

        Log.println("准备创建 UDP socket,端口是 " + PORT);
        DatagramSocket socket = new DatagramSocket(PORT);
        Log.println("UDP socket 创建成功");

        // 作为服务器,是被动的,循环的进行请求-响应周期的处理
        // 等待请求,处理并发送响应,直到永远
        while (true) {
            // 1. 接收请求
            byte[] buf = new byte[1024];    // 1024 代表我们最大接收的数据大小(字节)
            DatagramPacket receivedPacket = new DatagramPacket(buf, buf.length);
            Log.println("准备好接收 DatagramPacket,最大大小为: " + buf.length);
            Log.println("开始接收请求");
            socket.receive(receivedPacket); // 这个方法就会阻塞(程序执行到这里就不动了,直到有客户发来请求,才能继续)
            Log.println("接收到请求");

            // 2. 一旦走到此处,一定是接收到请求了,拆信
            // 拆出对方的 ip 地址
            InetAddress address = receivedPacket.getAddress();
            Log.println("对方的 IP 地址: " + address);
            // 拆出对方的端口
            int port = receivedPacket.getPort();
            Log.println("对方的 port: " + port);

            // 拆出对方的 ip 地址 + port
            SocketAddress socketAddress = receivedPacket.getSocketAddress();
            Log.println("对象的完整地址: " + socketAddress);

            // 拆出对方发送过来的数据,其实这个 data 就是我们刚才定义的 buf 数组
            byte[] data = receivedPacket.getData();
            Log.println("接收到的对象的数据: " + Arrays.toString(data));
            // 拆出接收到的数据的大小(字节)
            int length = receivedPacket.getLength();
            Log.println("接收的数据大小(字节):" + length);

            // 3. 解析请求 :意味着我们需要定义自己的应用层协议
            // 首先,做字符集解码    byte[] -> String
            String request = new String(data, 0, length, "UTF-8");
            // 这个按照我们的应用层协议
            String engWord = request;
            Log.println("请求(英文单词):" + engWord);

            // 4. 执行业务(翻译服务),不是我们本次演示的重点
            String chiWord = translate(engWord);
            Log.println("翻译后的结果:" + chiWord);

            // 5. 按照应用层协议,封装响应
            String response = chiWord;
            // 进行字符集编码  String -> byte[]
            byte[] sendBuf = response.getBytes("UTF-8");

            // 6. 发送响应
            // 作为发送方需要提供
            DatagramPacket sentPacket = new DatagramPacket(
                    sendBuf, 0, sendBuf.length,     // 要发送的数据
                    socketAddress                         // 从请求信封中拆出来的对象的地址(ip + port)
            );
            Log.println("准备好发送 DatagramPacket 并发送");
            socket.send(sentPacket);
            Log.println("发送成功");

            // 7. 本次请求-响应周期完成,继续下一次请求-响应周期
        }

//        socket.close(); // 由于我们是死循环,这里永远不会走到
    }

    private static final HashMap<String, String> map = new HashMap<>();

    private static void initMap() {
        map.put("apple", "苹果");
        map.put("pear", "梨");
        map.put("orange", "橙子");
    }

    private static String translate(String engWord) {
        String chiWord = map.getOrDefault(engWord, "查无此单词");
        return chiWord;
    }
}

自定义的日志类(记得导入此类)

public class Log {
    public static void println(Object o) {
        LocalDateTime localDateTime = LocalDateTime.now(ZoneId.of("Asia/Shanghai"));

        DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");

        String now = formatter.format(localDateTime);

        String message = now + ": " + (o == null ? "null" : o.toString());

        System.out.println(message);
    }

    public static void main(String[] args) {
        println(1);
    }
}

四、TCP数据报套接字编程

4.1 Java流套接字通信模型

在这里插入图片描述

4.2 ServerSocket API

ServerSocket 是创建TCP服务端Socket的API。

4.2.1 ServerSocket 构造方法:

在这里插入图片描述服务器使用的TCP Socket对象(传入的端口,就是要公开的端口,一般称为监听(listen)端口)

4.2. ServerSocket 普通方法

在这里插入图片描述注意

  1. accept:接起电话(服务器是电话铃响的这一方)

  2. Socket对象:建立起的连接在这里插入图片描述

  3. close:挂电话(谁都可以挂)

4.3 Socket API

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

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

4.3.1 Socket 构造方法:

在这里插入图片描述注意

  1. 服务器的Socket对象是从accept()中获取到的,所以,只有客户端的Socket对象需要手动实例化出来,这个构造方法是给客户端使用,传入服务器的ip+port
  2. 一旦socket对象拿到(双方是同时拿到的),双方就地位平等了,只区分发送方和接收方即可

4.3.2 Socket 普通方法:

在这里插入图片描述注意

  1. 输入流:站在进程角度,背后对象就是网卡,网卡抽象出来的TCP连接,所以是给接收方使用
  2. 输出流:同理,所以是给发送方使用

4.4 TCP中的长短连接

TCP发送数据时,需要先建立连接,什么时候关闭连接就决定是短连接还是长连接:

  1. 短连接:每次接收到数据并返回响应后,都关闭连接,即是短连接。也就是说,短连接只能一次收发数据。
  2. 长连接:不关闭连接,一直保持连接状态,双方不停的收发数据,即是长连接。也就是说,长连接可以多次收发数据。

对比以上长短连接,两者区别如下:

  • 建立连接、关闭连接的耗时:短连接每次请求、响应都需要建立连接,关闭连接;而长连接只需要第一次建立连接,之后的请求、响应都可以直接传输。相对来说建立连接,关闭连接也是要耗时的,长连接效率更高。
  • 主动发送请求不同:短连接一般是客户端主动向服务端发送请求;而长连接可以是客户端主动发送请求,也可以是服务端主动发。
  • 两者的使用场景有不同:短连接适用于客户端请求频率不高的场景,如浏览网页等。长连接适用于客户端与服务端通信频繁的场景,如聊天室,实时游戏等。

扩展了解:

基于BIO(同步阻塞IO)的长连接会一直占用系统资源。对于并发要求很高的服务端系统来说,这样的消耗是不能承受的。

由于每个连接都需要不停的阻塞等待接收数据,所以每个连接都会在一个线程中运行。
一次阻塞等待对应着一次请求、响应,不停处理也就是长连接的特性:一直不关闭连接,不停的处理请求。

实际应用时,服务端一般是基于NIO(即同步非阻塞IO)来实现长连接,性能可以极大的提升。

现在还遗留一个问题

如果同时多个长连接客户端,连接该服务器,能否正常处理?

需要在IDEA配置客户端支持同时运行多个实例!

  1. 短连接客户端 <–> 短连接服务器 支持同时在线
  2. 短连接客户端 <-> 长连接服务器 支持同时在线
  3. 长连接客户端 <-> 长连接服务器 不支持同时在线

所以可以使用多线程解决长连接客户端不支持同时在线的问题:

将任务专门交给其他线程来处理,主线程只负责接受socket。

4.5 代码示例(短连接)

这里仅演示短连接,长连接和多线程在博主的个人仓库下:

网络开发代码

TCP服务端:

public class TranslateServerShortConnection {
    public static final int PORT = 8888;

    public static void main(String[] args) throws Exception {
        Log.println("启动短连接版本的 TCP 服务器");
        initMap();
        ServerSocket serverSocket = new ServerSocket(PORT);
        while (true) {
            // 接电话
            Log.println("等待对方来连接");
            Socket socket = serverSocket.accept();
            Log.println("有客户端连接上来了");

            // 对方信息:
            InetAddress inetAddress = socket.getInetAddress();  // ip
            Log.println("对方的 ip: " + inetAddress);
            int port = socket.getPort();    // port
            Log.println("对方的 port: " + port);
            SocketAddress remoteSocketAddress = socket.getRemoteSocketAddress();    // ip + port
            Log.println("对方的 ip + port: " + remoteSocketAddress);

            // 读取请求
            InputStream inputStream = socket.getInputStream();
            Scanner scanner = new Scanner(inputStream, "UTF-8");
            String request = scanner.nextLine();    // nextLine() 就会去掉换行符
            String engWord = request;
            Log.println("英文: " + engWord);

            // 翻译
            String chiWord = translate(engWord);
            Log.println("中文: " + chiWord);

            // 发送响应
            String response = chiWord;  // TODO: 响应的单词中是没有 \r\n

            OutputStream outputStream = socket.getOutputStream();
            OutputStreamWriter outputStreamWriter = new OutputStreamWriter(outputStream, "UTF-8");
            PrintWriter writer = new PrintWriter(outputStreamWriter);

            Log.println("准备发送");
            writer.printf("%s\r\n", response);

            writer.flush();
            Log.println("发送成功");

            // 挂掉电话
            socket.close();
            Log.println("挂断电话");
        }
//        serverSocket.close();
    }


    private static final HashMap<String, String> map = new HashMap<>();

    private static void initMap() {
        map.put("apple", "苹果");
        map.put("pear", "梨");
        map.put("orange", "橙子");
    }

    private static String translate(String engWord) {
        String chiWord = map.getOrDefault(engWord, "查无此单词");
        return chiWord;
    }
}

TCP客户端:

public class UserInputLoopShortConnectionClient {
    public static void main(String[] args) throws Exception {
        Scanner userInputScanner = new Scanner(System.in);


        while (true) {
            // 这里做了一个假设:1)用户肯定有输入  2)用户一行一定只输入一个单词(没有空格)
            System.out.print("请输入英文单词: ");
            if (!userInputScanner.hasNextLine()) {
                break;
            }
            String engWord = userInputScanner.nextLine();

            // 直接创建 Socket,使用服务器 IP + PORT
            Log.println("准备创建 socket(TCP 连接)");
            Socket socket = new Socket("127.0.0.1", TranslateServerShortConnection.PORT);
            Log.println("socket(TCP 连接) 创建成功");

            // 发送请求
            Log.println("英文: " + engWord);
            String request = engWord + "\r\n";

            OutputStream os = socket.getOutputStream();
            OutputStreamWriter osWriter = new OutputStreamWriter(os, "UTF-8");
            PrintWriter writer = new PrintWriter(osWriter);

            Log.println("发送请求中");
            writer.print(request);
            writer.flush();
            Log.println("请求发送成功");

            // 等待接受响应
            InputStream is = socket.getInputStream();
            Scanner socketScanner = new Scanner(is, "UTF-8");
            // 由于我们的响应一定是一行,所以使用 nextLine() 进行读取即可
            // nextLine() 返回的数据中,会自动把 \r\n 去掉
            // TODO: 没有做 hasNextLine() 的判断
            Log.println("准备读取响应");
            String chiWord = socketScanner.nextLine();
            Log.println("中文: " + chiWord);

            socket.close();
        }
    }
}

五、 关于输入流和输出流的使用

5.1 关于输入流的使用:

  1. 如果直接进行二进制读取
byte[] buf = new byte[1024];
int n = inputStream.read(buf);
  1. 如果读取文本数据,建议直接使用Scanner封装InputStream后再使用
Scanner S = new Scanner(inputStream,"UTF-8");
s.nextLine() ... s.hasNextLine()

5.2 关于输出流的使用:

  1. 如果直接进行二进制输出outputStream.write(buf,offset,length)
  2. 如果是文本输出,建议OutputStream -> OutputStreamWriter -> PrintWriter
OutputStreamWriter osWriter = new OutputStreamWriter(outputStream,"UTF-8");
PrintWriter writer = new PrintWriter(osWriter);
writer.println(...);
writer.print(...);
writer.printf(format,...);

重要:不要忘记刷新缓冲区,否则数据可能无法到达对方!!!

outputStream.flush();
//writer.flush();

六、面向数据报文VS面向字节流

举个栗子:

在这里插入图片描述在这里插入图片描述

总结

关于端口被占用的问题:

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

在这里插入图片描述
此时需要检查进程B绑定的是哪个端口,再查看该端口被哪个进程占用。以下为通过端口号查进程的方式:

  • 在cmd输入 netstat -ano | findstr 端口号 ,则可以显示对应进程的pid。如以下命令显
    示了8888进程的pid
    在这里插入图片描述

  • 在任务管理器中,通过pid查找进程
    在这里插入图片描述
    解决端口被占用的问题

  1. 如果进程没啥用,就可以把进程杀掉
  2. 如果进程不确定,可以换个端口使用

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

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

相关文章

Shell函数

1、 函数定义 格式一&#xff1a; function name() { Command sequence; } 格式二&#xff1a; name() { Command sequence); } 1、()内是没有参数的&#xff0c;他只是函数定义的固定格式。 2、第八行fun 是函数的调用(第一种方式) 2、 函数传参 1、在Shell中&#xff0c;调用…

Jina 实例秀|基于CLIP模型的跨模态视频搜索

不同于传统的关键词搜索&#xff0c;你不需要给每个视频素材人为地打上标签。使用开源产品 CLIP-as-service&#xff0c;输入画面的描述文本&#xff0c;直接搜索到对应的视频片段。CLIP 是一个强大的模型&#xff0c;能够很好地判别文本和图片是否相关&#xff0c;但将其集成到…

【LeetCode】验证二叉搜索树 [M]

98. 验证二叉搜索树 - 力扣&#xff08;LeetCode&#xff09; 一、题目 给你一个二叉树的根节点 root &#xff0c;判断其是否是一个有效的二叉搜索树。 有效 二叉搜索树定义如下&#xff1a; 节点的左子树只包含 小于 当前节点的数。节点的右子树只包含 大于 当前节点的数。…

微信小程序框架02

目录 1.框架简介 2.视图层 View 2.2 WXML 2.3 WXSS 2.4 JS 3.事件 4.逻辑层 APP service 4.1 生命周期 4.2 页面路由 4.3模块化 1.框架简介 小程序开发框架的目标是通过尽可能简单、高效的方式让开发者可以在微信中开发具有原生 APP 体验的服务。 整个小程序框架系统分为两部…

山东大学机器学习课程资源索引

实验 完整实验代码获取 github repo 【ML实验4】多分类贝叶斯模型 【ML实验5】SVM&#xff08;手写数字识别、核方法&#xff09; 【ML实验6】K-means&#xff08;图像压缩&#xff09; 【ML实验7】人脸识别综合项目&#xff08;PCA、多分类SVM&#xff09; 一个PCA加速技巧 …

数据治理:企业数据治理蓝图

参考《一本书讲透数据治理》、《数据治理》等 文章目录企业数据治理体系企业数据治理9个要素企业数据治理4个层面企业数据治理之道企业数据治理之法企业数据治理之术企业数据治理之器企业数据治理体系 数据治理、数据管理、数据管控三者是什么关系&#xff1f;很多人都搞混&am…

【Spring(二)】IoC入门案例(XML版)

文章目录前言1.IoC入门案例总结前言 上篇文章我们讲了IOC和DI两个核心概念&#xff0c;本篇文章我们会在Spring的环境下来实现它们&#x1f4aa;&#x1f4aa;。 1.IoC入门案例 我们先来实现IoC也就是管理Bean的这套模式&#xff0c;我们先来说说这套程序应该怎么做&#xff0c…

深度学习Week12-训练自己的数据集(YOLOv5)

这周接着详细解析小白YOLOv5全流程-训练实现数字识别_牛大了2022的博客-CSDN博客_yolov5识别数字&#xff0c;上周入门教大家下载配置环境&#xff0c;如果没有的话请参考上周的文章深度学习Week11-调用官方权重进行检测&#xff08;YOLOv5&#xff09;_牛大了2022的博客-CSDN博…

《IDC MarketScape:2022全球通用计算机视觉厂商评估》出炉,腾讯云智能入选

近日&#xff0c;全球领先的IT市场研究和咨询公司IDC发布了2022年度《MarketScape&#xff1a;全球通用计算机视觉厂商评估》报告&#xff08;以下简称“报告”&#xff09;&#xff0c;腾讯云智能凭借在计算机视觉领域领先的技术积累、出色的产品能力和丰富的行业落地实践&…

案例分享:硬件敏捷

“没有人能够在硬件领域推动以两周为单位的循环迭代&#xff01;”当人们谈起敏捷方法在包含了硬件及软件产品开发时&#xff0c;第一反应都是类似的论调。然而&#xff0c;已经有一些团队&#xff0c;尝试将已有的可靠硬件开发理念与少量从敏捷软件中借鉴的新鲜思想结合&#…

系统日志- Journal and Rsyslog

Log文件 Rsyslog 的配置文件/etc/rsyslog.conf Rsyslog的旧的信息会在最前面&#xff0c;新的信息会在最下面。 tail -f /var/log/messages #可以动态监控日志信息logger 发送日志信息 logger -p user.notice #“内容” -p选项测试实验&#xff1a; 1.在/etc/rsyslog.d/文…

基于ThinkPHP框架开发的全套家政服务小程序源码(带调试视频)

家政服务小程序源码 在线派单 商家入驻 多城市带积分开源小程序 了解更多内容可私信我。 1、独立版 ThinkPHP框架 后端全开源&#xff1b; 2. 开发语言&#xff1a;PHP&#xff1b; 3. 数据库&#xff1a;MySQL&#xff1b; 4.小程序端&#xff1a;Uniapp&#xff1b; 5.…

Vuex基础概念用法(新手入门)

一.Vuex概念及解释 定义: vue全局状态管理器。有了Vuex在任意组件/页面都可以访问vuex数据&#xff0c;当数据更新的时候&#xff0c;引用vuex的组件视图会自动更新。也就是说Vuex实现数据全局共享&#xff0c;响应式更新。 1.state&#xff08;存放状态&#xff09; $store…

《flask》flask+mqtt联动快速上手

简介 本文旨在介绍如何快速上手联动flask mqtt&#xff0c;本文将会给出一个简单的demo&#xff0c;用于演示在如何通过访问flask接口来触发mqtt&#xff0c;并在flask运行的基础的上对mqtt进行订阅。 快速上手 因为有项目需求&#xff0c;所以需要flask mqtt进行联动&…

Docker网络中篇-docker网络的四种类型

通过上一篇学习,我们对docker网络有了初步的了解。本篇,咱们就来实战docker网络。 docker网络实战 实战docker网络,我们将从以下几个案例来讲解 1:birdge是什么? 2:host 3:none 4:container 实战网络类型如下: 在docker中,网络的配置是以json格式存在的,下面…

知识变现创业者必读——《知识变现实操手册》

现在越来越多人&#xff0c;正在跑步进入知识变现创业这个赛道。 为什么进入这个赛道&#xff0c;因为能赚钱钱啊&#xff0c;大部分人是受到了知识变现大咖们日入万元&#xff0c;月入十万&#xff0c;这些赚钱效益的刺激&#xff0c;匆忙进入的。 我问一句&#xff0c;你知识…

网络路由技术和协议

网络路由是网络通信的重要组成部分。路由可帮助您的网络组件从可用选项中选择最佳网络路径。这使得网络通信高效可靠。启用此功能的硬件组件称为路由器。监控和管理路由器是网络管理员日常工作中不可或缺的一部分。由于路由器可以决定网络连接和可用性的成败&#xff0c;因此了…

MATLAB-plot绘图函数

plot函数是MATLAB中最核心的二维绘图函数&#xff0c;它有多种语法格式可以实现多种功能。 plot函数的基本调用格式如下。 plot(y) 当y为向量时&#xff0c;是以y的分量为纵坐标、元素序号为横坐标&#xff0c;用直线依次连接数据点&#xff0c; 绘制曲线。若y为实矩阵&#…

【技术分享】如何实现功能完备性能优异的RTMP、RTSP播放器?

技术背景 这几年&#xff0c;我们对接了太多有RTSP或RTMP直播播放器诉求的开发者&#xff0c;他们当中除了寻求完整的解决方案的&#xff0c;还有些是技术探讨&#xff0c;希望能借鉴我们播放端的开发思路或功能特性&#xff0c;完善自己的产品。 忙里偷闲&#xff0c;今天我…

【GO】K8s 管理系统项目[API部分--Pv]

K8s 管理系统项目[API部分–Pv] 1. 接口实现 service/dataselector.go type pvCell corev1.PersistentVolumefunc(p pvCell) GetCreation() time.Time {return p.CreationTimestamp.Time }func(p pvCell) GetName() string {return p.Name }2. Pv功能 service/pv.go 2.1 获…