Linux socket编程

目录

  • 基础概念
    • 端口和端口号
    • Socket(套接字)
    • UDP和TCP的概念
  • Socket编程实战
    • socket的类型
    • struct sockaddr
    • struct sockaddr_in
    • 网络字节序
    • socket API
    • TCP网络编程流程分析
    • UDP Demo样例
  • other
    • 概念补充
    • setsockopt函数
    • 心跳机制
    • 面向字节流与面向报文的理解
  • 参考

基础概念

端口和端口号

我们常说的端口有两种:物理端口和逻辑端口。物理端口指的是用于连接物理设备之间的接口,如集线器、交换机、路由器上用于连接其他网络设备的接口。逻辑端口指的是指逻辑意义上用于区分服务的端口,比如用于浏览网页服务的80端口,用于FTP服务的21端口等。网络编程场景下所指的端口是逻辑端口,本文所说端口如果没有特指都是指的的逻辑端口。

物理端口比较好理解,那么逻辑端口是怎么一回事呢?端口在操作系统中用于表示一种特定的服务,每个端口都有一个独一无二的端口号,不会出现重复端口号的情况。既然要提供服务,那么每个端口号之下一定是要绑定对应的进程的。但需要注意的是,一个端口下只能绑定一个进程,而一个进程可以同时绑定多个端口号,这就像一个银行的窗口柜台下只能有一个工作人员,而一个工作人员却可以同时在多个窗口下工作。可以参考下图理解端口号的概念:
端口号

其实,网络通信的本质就是进程间通信。但与本地通信不同的是,网络通信是两台计算机上的两个进程跨网络之间的通信。进程在本地通信时需要根据进程的pid进行辨别,那么进程在网络之间通信时,同样也需要根据端口号进行辨别。

之所以要有端口号这个东西,这是因为在网络环境中的计算机可以通过IP地址和MAC地址来被找到,但IP+MAC地址并不能表示一台计算机上的特定进程,如果想要访问到特定的进程就一定需要再对其细分,于是端口就成了一个很好的解决方案。

那么为什么不直接使用进程pid,却要额外引入一个端口号,增加通信的复杂程度呢?其实这个问题可以从多个角度来回答,下面先简单列举一些:

  1. 对于服务器而言,进程可以在任何时候启动、关闭或重新启动,因此进程的pid随时可能会发生变化。而如果用端口号,则每次让进程启动时绑定到端口号就可以做到固定服务绑定固定端口号的效果了。
  2. 进程pid 是一个操作系统级别的标识符,只在单机环境中有效。当涉及到不同操作系统之间的通信时,进程pid 就不再适用了。而端口号则可以很好的解决这种不同环境下进程表示的差异性问题。
  3. 端口号与进程pid之间的解耦,可以使得服务与进程之间独立开来,使得服务的配置操作更加灵活。

端口号是一个2字节的整数,范围是1至65535,0不使用。其中 [1, 1023] 是知名端口号,这些端口号一般固定分配给一些服务,是不会改变的,一般也不允许修改(Linux下需要root权限才能操作)。例如80端口表示HTTP服务、21端口表示FTP 文件传输服务、53端口表示DNS 域名解析服务、443端口表示HTTPS 加密的超文本传输服务等。而1024到65535表示动态端口号,是普通用户可以申请的使用的端口号。 如果一个可执行程序程序没有设置端口号,那么操作系统会在动态端口号这个范围内随机挑选一个没有被占用端口号可执行程序使用。

Socket(套接字)

socket 的原意是“插座”,我们把插头插到插座上就能从电网获得电力供应,相应的,应用程序通过socket就可以连接到因特网,进而就可以通过互联网与远程计算机进行数据传输了。也就是说socket 就是用来连接到因特网的工具,通过socket,一台计算机可以接收其他计算机的数据,也可以向其他计算机发送数据。

严格来说socket并不属于任何一个网络协议层,socket是一种介于应用层和传输层的一个抽象层,起到一个承上启下的作用。它为应用层提供了访问传输层网络协议的接口,在Linux中,socket API 封装了操作系统内核中的 TCP/IP 协议栈所提供的服务,使得开发者能够通过这些接口收发数据。

一般来说,IP和端口号是socket的基础属性,大多数场景下的socket操作都需要用到这两个值。

