Linux网络编程——socket 通信基础

news2024/10/1 23:46:28

Linux网络编程——socket 通信基础

    • 1. socket 介绍
    • 2. 字节序
      • 2.1 简介
      • 2.2 字节序举例
      • 2.3 字节序转换函数
    • 3. socket 地址
      • 3.1 通用 socket 地址
      • 3.2 专用 socket 地址
    • 4. IP地址转换(字符串ip -> 整数,主机、网络字节序的转换 )
    • 5. TCP 通信流程
    • 6. 套接字函数

1. socket 介绍

    所谓 socket套接字),就是对网络中不同主机上的应用进程之间进行 双向通信的 端点的抽象。一个套接字就是网络上进程通信的一端,提供了 应用层进程 利用网络协议交换数据的机制。从所处的地位来讲,套接字 上联 应用进程下联 网络协议栈,是 应用程序 通过 网络协议 进行通信的接口,是 应用程序 与 网络协议根 进行交互的接口。

    socket 可以看成是两个网络应用程序进行通信时,各自通信连接中的端点,这是一个 逻辑上的概念。它是网络环境中 进程间通信API,也是可以被命名寻址通信端点,使用中的每一个套接字都有其类型和一个与之相连进程。通信时其中一个网络应用程序将要传输的一段信息写入它所在主机的 socket 中,该 socket 通过与 网络接口卡NIC)相连的传输介质将这段信息送到另外一台主机socket 中,使对方能够接收到这段信息。socket 是由 IP 地址端口 结合的,提供向应用层进程传送数据包的机制

    socket 本身有“ 插座 ”的意思,在 Linux 环境下,用于表示 进程间网络通信特殊文件类型本质为 内核 借助 缓冲区 形成的 伪文件。既然是文件,那么理所当然的,我们可以使用 文件描述符 引用套接字。与管道类似的,Linux 系统将其封装成文件的目的是为了 统一接口,使得 读写套接字读写文件 的操作一致。区别是 管道 主要应用于 本地进程间通信,而 套接字 多应用于 网络进程间数据的传递

在这里插入图片描述

使用 文件描述符 fd 引用 socket
在这里插入图片描述

套接字通信 分两部分:

  • 服务器端被动 接受连接,一般不会主动发起连接
  • 客户端主动 向服务器发起连接

socket 是一套 通信的接口LinuxWindows 都有,但是有一些细微的差别

2. 字节序

2.1 简介

    现代 CPU 的累加器 一次都能 装载(至少)4 字节(这里考虑 32 位机),即一个整数。那么这 4 字节 在 内存 中排列的顺序 将影响它被累加器装载成的整数的值,这就是字节序问题。在各种计算机体系结构中,对于字节等的存储机制有所不同,因而引发了计算机通信领域中一个很重要的问题,即通信双方交流的信息单元比特字节双字 等等)应该以什么样的顺序进行传送。如果不达成一致的规则,通信双方将无法进行正确的编码/译码从而导致通信失败。

    字节序,顾名思义 字节的顺序,就是大于一个字节类型的数据在内存中的存放顺序 (一个字节的数据当然就无需谈顺序的问题了)。

    字节序 分为 小端字节序Big-Endian) 和 大端字节序Little-Endian)。

  • 小端字节序则 是指 整数的 低位字节 则存储在内存的 地址 处,而 高位字节 存储在内存的 地址 处。
  • 大端字节序 是指一个 整数的 最高位字节23 ~ 31 bit)存储在内存的 地址 处,低位字节0 ~ 7 bit)存储在 内存的 地址 处;

2.2 字节序举例

小端字节序

  • 0x 01 02 03 04 (十六进制,四字节ff = 255)

  • 内存的方向 ----->

  • 内存的低位 -----> 内存的高位
    04 03 02 01

0x 11 22 33 44 12 34 56 78八个字节
在这里插入图片描述

大端字节序

  • 0x 01 02 03 04
  • 内存的方向 ----->
  • 内存的低位 -----> 内存的高位
    01 02 03 04

0x 12 34 56 78 11 22 33 44
在这里插入图片描述

  • 通过代码检测当前主机的字节序