在这里插入图片描述
从Linux的角度来看,socket套接字是一种抽象的文件格式,和管道文件一样,它是一种伪文件,存在于内核的缓冲区中,大小不变,一直是0。
其中,套接字是全双工的通信方式,分别有读写缓冲区。全双工的概念如下:

单工通信只支持信号在一个方向上传输(正向或反向),任何时候不能改变信号的传输方向。
半双工通信允许信号在两个方向上传输,但某一时刻只允许信号在一个信道上单向传输。
全双工通信允许数据同时在两个方向上传输,即有两个信道,因此允许同时进行双向传输。

UDP和TCP的概念

TCP是一种传输层协议,是一种面向字节流的有连接可靠传输。UDP也是一种传输层协议,但它是一种面向数据报的无连接不可靠传输。

TCP的特点是:面向字节流、有连接、可靠传输
UDP的特点是:面向数据报、无连接、不可靠传输

之所以TCP是可靠的传输,是因为TCP比UDP多了很多安全检查和差错处理,而UDP只管发送数据报,所以UDP的丢包率理论上要比TCP的高。但并不是说UDP就是一无是处的,TCP在得到安全的同时,也导致了效率的下降。所以相比之下,TCP的可靠性好,丢包率低,但UDP的效率高。所以TCP主要被用于可靠性较高的一些场景,例如HTTP、HTTPS、FTP等;UDP主要被用于对可靠性要求不是那么高的场景,例如各种直播和语音通话等,偶尔丢一两帧数据并不影响整体的体验。

需要注意的是UDP虽然理论上丢包率要略高于TCP,但这并不是就说明UDP一定丢包率很高,一般来说UDP的丢包率只会略高于TCP,但并不会高很多。

Socket编程实战

socket的类型

套接字的主要类型有三种:

  1. 数据报套接字(SOCK_DGRAM):数据报格式套接字(Datagram Sockets)也叫无连接的套接字,在代码中使用 SOCK_DGRAM 表示。只管传输数据,不作数据校验,如果数据在传输中损坏,或者没有到达另一台计算机,是没有办法补救的。也就是说,数据错了就错了,无法重传。使用的是UDP协议。

  2. 流式套接字(SOCK_STREAM):流式套接字(Stream Sockets)也叫面向连接的套接字,在代码中使用 SOCK_STREAM 表示。SOCK_STREAM 是一种可靠的、双向的通信数据流,数据可以准确无误地到达另一台计算机,如果损坏或丢失,可以重新发送。

  3. 原始套接字(SOCK_RAW):一种不同于SOCK_STREAM、SOCK_DGRAM的套接字,它实现于系统核心。原始套接字允许直接发送和接收 IP 数据包,而无需任何特定于协议的传输层格式,而且可以读写内核没有处理过的 IP 数据包。

本文我们只关注数据包套接字和流式套接字。

struct sockaddr

在正式认识socke apit之前,需要先搞清struct sockaddrstruct sockaddr_in这两个结构体。

网络编程的时候,有各种各样不同的应用场景,理论上而言,我们应该给每种场景都设计一套编程接口,但由于Linux内核是由C语言编写的缘故,并没有多态这种语法,如果要实现所有场景的接口,就需要定义多种功能相似的函数。

所以为了实现接口的统一性,统一便用struct sockaddr来描述一个网络字段,其格式为:类型+地址,而其它的类型则固定首16位字节一定是地址类型,这样就能通过类型+地址的方式解析所有的结构,进而统一接口(只需要在传参时进行对应的类型转换),参考下图理解。
在这里插入图片描述
struct sockaddr的定义如下:

struct sockaddr {u_short	sa_family;char	sa_data[14];
};

struct sockaddr_in

struct sockaddr_in为IPv4的结构体,struct sockaddr_un结构体主要为同一台机器上的进程间进行高效通信,struct sockaddr_in6则表示IPv6的结构体。本文的主要以struct sockaddr_in的使用为主。

sockaddr_in中有4个成员:sin_family、sin_port、sin_addr、sin_zero,其结构体定义(在netinet/in.h中定义)如下:

struct sockaddr_in {short int sin_family;                /* Address family */unsigned short int sin_port;      	 /* Port number */struct in_addr sin_addr;             /* Internet address */unsigned char sin_zero[8];           /* Same size as struct sockaddr */
};
  • sin_family
    sin_family表示要使用的网络协议簇,协议簇的在“linux/socket.h”里有详细定义,常见的有如下几个:
  1. AF_UNIX (本机通信)
  2. AF_INET (TCP/IP & IPv4)
  3. AF_INET6 (TCP/IP & IPv6)