#include <stdio.h>

int main() {

    union {
        short value;    // 2字节
        char bytes[sizeof(short)];  // char[2]
    } test;

    test.value = 0x0102;
    if((test.bytes[0] == 1) && (test.bytes[1] == 2)) {
        printf("大端字节序\n");
    } else if((test.bytes[0] == 2) && (test.bytes[1] == 1)) {
        printf("小端字节序\n");
    } else {
        printf("未知\n");
    }

    return 0;
}

在这里插入图片描述

2.3 字节序转换函数

当格式化的数据在两台使用不同字节序的主机之间直接传递时,接收端必然错误的解释之。

  • 解决问题的方法是:发送端 总是 把要发送的数据 转换成 大端字节序数据 后再发送,而接收端知道对方传送过来的数据总是采用大端字节序,所以接收端可以根据自身采用的字节序决定是否对接收到的数据进行转换(小端机转换,大端机不转换)。

    网络字节顺序TCP/IP规定好的 一种数据表示格式,它与具体的 CPU 类型、操作系统等无关,从而可以保证数据在不同主机之间传输时能够被正确解释,网络字节顺序 采用 大端排序 方式。


BSD Socket 提供了封装好的 转换接口,方便程序员使用。

  • 包括从 主机字节序网络字节序转换函数htonshtonl
  • 网络字节序主机字节序 的转换函数:ntohsntohl
h 	- host 主机,主机字节序
to 	- 转换成什么
n 	- network 网络字节序
s 	- short : unsigned short   无符号短整型,两个字节
l 	- long : unsigned int		 无符号长整型,四个字节
#include <arpa/inet.h>
/*
	  网络通信时,需要将主机字节序转换成网络字节序(大端),
	  另外一段获取到数据以后根据情况将网络字节序转换成主机字节序。
*/

// 32位,转IP
uint32_t htonl(uint32_t hostlong);    	// 主机字节序 -> 网络字节序
uint32_t ntohl(uint32_t netlong); 		// 网络字节序 -> 主机字节序

// 16位,转换端口
uint16_t htons(uint16_t hostshort); 	// 主机字节序 -> 网络字节序
uint16_t ntohs(uint16_t netshort);		// 网络字节序 -> 主机字节序
#include <stdio.h>
#include <arpa/inet.h>

int main() {

    // htonl  转换IP
    char buf[4] = {192, 168, 1, 100};
    int num = *(int *)buf;
    int sum = htonl(num);
    unsigned char *p = (char *)&sum;

    printf("%d %d %d %d\n", *p, *(p+1), *(p+2), *(p+3));

    printf("=======================\n");

    // htons 转换端口
    unsigned short a = 0x0102;
    printf("a : %x\n", a);
    unsigned short b = htons(a);
    printf("b : %x\n", b);

    printf("=======================\n");

    // ntohl 转换IP
    unsigned char buf1[4] = {1, 1, 168, 192};
    int num1 = *(int *)buf1;
    int sum1 = ntohl(num1);
    unsigned char *p1 = (unsigned char *)&sum1;
    printf("%d %d %d %d\n", *p1, *(p1+1), *(p1+2), *(p1+3));
    
    // ntohs 转换端口


    return 0;
}

3. socket 地址


    socket 地址 其实是一个 结构体,封装 端口号IP 等信息。 后面的 socket 相关的 api 中需要使用到这个 socket地址

  • 客户端 -> 服务器IP, Port

3.1 通用 socket 地址


    socket 网络编程接口 中表示 socket 地址 的是 结构体 sockaddr,其定义如下:

#include <bits/socket.h>

struct sockaddr {
	sa_family_t sa_family;	// 地址族类型
	char sa_data[14]; 		// 14字节
};

typedef unsigned short int sa_family_t;		// 2字节

    sa_family 成员是 地址族类型sa_family_t)的 变量地址族类型 通常与 协议族类型 对应。常见的 协议族protocol family,也称 domain)和对应的 地址族address family) 如下所示:
在这里插入图片描述
     PF_*AF_* 都定义在 bits/socket.h 头文件中,后者前者完全相同的值,所以二者通常混用

    sa_data 成员用于存放 socket 地址值。但是,不同的协议族的地址值具有不同的含义和长度,如下所示:
在这里插入图片描述

     由上表可知,14 字节sa_data 根本无法容纳多数协议族的地址值。因此,Linux 定义了下面这个 新的通用的 socket 地址结构体,这个结构体不仅提供了足够大的空间用于存放地址值,而且是 内存对齐 的。

#include <bits/socket.h>
struct sockaddr_storage
{
	sa_family_t sa_family;			// 地址族类型
	unsigned long int __ss_align;	// 内存对齐
	char __ss_padding[ 128 - sizeof(__ss_align) ];
};

typedef unsigned short int sa_family_t;

3.2 专用 socket 地址

     很 多网络编程函数 诞生 早于 IPv4 协议,那时候都使用的是 struct sockaddr 结构体,为了向前兼容,现在 sockaddr 退化成了(void *)的作用,传递一个地址 给函数,至于这个函数是 sockaddr_in 还是 sockaddr_in6,由地址族确定,然后函数内部再 强制类型转化 为所需的地址类型。
在这里插入图片描述
    UNIX 本地域协议族 使用如下 专用的 socket 地址结构体

#include <sys/un.h>

struct sockaddr_un
{
	sa_family_t sin_family;
	char sun_path[108];
};

    TCP/IP 协议族sockaddr_insockaddr_in6 两个专用的 socket 地址结构体,它们分别用于 IPv4IPv6

#include <netinet/in.h>

struct sockaddr_in
{
	sa_family_t sin_family; 		/* __SOCKADDR_COMMON(sin_) 地址族类型*/
	in_port_t sin_port; 			/* Port number. */
	struct in_addr sin_addr; 		/* Internet address. */
	/* Pad to size of `struct sockaddr'.  填充*/
	unsigned char sin_zero[sizeof (struct sockaddr) - __SOCKADDR_COMMON_SIZE - sizeof (in_port_t) - sizeof (struct in_addr)];
};

struct in_addr
{
	in_addr_t s_addr;
};

struct sockaddr_in6
{
	sa_family_t sin6_family;
	in_port_t sin6_port; /* Transport layer port # */
	uint32_t sin6_flowinfo; /* IPv6 flow information */
	struct in6_addr sin6_addr; /* IPv6 address */
	uint32_t sin6_scope_id; /* IPv6 scope-id */
};

typedef unsigned short uint16_t;
typedef unsigned int uint32_t;
typedef uint16_t in_port_t;
typedef uint32_t in_addr_t;

#define __SOCKADDR_COMMON_SIZE (sizeof (unsigned short int))

    所有 专用 socket 地址(以及 sockaddr_storage)类型的变量 在实际使用时都需要转化通用 socket 地址 类型 sockaddr强制转化 即可),因为所有 socket 编程接口 使用的地址参数类型都是 sockaddr

4. IP地址转换(字符串ip -> 整数,主机、网络字节序的转换 )

    通常,人们习惯用 可读性好的字符串 来表示 IP 地址,比如用 点分十进制字符串 表示 IPv4 地址,以及用 十六进制字符串 表示 IPv6 地址。但编程中我们需要先把它们 转化为 整数(二进制数)方能使用。而记录日志时则相反,我们要把整数表示的 IP 地址 转化为 可读的字符串。下面 3 个函数可用于用 点分十进制字符串 表示的 IPv4 地址 和 用 网络字节序整数 表示的 IPv4 地址 之间的转换:

#include <arpa/inet.h>
 
// 下面的这些函数比较久,使用起来比较麻烦,不推荐使用 
in_addr_t inet_addr(const char *cp); 	// 点分十进制字符串 -> 整数
int inet_aton(const char *cp, struct in_addr *inp);	// 点分十进制字符串 -> 整数, 并保存到结构体指针 inp 中
char *inet_ntoa(struct in_addr in); 	// 整数 -> 点分十进制字符串

    下面这对更新的函数也能完成前面 3 个函数同样的功能,并且它们同时适用 IPv4 地址IPv6 地址:⭐️