在当前IPv4的场景下,sin_family一般设为AF_INET。

  • sin_port
    sin_port就表示的是要使用的端口号。

  • sin_addr
    sin_addr是一个 struct in_addr 类型的成员,其定义如下:

    struct in_addr {unsigned long s_addr;
    };
    

    也就是说,其本质是一个unsigned long类型的整型数据。其中,可以用inet_addr函数将char*的ip地址转为4字节序列,并转为网络字节序。
    0.0.0.0表示任意IP地址绑定,也就是不限IP的意思,在编程时可以使用INADDR_ANY宏来表示,其定义为:

    #define    INADDR_ANY		((in_addr_t) 0x00000000)
    
  • sin_zero
    sin_zero是为了让sockaddr与sockaddr_in两个数据结构保持大小相同而保留的空字节,一般用不到。

网络字节序

由于不同的机器之间有大端和小端之间的差异,所以规定:所有到达网络的数据,必须是大端格式的。也就是说,网络字节序就是大端字节序。不知道大端和小段的可以参考:大端和小端

其中在编程时,对于网络字节序的操作有如下一些封装好的函数,用以简化我们的操作:

#include <arpa/inet.h>uint32_t htonl(uint32_t hostlong);	  // 本地字节序转成网络字节序 - 32位整型
uint16_t htons(uint16_t hostshort);	  // 本地字节序转成网络字节序 - 16位整型
uint32_t ntohl(uint32_t netlong);	  // 网络字节序转成本地字节序 - 32位整型
uint16_t ntohs(uint16_t netshort);	  // 网络字节序转成本地字节序 - 16位整型

其中,h表示host,n表示net,l表示长的32位整型,s表示短的16位整型。注意,避免对同一个数多次调用上述函数,因为这只些只是单纯的转换,并不会做大端小端的检查,如果调用两次可能就又转回去了。

除此之外还有inet_addrinet_aton函数用来将字符串类型的点分十进制IP地址(例如 “192.168.1.1”)转换成可以用于网络传输的32位整型数字,函数声明如下:

#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>int inet_aton(const char *cp, struct in_addr *inp);
in_addr_t inet_addr(const char *cp);

socket API

下面来介绍Linux下常用的套接字函数

  • socket

    #include <sys/types.h>        
    #include <sys/socket.h>int socket(int domain, int type, int protocol);
    

    用于创建一个套接字文件,并返回其文件描述符。

    domain参数:表示所选的协议簇,如下是几种常用的宏定义:

    定义含义
    PF_UNIX / PF_LOCAL本地通讯
    AF_INET / PF_INETIPv4 Internet协议
    PF_INET6IPv6 Internet协议

    一般IPv4的情况下使用AF_INET 就可以了
    type参数:表示套接字通信的类型,常见的几种type参数如下:

    定义含义
    SOCK_DGRAM数据报套接字,无连接的套接字,与UDP协议对应
    SOCK_STREAM流式套接字,有连接的套接字,与TCP协议对应

    protocol参数:用于指定协议的特定类型。如果为零,则表示自动选择一个。一般来说每种协议都只有一种类型,所以一般设为0即可。如下是一些参数示例:

    IPPROTO_TCP - tcp协议
    IPPROTO_UDP - udp协议


  • bind

    #include <sys/types.h>        
    #include <sys/socket.h>int bind(int sockfd, const struct sockaddr *addr, socklen_t addrlen);
    

    用于将addr绑定到指定套接字,主要起到i端口号绑定的作用,一般用于服务端套接字绑定的工作。如果没有调用这个bind函数,那么在socket通信时,会绑定到一个本机的随机空闲端口号。
    一般来说,bind函数只在服务端用,客户端不进行bind,这是因为服务器的服务需要有一个明确的固定端口号,这样才能够很好的被访问到,而客户端一般有各种各样的进程,手动指定端口号很容易就造成端口号冲突的问题,所以就不调用bind函数,让系统自动为我们选择一个空闲的端口号。

    sockfd参数:需要绑定的套接字文件描述符
    addr参数:sockaddr相关信息
    addrlen参数:addr参数的长度


  • sendto 和 send
    sendto:sendto函数一般用于UDP协议的数据传输工作。

    ssize_t sendto(int sockfd, const void *buf, size_t len, int flags,const struct sockaddr *dest_addr, socklen_t addrlen);
    

    sockfd:表示要通过哪个socket发送
    buf:表示要发送的内容
    len:表示要发送内容的大小
    flags:标志位,一般设为0即可
    dest_addr:表示dest_addr对应字段的字节数,要是为了应对多种 struct sockaddr_* 的情况。这个参数不能省略或者直接填0,必须是dest_addr的字节数。

    send:send函数主要用于TCP协议的数据传输工作。

    ssize_t send(int sockfd, const void *buf, size_t len, int flags);
    

    send与sendto的参数基本类似,send函数只是少了dest_addr和addrlen函数,这是因为TCP是面向连接的,一旦建立连接之后,就可以直接收发数据了,所以就不用再指定sockaddr 了。
    其中,当flags参数设为0时,send函数就等同于write函数:

    With a zero flags argument, send() is equivalent to write(2).

    注意,send函数和write函数在socket写入时,如果出错则会触发SIGPIPE信号。


  • recvfrom 和 recv
    recvfrom :recvfrom 函数一般用于UDP协议的数据传输工作。

    ssize_t recvfrom(int sockfd, void *buf, size_t len, int flags,struct sockaddr *src_addr, socklen_t *addrlen);
    

    其参数含义与上面的sendto一样,只是这里的src_addr表示的是要从哪里接受数据的参数,而不再是目标地的参数了。

    recv:recv函数主要用于TCP协议的数据传输工作。

    ssize_t recv(int sockfd, void *buf, size_t len, int flags);
    

    recv的参数含义与send的类似,而且当flags为0时,recv函数就等同于read函数了。


  • connect、listen、accept

这三个函数都是TCP协议特有的,所以就放在一起说了。
connect:通常由客户端调用,与服务器建立连接

int connect(int sockfd, const struct sockaddr *addr, socklen_t addrlen);

该函数的功能为主动建立连接(通过三次握手),而这个连接的过程是由内核完成的,不是这个函数完成的,这个函数的作用仅仅是通知 Linux 内核,让 Linux 内核自动完成 TCP 三次握手连接。通常情况下,客户端的 connect() 函数默认会一直阻塞,直到三次握手成功或超时失败才返回(正常的情况,这个过程很快完成)。


listen:设为监听状态,等待客户端的连接请求

int listen(int sockfd, int backlog);

listen函数的主要作用就是将套接字( sockfd )变成被动的连接监听套接字(被动的等待客户端的连接),至于参数 backlog 的作用是设置内核中连接对应的消息队列的长度,TCP 三次握手也不是由这个函数完成,listen()的作用仅仅告诉内核一些信息。

需要注意的是,listen函数不会阻塞,它主要做的事情为,将该套接字和套接字对应的连接队列长度告诉 Linux 内核,然后,listen函数就结束。这样的话,当有一个客户端主动连接(connect),Linux 内核就自动完成TCP 三次握手,将建立好的链接自动存储到队列中,如此重复。所以,只要 TCP 服务器调用了listen函数,客户端就可以通过connect函数和服务器建立连接,而这个连接的过程是由内核完成。


accept:等待连接并为其创建一个新的套接字

int accept(int sockfd, struct sockaddr *addr, socklen_t *addrlen);

accept函数功能是,从处于 established 状态的连接队列头部取出一个已经完成的连接,并为其创建一个新的套接字,这样我们就可以拿着这个套接字进行操作了。如果这个队列没有已经完成的连接,accept函数就会阻塞等待,直到取出队列中已完成的用户连接为止。

TCP网络编程流程分析

本文暂不提供tcp协议的demo样例,感兴趣的话可以参考:Linux下的socket演示程序,如下是tcp协议的 client-server 常规流程:

TCP流程图

UDP Demo样例

如下是基于udp协议的 client-server 的demo样例,为了便于查看,略去了相关安全检查的部分。流程图如下所示:
UDP网络编程流程图

  • server端