#include <arpa/inet.h>

// p:点分十进制的IP字符串,n:表示network,网络字节序的整数
int inet_pton(int af, const char *src, void *dst);
	af:地址族: AF_INET AF_INET6
	src:需要转换的点分十进制的IP字符串
	dst:转换后的结果保存在这个里面
	
// 将网络字节序的整数,转换成点分十进制的IP地址字符串
const char *inet_ntop(int af, const void *src, char *dst, socklen_t size);
	af:地址族: AF_INET AF_INET6
	src: 要转换的ip的整数的地址
	dst: 转换成IP地址字符串保存的地方
	size:第三个参数的大小(数组的大小)
	返回值:返回转换后的数据的地址(字符串),和 dst 是一样的
#include <stdio.h>
#include <arpa/inet.h>

int main() {

    // 创建一个ip字符串,点分十进制的IP地址字符串
    char buf[] = "192.168.1.4"; // 后面默认还有一个字符串结束符
    unsigned int num = 0;

    // 将点分十进制的IP字符串转换成网络字节序的整数
    inet_pton(AF_INET, buf, &num);
    unsigned char * p = (unsigned char *)&num;
    printf("%d %d %d %d\n", *p, *(p+1), *(p+2), *(p+3)); // 大端排序


    // 将网络字节序的IP整数转换成点分十进制的IP字符串
    char ip[16] = "";
    const char * str =  inet_ntop(AF_INET, &num, ip, 16);
    printf("str : %s\n", str);
    printf("ip : %s\n", str);
    printf("%d\n", ip == str);

    return 0;
}

在这里插入图片描述

5. TCP 通信流程

TCPUDP -> 传输层的协议

  • UDP: 用户数据报协议,面向无连接,可以单播,多播,广播, 面向 数据报,不可靠
  • TCP: 传输控制协议,面向连接的,可靠的,基于 字节流仅支持单播传输
UDPTCP
是否创建连接无连接面向连接
是否可靠不可靠可靠的
连接的对象个数一对一、一对多、多对一、多对多支持一对一
传输的方式面向数据报面向字节流
首部开销8个字节最少20个字节
适用场景实时应用(视频会议,直播)可靠性高的应用(文件传输)

在这里插入图片描述

TCP 通信的流程 ⭐️⭐️⭐️

⭐️服务器端被动接受连接的角色)⭐️

  1. 创建 一个用于监听的套接字
    • 监听:监听有客户端的连接
    • 套接字 :这个套接字其实就是一个 文件描述符
  2. 将这个 监听 文件描述符本地的IP端口 绑定IP端口 就是 服务器的地址信息
    • 客户端 连接 服务器 的时候使用的就是这个 IP端口
  3. 设置 监听,监听的 fd 开始工作
  4. 阻塞等待,当有客户端发起连接,解除阻塞,接受客户端的连接,会得到一个 和客户端通信的 套接字fd
  5. 通信
    • 接收数据
    • 发送数据
  6. 通信结束断开连接

⭐️客户端⭐️

  1. 创建一个用于通信的套接字fd
  2. 连接服务器,需要指定连接的服务器IP端口
  3. 连接成功了,客户端可以直接和服务器 通信
    • 接收数据
    • 发送数据
  4. 通信结束断开连接

6. 套接字函数

#include <sys/types.h>
#include <sys/socket.h>
#include <arpa/inet.h> // 包含了这个头文件,上面两个就可以省略

int socket(int domain, int type, int protocol);
	- 功能:创建一个套接字
	- 参数:
		- domain: 协议族
			AF_INET : ipv4
			AF_INET6 : ipv6
			AF_UNIX, AF_LOCAL : 本地套接字通信(进程间通信)
		- type: 通信过程中使用的协议类型
			SOCK_STREAM : 流式协议
			SOCK_DGRAM : 报式协议
		- protocol : 具体的一个协议。一般写0(默认)
			- SOCK_STREAM : 流式协议默认使用 TCP
			- SOCK_DGRAM : 报式协议默认使用 UDP
		- 返回值:
			- 成功:返回文件描述符,操作的就是内核缓冲区。
			- 失败:-1