int main()
{uint16_t port = 8008;const int buf_len = 1024;// 创建套接字int sock = socket(AF_INET, SOCK_DGRAM, 0);// 设置套接字属性struct sockaddr_in addr;memset(&addr, 0, sizeof(addr));addr.sin_family = AF_INET;addr.sin_addr.s_addr = INADDR_ANY;addr.sin_port = htons(port);// bind操作bind(sock, (struct sockaddr*)&addr, sizeof(addr));// 服务器收发操作char buf[buf_len];while (true){struct sockaddr_in rcv_addr;socklen_t rcv_len = sizeof(rcv_addr);// 接收(等待)操作int rcv_size = recvfrom(sock, buf, buf_len - 1, 0, (struct sockaddr*)&rcv_addr, &rcv_len);buf[rcv_size] = '\0';// 发送(回复)操作const char *reply = "this is reply";sendto(sock, reply, strlen(reply), 0, (struct sockaddr*)&rcv_addr, rcv_len);}
}

  • client端
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <string.h>// 参数设置
const char* dst_ip = "127.0.0.1";
const int dst_port = 8008;
const int buf_len = 1024;// 关键函数:socket sendto recvfrom
int main()
{// 创建套接字int sock = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);// 设置套接字属性struct sockaddr_in send_sockaddr;memset(&send_sockaddr, 0, sizeof(send_sockaddr));send_sockaddr.sin_family = AF_INET;send_sockaddr.sin_addr.s_addr = inet_addr(dst_ip);send_sockaddr.sin_port = htons(dst_port);// 收发操作while (true){// 发送数据const char* msg = "a test msg.";sendto(sock, msg, strlen(msg), 0, (struct sockaddr*)&send_sockaddr, sizeof(send_sockaddr));// 接收数据char buf[buf_len];struct sockaddr_in recv_sockaddr;socklen_t len = sizeof(recv_sockaddr);recvfrom(sock, buf, buf_len - 1, 0, (struct sockaddr*)&recv_sockaddr, &len);}return 0;
}

other

概念补充

  1. 127.0.0.1是Linux的本地环回地址IP,相当于把数据自己转给自己,一般用于测试。
  2. 测试socket程序时,需要检查防火墙对应的端口号是否是开发状态,否则消息可能会被防火墙拦截。
  3. 0.0.0.0表示任意IP地址绑定,也就是不限IP的意思。
  4. socket是全双工的,可以同时读写,不会出现多线程的读写的问题。所以socket的多线程的使用是安全的。

setsockopt函数

setsockopt函数,用于设置套接字的属性,其函数定义如下:

#include <sys/types.h>
#include <sys/socket.h>int setsockopt(int sockFd, int level, int optname, const void *optval, socklen_t optlen);

sockfd参数:将要被设置或者获取选项的套接字。

level参数:选项定义的层次;支持SOL_SOCKET、IPPROTO_TCP、IPPROTO_IP和IPPROTO_IPV6。一般设成SOL_SOCKET以存取socket层。

> SOL_SOCKET				通用套接字选项.
> IPPROTO_IP				IP选项.IPv4套接口
> IPPROTO_TCP				TCP选项.
> IPPROTO_IPV6				IPv6套接口

optname参数: 设置的选项,有如下几种选项:

在这里插入图片描述
optval参数:对于setsockopt,指针,指向存放选项待设置的新值的缓冲区。获得或者是设置套接字选项.根据选项名称的数据类型进行转换。

optlen参数:optval缓冲区长度。

心跳机制

当服务器上的某个服务配置好了之后,或者建立连接止呕,那么该如何知道这个的服务未来在任何一个时刻,都是健康的呢?我们可以定期(例如30s)向对应的服务发送小请求,类似于ping服务,如果得到了回复,就说明我们的服务是正常的。这个机制,我们就称之为心跳机制,

面向字节流与面向报文的理解

可以将tcp和upd看成不同公司的出租车,tcp这个公司的出租车司机(tcp头)在拉客的时候,一看来了一个乘客,可是自己车上还有三个位置,司机就会继续等,直到自己车上去同一个目的地的乘客坐满了才开车,因为tcp公司认为遵循Nagle算法可以提高效率,节省能源,从socket学校走出来三个团体的学生,每一个团体只有一个人,可能只要消耗一个tcp出租车。如果从socket学校出来了一个团队的学生,但是这个团队有6个学生,一号tcp出租车看看自己车上还有两个个空位置,就让这个团队的两个学生上车了,剩下的学生只能做下一辆车了。这也就造成了一个问题,一号出租车开到了城市中的一个小餐馆,餐馆老板并不知道他们四个学生是不是一个团队的,这也就是粘包粘包的问题。

udp公司的出租车与tcp公司的出租车不一样,只要有一个团队的人走过来,不管是一个人还是7个人,udp出租车都可以一次性给你送走(当然下层的ip层还是可能会分包的,这些我们不用管),不需要等待。到餐馆后,餐馆老板一看是udp公司的出租车,就知道这是一个团队的(也就是不会出现粘包粘包的问题)。

  • 摘自:面向字节流与面向报文的通俗解释

参考

  1. socket编程入门 | C语言中文网
  2. 【计算机网络】端口详解
  3. socket 函数参数详解
  4. 网络编程:UDP网路编程
  5. TCP网络编程中connect()、listen()和accept()
  6. socket 网络编程——端口复用技术

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

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

相关文章

Linux update-alternatives 命令详解

1、查看所有候选项 sudo update-alternatives --list &#xff08;java筛选​​​​​​​sudo update-alternatives --list java&#xff09; 2、​​​​​​​更换候选项 sudo update-alternatives --config java 3、自动选择优先级最高的作为默认项 sudo update-alterna…

unity3d—demo(2d人物左右移动发射子弹)

目录 人物代码示例&#xff1a; 子弹代码示例&#xff1a; 总结上面代码&#xff1a; 注意点&#xff1a; 人物代码示例&#xff1a; using System.Collections; using System.Collections.Generic; using UnityEngine;public class PlayerTiao : MonoBehaviour {public f…

JSP技术发展现状

多年前&#xff0c;Java入门时学习的JSP可谓时风光无限&#xff0c;J2EE如日中天&#xff0c;短短数年&#xff0c;技术迭代更新光速般发展&#xff0c;有些技术慢慢就退出历史舞台。 JSP&#xff08;Java Server Pages&#xff09; 技术在早期 Java Web 开发中曾是构建动态网…

科技绽放-EtherCAT转Profinet网关智能连接项目

一、项目名称 备选名称及含义&#xff1a;开疆智能EtherCAT转Profinet网关智能连接项目&#xff1a;直接体现了从Profinet到EtherCAT的连接核心内容&#xff0c;智能连接突出了该项目的技术特点。工业互联方案强调了该项目在工业领域实现不同协议设备互联的目标&#xff0c;方案…

《计算机网络》(408大题)

2009 路由转发和静态路由的计算 子网划分、路由聚合的计算 注&#xff1a;CIDR中的子网号可以全为0或1&#xff0c;但是其主机号不允许。 注&#xff1a; 这里其实是把到互联网的路由当做了一个默认路由&#xff08;当一个目的网络地址与路由表中其他都不匹配时&#xff0c;…

matlab读取NetCDF文件

matlab对NetCDF文件进行信息获取和读取数据 文章目录 前言一、什么是NetCDF文件二、读取NetCDF文件数据 1.引入库 2.读入数据总结 前言 在气象学中&#xff0c;许多气象数据存储在NetCDF文件中&#xff0c;后缀为.nc&#xff0c;通常可以用NCL、python和MATLAB等对该…

MySQL用户管理、权限管理练习

1.使用root用户登录MySQL客户端&#xff0c;创建一个名为userl的用户&#xff0c;初始密码为123456; 创建一个名为user2的用户&#xff0c;无初始密码。然后&#xff0c;分别使用uesr1、user2登录MySQL 客户端。 2.使用root用户登录&#xff0c;将user2用户的密码修改为abcabc。…

SpringAOP手动模拟实现反射注解Jdk动态代理

代理 package com.xtq.aop.proxy;import com.xtq.annotations.After; import com.xtq.annotations.Before; import com.xtq.annotations.Pointcut;import java.lang.annotation.Annotation; import java.lang.reflect.InvocationHandler; import java.lang.reflect.Invocation…

算法-字符串-3.无重复字符的最长子串

一、思路&#xff1a; 无重复针对唯一的元素首选哈希表方法 在字符串中找出最长字串——动态数组 二、使用的一些常见方法 1.HashMap a.声明 HashMap<Character,Integer> mapnew HashMap(); b.添加元素 map.put(s.charAt(i),i); c.查询是否有对应的元素存在并获取 m…

Ubuntu中安装配置交叉编译工具并进行测试