int bind(int sockfd, const struct sockaddr *addr, socklen_t addrlen); // socket命名
	- 功能:绑定,将fd 和本地的IP + 端口进行绑定
	- 参数:
		- sockfd : 通过socket函数得到的文件描述符
		- addr : 需要绑定的socket地址,这个地址封装了ip和端口号的信息
		- addrlen : 第二个参数结构体占的内存大小

int listen(int sockfd, int backlog);  /proc/sys/net/core/somaxconn
	- 功能:监听这个socket上的连接
	- 参数:
		- sockfd : 通过socket()函数得到的文件描述符
		- backlog : 未连接的队列 和 已经连接的队列 和的最大值(以使用 cat /proc/sys/net/core/somaxconn 查看:4096),一般不用设置那么大,如:128

int accept(int sockfd, struct sockaddr *addr, socklen_t *addrlen);
	- 功能:接收客户端连接,默认是一个阻塞的函数,阻塞等待客户端连接
	- 参数:
		- sockfd : 用于监听的文件描述符
		- addr : 传出参数,记录了连接成功后客户端的地址信息(ip,port)
		- addrlen : 指定第二个参数的对应的内存大小
	- 返回值:
		- 成功 :用于通信的文件描述符
		- -1 : 失败

int connect(int sockfd, const struct sockaddr *addr, socklen_t addrlen);
	- 功能: 客户端连接服务器
	- 参数:
		- sockfd : 用于通信的文件描述符
		- addr : 客户端要连接的服务器的地址信息
		- addrlen : 第二个参数的内存大小
	- 返回值:成功 0, 失败 -1

ssize_t write(int fd, const void *buf, size_t count); // 写数据
ssize_t read(int fd, void *buf, size_t count); // 读数据

TCP 通信实现

(1)服务器端

    创建 server.c 文件

// TCP 通信的服务器端

#include <stdio.h>
#include <arpa/inet.h>
#include <unistd.h>
#include <string.h>
#include <stdlib.h>

int main() {

    // 1.创建socket(用于监听的套接字)
    int lfd = socket(AF_INET, SOCK_STREAM, 0);

    if(lfd == -1) {
        perror("socket");
        exit(-1);
    }

    // 2.绑定
    struct sockaddr_in saddr;
    saddr.sin_family = AF_INET;
    // inet_pton(AF_INET, "192.168.193.128", saddr.sin_addr.s_addr);
    saddr.sin_addr.s_addr = INADDR_ANY;  // 0.0.0.0
    saddr.sin_port = htons(9999);
    int ret = bind(lfd, (struct sockaddr *)&saddr, sizeof(saddr));

    if(ret == -1) {
        perror("bind");
        exit(-1);
    }

    // 3.监听
    ret = listen(lfd, 8);
    if(ret == -1) {
        perror("listen");
        exit(-1);
    }

    // 4.接收客户端连接
    struct sockaddr_in clientaddr;
    int len = sizeof(clientaddr);
    int cfd = accept(lfd, (struct sockaddr *)&clientaddr, &len);
    
    if(cfd == -1) {
        perror("accept");
        exit(-1);
    }

    // 输出客户端的信息
    char clientIP[16];
    inet_ntop(AF_INET, &clientaddr.sin_addr.s_addr, clientIP, sizeof(clientIP));
    unsigned short clientPort = ntohs(clientaddr.sin_port);
    printf("client ip is %s, port is %d\n", clientIP, clientPort);

    // 5.通信
    char recvBuf[1024] = {0};
    while(1) {
        
        // 获取客户端的数据
        int num = read(cfd, recvBuf, sizeof(recvBuf));
        if(num == -1) {
            perror("read");
            exit(-1);
        } else if(num > 0) {
            printf("recv client data : %s\n", recvBuf);
        } else if(num == 0) {
            // 表示客户端断开连接
            printf("clinet closed...");
            break;
        }

        char * data = "hello,i am server";
        // 给客户端发送数据
        write(cfd, data, strlen(data));
    }
   
    // 关闭文件描述符
    close(cfd);
    close(lfd);

    return 0;
}