01-下载获取交叉编译工具的源码 按照博文 https://blog.csdn.net/wenhao_ir/article/details/144325141的方法&#xff0c;把imx6ull的BSP下载好后&#xff0c;其中就有交叉编译工具。 当然&#xff0c;为了将来使用方便&#xff0c;我已经把它压缩并传到了百度网盘&#xff…

Idea实现定时任务

定时任务 什么是定时任务&#xff1f; 可以自动在项目中根据设定的时长定期执行对应的操作 实现方式 Spring 3.0 版本之后自带定时任务&#xff0c;提供了EnableScheduling注解和Scheduled注解来实现定时任务功能。 使用SpringBoot创建定时任务非常简单&#xff0c;目前主要…

Cesium 问题: 添加billboard后移动或缩放地球,标记点位置会左右偏移

文章目录 问题分析原先的:添加属性——解决漂移移动问题产生新的问题:所选的经纬度坐标和应放置的位置有偏差解决坐标位置偏差的问题完整代码问题 添加 billboard 后, 分析 原先的: // 图标加载 function addStation ({lon, lat, el, testName

PHP中GD库的使用

由于我要用到php的验证码 <?php session_start();// 生成验证码 $random_code substr(md5(uniqid(mt_rand(), true)), 0, 6);// 将验证码保存到 session 中 $_SESSION[captcha] $random_code;// 创建图片 $font 6; $image_width 100; $image_height 40;// 创建图像 $…

精确的单向延迟测量:使用普通硬件和软件

论文标题&#xff1a;Precise One-way Delay Measurement with Common Hardware and Software&#xff08;精确的单向延迟测量&#xff1a;使用普通硬件和软件&#xff09; 作者信息&#xff1a;Maciej Muehleisen 和 Mazen Abdel Latif&#xff0c;来自Ericsson Research Eri…

数据挖掘:一、Weka软件的基本操作

实验目的和要求 了解Weka软件的使用 实验环境 Windows11 Weka3.8.6 实验内容与过程 实验内容 1、了解Weka使用的一般步骤 2、利用Weka,对数据集进行关联规则挖掘及数据分类 3、记录操作步骤、使用的数据、最终的结果 实验过程 首先打开weka下载官网,选择合适

【Notepad++】---设置背景为护眼色(豆沙绿)最新最详细

在编程的艺术世界里&#xff0c;代码和灵感需要寻找到最佳的交融点&#xff0c;才能打造出令人为之惊叹的作品。而在这座秋知叶i博客的殿堂里&#xff0c;我们将共同追寻这种完美结合&#xff0c;为未来的世界留下属于我们的独特印记。 【Notepad】---设置背景为护眼色&#xf…

【微服务】SpringBoot 对接飞书多维表格事件回调监听流程详解

目录 一、前言 二、前置准备 2.1 创建一个应用 2.2 准备一张测试使用的多维表 2.3 获取对接文档 2.4 工程中添加SDK 三、对接过程 3.1 配置Encrypt Key 和 Verification Token 3.2 配置订阅方式 3.3 添加事件 3.4 申请权限 3.5 编写订阅代码 3.6 订阅文档事件 3.…

从0开始边做边学,用vue和python做一个博客,非规范化项目,怎么简单怎么弄,跑的起来有啥毛病解决啥毛病(三)

基础的内容都差不多了&#xff0c;开始研究文章详情页的内容设计&#xff0c;程序员嘛肯定要用markdown编辑了&#xff0c;找了一些发现这个md-editor看着还不错&#xff0c;文档介绍页比较清晰&#xff0c;用着也比较简单。 md-editor安装配置使用Mock.js模拟请求接口mock.js …

pdf转word/markdown等格式——MinerU的部署:2024最新的智能数据提取工具

一、简介 MinerU是开源、高质量的数据提取工具&#xff0c;支持多源数据、深度挖掘、自定义规则、快速提取等。含数据采集、处理、存储模块及用户界面&#xff0c;适用于学术、商业、金融、法律等多领域&#xff0c;提高数据获取效率。一站式、开源、高质量的数据提取工具&…

SWIRL:有望成为2025年顶级AI搜索引擎

现在几乎每家公司都会有内部文档系统&#xff0c;如阿里的语雀、钉钉&#xff0c;字节的飞书&#xff0c;Confluence&#xff0c;印象笔记等等都可以提供给B端在局域网部署。因此&#xff0c;如果能把搜索功能做得高效&#xff0c;就能提高自家产品的竞争力。 想象一下&#xf…