注: 仅供学习参考,如有不足,欢迎指正!

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

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

相关文章

Linux线程【控制】

目录 前言&#xff1a; 正文&#xff1a; 1、线程知识补充 1.1、线程私有资源 1.2线程的共享资源 1.3原生线程库 2. 线程控制接口 2.1线程创建 2.1.1一批线程 2.2线程等…

Spring学习笔记(六)利用Spring的jdbc实现学生管理系统的用户登录功能

一、案例分析 本案例要求学生在控制台输入用户名密码&#xff0c;如果用户账号密码正确则显示用户所属班级&#xff0c;如果登录失败则显示登录失败。 &#xff08;1&#xff09;为了存储学生信息&#xff0c;需要创建一个数据库。 &#xff08;2&#xff09;为了程序连接数…

python 基础知识点(蓝桥杯python科目个人复习计划56)

今日复习内容&#xff1a;做题 例题1&#xff1a;最小的或运算 问题描述&#xff1a;给定整数a,b&#xff0c;求最小的整数x&#xff0c;满足a|x b|x&#xff0c;其中|表示或运算。 输入格式&#xff1a; 第一行包括两个正整数a&#xff0c;b&#xff1b; 输出格式&#…

VBA数据库解决方案第九讲:打开数据库记录集,所得数据回填

《VBA数据库解决方案》教程&#xff08;版权10090845&#xff09;是我推出的第二套教程&#xff0c;目前已经是第二版修订了。这套教程定位于中级&#xff0c;是学完字典后的另一个专题讲解。数据库是数据处理的利器&#xff0c;教程中详细介绍了利用ADO连接ACCDB和EXCEL的方法…

【Vue3】全局切换字体大小

VueUse 先安装VueUse <template><header><div class"left">left</div><div class"center">center</div><div class"right">right</div></header><div><button click"cha…

对象变更记录objectlog工具(持续跟新)

文章目录 前言演示代码参考仓库 前言 对于重要的一些数据&#xff0c;我们需要记录一条记录的所有版本变化过程&#xff0c;做到持续追踪&#xff0c;为后续问题追踪提供思路。 演示代码 下面我们通过一段代码演示代码&#xff0c;展示如何自动将枚举字段&#xff0c;主键关…

计算机网络-下一代互联网

1.IPV6概念&#xff1a; 下一代网络NGI&#xff0c;目前是第6版。IPV6分组有1个固定头部和n个扩展头部&#xff08;任选&#xff09;以及上层协议的负载组成。如下图&#xff1a; 2.IPV6固定头部&#xff1a;各字段解释含义。 版本0110&#xff0c;4位&#xff0c;代表IPV6通…

Unity TMP文字移动效果

前言 看见很多游戏有很特殊的波浪形文字效果&#xff0c;于是来尝试一下控制TMP文字顶点的方式达到类似效果。 原理 挂载tmp text&#xff0c;在里面随便放入非空格字符。 tmp text组件开放了textInfo接口&#xff0c;也就是GetComponent<TextMeshProUGUI>().textInfo…

使用R语言进行Logistic回归分析(2)

一、数据集描述&#xff0c;问题要求 下表是40位肺癌病人的生存资料&#xff0c;X1表示生活行为能力平分&#xff08;1到100&#xff09;&#xff0c;X2为病人的年龄&#xff08;年&#xff09;&#xff0c;X3由诊断到进入研究的时间&#xff08;月&#xff09;&#xff0c;X4…

VMwareWorkstation17.0虚拟机搭建WindowsXP虚拟机(完整安装步骤详细图文教程)

VMwareWorkstation17.0虚拟机搭建WindowsXP虚拟机&#xff08;完整安装步骤详细图文教程&#xff09; 一、Windows XP1.Windows XP简介2.Windows XP 的下载地址 二、配置 Windows XP 虚拟机运行环境1.新建虚拟机2.选择类型配置3.插入WinXP光盘映像文件(ISO)4.选择操作系统5.命名…

【MCAL】TC397+EB-tresos之CAN配置实战 - (CAN/CANFD)

本篇文章介绍了在TC397平台使用EB-tresos对CAN驱动模块进行配置的实战过程,不仅介绍了标准CAN的发送与接收&#xff0c;还介绍了CANFD的实现与调试以及扩展帧的使用。M_CAN是德国博世公司开发的IP&#xff0c;因为英飞凌的芯片完整的集成了这个IP&#xff0c;所以整体的配置都比…

【电路笔记】-RC网络-时间常数

时间常数 文章目录 时间常数1、概述2、RC 电路的时间常数3、示例14、示例25、RC瞬态放电曲线6、示例37、总结Tau τ \tau τ 是 RC 电路在阶跃变化输入条件下从一种稳态条件变为另一种稳态条件所需的时间常数。 1、概述 Tau,符号 τ \tau τ,是电气和电子计算中使用的希腊字…

C++_数据结构_数据的输入

作用 用于从键盘获取数据 关键字 cin >> 变量示例

Jenkins的Pipeline概念

文章目录 Pipeline什么是Jenkins Pipeline声明式和脚本式Pipeline语法为何使用PipelinePipeline概念PipelineNodeStageStep Pipeline语法概述声明式Pipeline脚本式Pipeline Pipeline示例 参考 Pipeline 什么是Jenkins Pipeline Jenkins Pipeline是一套插件&#xff0c;它支持…

YOLOv9有效改进|使用空间和通道重建卷积SCConv改进RepNCSPELAN4

专栏介绍&#xff1a;YOLOv9改进系列 | 包含深度学习最新创新&#xff0c;主力高效涨点&#xff01;&#xff01;&#xff01; 一、改进点介绍 SCConv是一种即插即用的空间和通道重建卷积。 RepNCSPELAN4是YOLOv9中的特征提取模块&#xff0c;类似YOLOv5和v8中的C2f与C3模块。 …

国内chatgpt写作软件,chatgpt国内使用

随着人工智能技术的不断发展&#xff0c;国内涌现出了一些基于ChatGPT模型的写作软件&#xff0c;这些软件不仅能够实现智能化的文章写作&#xff0c;还支持批量生成各种类型的文章。本文将深入探讨国内ChatGPT写作软件&#xff0c;以及它们在批量文章创作方面的应用与优势。 C…

STM32作为SPI slave与主机异步通信

背景 最近被测试提了个BUG&#xff0c;说某款产品在用户按下前面板的按键后&#xff0c;对应的按键灯没有亮起来。前面板跟主机是通过SPI口通信&#xff0c;前面板是从机&#xff0c;从机想要主动发送消息&#xff0c;需要通过GPIO中断来通知主机&#xff1a; 上图前面板是ST…

php源码 单色bmp图片取模工具 按任意方式取模 生成字节数组 自由编辑点阵

http://2.wjsou.com/BMP/index.html 想试试chatGPT4生成&#xff0c;还是要手工改 php 写一个网页界面上可以选择一张bmp图片&#xff0c;界面上就显示这张bmp图片&#xff0c; 点生成取模按钮&#xff0c;在图片下方会显示这张bmp图片的取模数据。 取模规则是按界面设置的&a…

蓝桥ACM培训-搜索

前言&#xff1a; 今老师讲了了dfs,虽然我自己之前也自学了一点点&#xff0c;但我还是感觉做题并不是很顺&#xff0c;尤其是今天最后一题&#xff0c;我调试了一下午都没过&#xff0c;还需要积累经验呀。 正文&#xff1a; Problem:A 白与黑-搜索&#xff1a; #include &l…

DbSchema导出HTML/PDF版表结构

一、连接数据库 登录成功默认显示当前用户的所有资源&#xff08;表、视图、序列、方法、触发器等&#xff09;&#xff0c;如果不操作将导出此用户的全部信息。 至此连接数据库完成 二、表结构导出 本次不想给用户全部导出&#xff0c;只给导出几张&#xff0c;选择需要…