Linux的Socket开发概述

news2024/12/28 19:25:20

套接字(socket)是 Linux 下的一种进程间通信机制(socket IPC),在前面的内容中已经给大家提到过,使用 socket IPC 可以使得在不同主机上的应用程序之间进行通信(网络通信),当然也可以是同一台主机上的不同应用程序。socket IPC 通常使用客户端<--->服务器这种模式完成通信,多个客户端可以同时连接到服务器中,与服务器之间完成数据交互。

内核向应用层提供了 socket 接口,对于应用程序开发人员来说,我们只需要调用 socket 接口开发自己的应用程序即可!socket 是应用层与 TCP/IP 协议通信的中间软件抽象层,它是一组接口。在设计模式中,socket 其实就是一个门面模式,它把复杂的 TCP/IP 协议隐藏在 socket 接口后面,对用户来说,一组简单的接口就是全部,让 socket 去组织数据,以符合指定的协议。所以,我们无需深入的去理解 tcp/udp 等各种复杂的 TCP/IP 协议,socket 已经为我们封装好了,我们只需要遵循 socket 的规定去编程,写出的程序自然遵循 tcp/udp 标准的。

当前网络中的主流程序设计都是使用 socket 进行编程的,因为它简单易用,它还是一个标准(BSD socket),能在不同平台很方便移植,比如你的一个应用程序是基于 socket 接口编写的,那么它可以移植到任何实现 BSD socket 标准的平台,譬如 LwIP,它兼容 BSD Socket;又譬如 Windows,它也实现了一套基于socket 的套接字接口,更甚至在国产操作系统中,如 RT-Thread,它也实现了 BSD socket 标准的 socket 接口。

BSD套接字是最早在1983年随着BSD操作系统发布的套接字接口的名称。后来,这个接口被整合到了POSIX规范中,并增加了一些更详细的操作规范。除了术语不同以外,可以认为没有任何区别。BSD套接字涵盖了随着BSD操作系统发布的API,而POSIX标准适用于任何希望符合POSIX的操作系统。

可参考:

具体了解该套接字,可参考:

伯克利套接字(BSD Socket)-CSDN博客

建立连接相关API 

socket()函数

socket()函数原型如下所示:

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

socket()函数类似于 open()函数,它用于创建一个网络通信端点(打开一个网络通信),如果成功则返回一个网络文件描述符,通常把这个文件描述符称为 socket 描述符(socket descriptor),这个 socket 描述符跟文件描述符一样,后续的操作都有用到它,把它作为参数,通过它来进行一些读写操作。

该函数包括 3 个参数,如下所示:

domain

参数 domain 用于指定一个通信域;这将选择将用于通信的协议族。可选的协议族如下表所示:

对于 TCP/IP 协议来说,通常选择 AF_INET 就可以了,当然如果你的 IP 协议的版本支持 IPv6,那么可以选择 AF_INET6。

type

参数 type 指定套接字的类型,当前支持的类型有:

protocol

即协议类别,表示为给定的通信域和套接字类型选择默认协议,一般设置为0即可,因为该函数会通过前两个参数自动推导出第三个参数的协议类别。

调用 socket()与调用 open()函数很类似,调用成功情况下,均会返回用于文件 I/O 的文件描述符,只不过对于 socket()来说,其返回的文件描述符一般称为 socket 描述符。当不再需要该文件描述符时,可调用close()函数来关闭套接字,释放相应的资源。

如果 socket()函数调用失败,则会返回-1,并且会设置 errno 变量以指示错误类型。

使用示例

int socket_fd = socket(AF_INET, SOCK_STREAM, 0);//打开套接字
if (0 > socket_fd) {
    perror("socket error");
    exit(-1);
}
......
......
close(socket_fd); //关闭套接字

注意,初始时,客户端和服务器都需要调用socket,来指定使用的协议族和socket类型。

bind()函数

bind()函数原型如下所示:

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

bind()函数用于将一个 IP 地址和端口号与一个套接字进行绑定,对于客户端来说,它与服务器进行通信,首先需要知道服务器的 IP 地址以及对应的端口号,通常服务器的 IP 地址以及端口号都是众所周知的。

调用 bind()函数将参数 sockfd 指定的套接字与一个地址 addr 进行绑定,成功返回 0,失败情况下返回-1,并设置 errno 以提示错误原因。

参数 addr 是一个指针,指向一个 struct sockaddr 类型变量,如下所示:

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

第二个成员 sa_data 是一个 char 类型数组,一共 14 个字节,在这 14 个字节中就包括了 IP 地址、端口号等信息,这个结构对用户并不友好,它把这些信息都封装在了 sa_data 数组中,这样使得用户不方便对sa_data 数组进行赋值。事实上,这是一个通用的 socket 地址结构体。

一般我们在使用的时候都会使用 struct sockaddr_in 结构体,sockaddr_in 和 sockaddr 是并列的结构(占用的空间是一样的),指向 sockaddr_in 的结构体的指针也可以指向 sockadd 的结构体,并代替它,而且sockaddr_in 结构对用户将更加友好,在使用的时候进行类型转换就可以了。该结构体内容如下所示:

struct sockaddr_in {
    sa_family_t sin_family;
    /* 协议族 */
    in_port_t sin_port;
    /* 端口号 */
    struct in_addr sin_addr;
    /* IP 地址 */
    unsigned char sin_zero[8];
};

这个结构体的第一个字段是与 sockaddr 结构体是一致的,而剩下的字段就是 sa_data 数组连续的 14 字节信息里面的内容,只不过从新定义了成员变量而已,sin_port 字段是我们需要填写的端口号信息,sin_addr字段是我们需要填写的 IP 地址信息,剩下 sin_zero 区域的 8 字节保留未用。

bind函数的最后一个参数 addrlen 指定了 addr 所指向的结构体对应的字节长度。

使用示例

struct sockaddr_in socket_addr;
memset(&socket_addr, 0x0, sizeof(socket_addr)); //清零
//填充变量
socket_addr.sin_family = AF_INET;
socket_addr.sin_addr.s_addr = htonl(INADDR_ANY);
socket_addr.sin_port = htons(5555);
//将地址与套接字进行关联、绑定
bind(socket_fd, (struct sockaddr *)&socket_addr, sizeof(socket_addr));

注意,代码中的 htons 和 htonl 并不是函数,只是一个宏定义,主要的作用在于为了避免大小端的问题,需要这些宏需要在我们的应用程序代码中包含头文件<netinet/in.h>。

说明:

ip用来确定一台设备,端口号用来确定一个进程。

bind函数是在服务端使用,并且绑定的是服务器本身的ip和端口号。

为啥不是绑定对方的ip和端口?比如客户端绑定服务器的,服务器绑定客户端的,可根据使用情况来看,只有服务端需要用bind,并且绑定的是自己的ip和端口,好像客户端的ip和端口不用显式绑定,为啥?

所以,为什么TCP服务端需要调用bind函数而客户端通常不需要呢?

bind是为了将当前的socket(可以理解成网络连接实例)绑定到了服务端的ip和端口上,然后服务端就可以实时监听有没有客户端连接到了这个socket上,也就是说,一旦绑定的这个ip和端口有动静,服务端就可以响应了。

客户端上线是主动向服务器发出请求的,因为服务器已经绑定了IP和端口,所以客户端上线的就向这个IP和端口发出请求,这时因为客户开始发数据了(发上线请求),操作系统就给客户端分配一个随机端口,这个端口和客户端的IP会随着上线请求一起发给服务器。

服务器收到上线请求后就可以从中获发此请求的客户的IP(源ip字段)和端口,接下来服务器就可以利用获的IP和端口给客户端回应消息了。

总之一句话:客户端是主动连接(Connect),而服务器是等待接受连接(Accept)

注意:一旦建立了连接,客户端 和 服务器端 均可以发送(Send)或者接收(Receive)数据。

更多参考:

为什么TCP服务端需要调用bind函数而客户端通常不需要呢?_tcp bind-CSDN博客

Tips:bind()函数并不是总是需要调用的,只有用户进程想与一个具体的 IP 地址或端口号相关联的时候才需要调用这个函数。如果用户进程没有这个必要,那么程序可以依赖内核的自动的选址机制来完成自动地址选择,通常在客户端应用程序中会这样做。

难怪客户端可以使用动态IP地址,原来是客户端可以让操作系统来自动获取ip和端口。

tcp服务端必须有bind, 客户端通常不用bind,  当然如果你够无聊, 那也可以用一下bind。在这里, 我要说一下了: 客户端用bind的程序很容易出问题, 你想想啊, 操作系统指定的不会冲突的随机端口难道不比你自己指定的容易冲突的固定端口好?在很多场景下, 我们要在一个pc上开启多个客户端进程, 如果指定固定端口,必然会造成端口冲突,影响通信!所以,我们就不要费力不讨好了,客户端就不要指定端口了,让操作系统来搞。

listen()函数

listen()函数只能在服务器进程中使用,让服务器进程进入监听状态,等待客户端的连接请求,listen()函数在一般在 bind()函数之后调用,在 accept()函数之前调用,它的函数原型是:

int listen(int sockfd, int backlog);

无法在一个已经连接的套接字(即已经成功执行 connect()的套接字或由 accept()调用返回的套接字)上执行 listen()。

参数 backlog 用来描述 sockfd 的等待连接队列能够达到的最大值。在服务器进程正处理客户端连接请求的时候,可能还存在其它的客户端请求建立连接,因为 TCP 连接是一个过程,由于同时尝试连接的用户过多,使得服务器进程无法快速地完成所有的连接请求,那怎么办呢?直接丢掉其他客户端的连接肯定不是一个很好的解决方法。因此内核会在自己的进程空间里维护一个队列,这些连接请求就会被放入一个队列中,服务器进程会按照先来后到的顺序去处理这些连接请求,这样的一个队列内核不可能让其任意大,所以必须有一个大小的上限,这个 backlog 参数告诉内核使用这个数值作为队列的上限。而当一个客户端的连接请求到达并且该队列为满时,客户端可能会收到一个表示连接失败的错误,本次请求会被丢弃不作处理。 一般设置为5、10。

accept()函数

服务器调用 listen()函数之后,就会进入到监听状态,等待客户端的连接请求,使用 accept()函数获取客户端的连接请求并建立连接。函数原型如下所示:

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

为了能够正常让客户端能正常连接到服务器,服务器必须遵循以下处理流程:

①、调用 socket()函数打开套接字;

②、调用 bind()函数将套接字与一个端口号以及 IP 地址进行绑定;

③、调用 listen()函数让服务器进程进入监听状态,监听客户端的连接请求;

④、调用 accept()函数处理到来的连接请求。

accept()函数通常只用于服务器应用程序中,如果调用 accept()函数时,并没有客户端请求连接(等待连接队列中也没有等待连接的请求),此时 accept()会进入阻塞状态,直到有客户端连接请求到达为止。当有客户端连接请求到达时,accept()函数与远程客户端之间建立连接,accept()函数返回一个新的套接字。这个套接字与 socket()函数返回的套接字并不同,socket()函数返回的是服务器的套接字(以服务器为例),而accept()函数返回的套接字连接到调用 connect()的客户端,服务器通过该套接字与客户端进行数据交互,譬如向客户端发送数据、或从客户端接收数据。

所以,理解 accept()函数的关键点在于它会创建一个新的套接字,其实这个新的套接字就是与执行connect()(客户端调用 connect()向服务器发起连接请求)的客户端之间建立了连接,这个套接字代表了服务器与客户端的一个连接。如果 accept()函数执行出错,将会返回-1,并会设置 errno 以指示错误原因。

参数 addr 是一个传出参数,参数 addr 用来返回已连接的客户端的 IP 地址与端口号等这些信息。参数addrlen 应设置为 addr 所指向的对象的字节长度,如果我们对客户端的 IP 地址与端口号这些信息不感兴趣,可以把 arrd 和 addrlen 均置为空指针 NULL。

connect()函数

connect()函数原型如下所示:

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

该函数用于客户端应用程序中,客户端调用 connect()函数将套接字 sockfd 与远程服务器进行连接,参数 addr 指定了待连接的服务器的 IP 地址以及端口号等信息,参数 addrlen 指定了 addr 指向的 struct sockaddr对象的字节大小。

客户端通过 connect()函数请求与服务器建立连接,对于 TCP 连接来说,调用该函数将发生 TCP 连接的握手过程,并最终建立一个 TCP 连接,而对于 UDP 协议来说,调用这个函数只是在 sockfd 中记录服务器IP 地址与端口号,而不发送任何数据。

函数调用成功则返回 0,失败返回-1,并设置 errno 以指示错误原因。

数据读写相关API

一旦客户端与服务器建立好连接之后,我们就可以通过套接字描述符来收发数据了(对于客户端使用socket()返回的套接字描述符,而对于服务器来说,需要使用 accept()返回的套接字描述符),这与我们读写普通文件是差不多的操作,譬如可以调用 read()或 recv()函数读取网络数据,调用 write()或 send()函数发送数据。

read()函数

read()函数大家都很熟悉了,通过 read()函数从一个文件描述符中读取指定字节大小的数据并放入到指定的缓冲区中,read()调用成功将返回读取到的字节数,此返回值受文件剩余字节数限制,当返回值小于指定的字节数时并不意味着错误;这可能是因为当前可读取的字节数小于指定的字节数(比如已经接近文件结尾,或者正在从管道或者终端读取数据,或者 read()函数被信号中断等),出错返回-1 并设置 errno,如果在调 read 之前已到达文件末尾,则这次 read 返回 0。

套接字描述符也是文件描述符,所以使用 read()函数读取网络数据时,read()函数的参数 fd 就是对应的套接字描述符。

recv()函数

recv()函数原型如下所示:

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

不论是客户端还是服务器都可以通过 revc()函数读取网络数据,它与 read()函数的功能是相似的。参数sockfd 指定套接字描述符,参数 buf 指向了一个数据接收缓冲区,参数 len 指定了读取数据的字节大小,参数 flags 可以指定一些标志用于控制如何接收数据。

函数 recv()与 read()很相似,但是 recv()可以通过指定 flags 标志来控制如何接收数据,通常一般我们将 flags 参数设置为 0,当然,你可以根据自己的需求设置该参数。

write()函数

通过 write()函数可以向套接字描述符中写入数据,函数调用成功返回写入的字节数,失败返回-1,并设置 errno 变量。

send()函数

函数原型如下所示:

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

send 和 write 很相似,但是 send 可以通过参数 flags 指定一些标志,来改变处理传输数据的方式。

即使 send()成功返回,也并不表示连接的另一端的进程就一定接收了数据,我们所能保证的只是当 send成功返回时,数据已经被无错误的发送到网络驱动程序上。

close()函数

当不再需要套接字描述符时,可调用 close()函数来关闭套接字,释放相应的资源。

参考示例

综合实践之TCP参考:

TCP(TCP客户端、服务器如何通信)_tcp客户端和服务器-CSDN博客

综合实践之UDP参考:

UDP编程流程(UDP客户端、服务器互发消息流程)_udp网络编程-CSDN博客

注意:在TCP连接中,服务器通常处于被动状态,等待客户端的连接请求。而客户端则处于主动状态,负责发起连接请求。一旦连接建立成功,双方就可以进行数据传输。  

TCP和UDP通信过程对比图如下:

更多待补充。

更多参考:

【网络】网络编程_编写网络程序-CSDN博客

RPC

什么是rpc?

进程间通信-浅谈RPC- 最小原型_进程间rpc-CSDN博客

RPC框架:从原理到选型,一文带你搞懂RPC-CSDN博客

深入理解RPC:从原理到实战_深入理解rpc框架原理与实现 pdf-CSDN博客

RPC、Http、TCP/IP、Socket之间的关系及定义_socket rpc-CSDN博客

有了 HTTP 协议,为什么还要 RPC 协议,两者有什么区别?-腾讯云开发者社区-腾讯云 (tencent.com)

RPC核心原理和实战 —— 基础篇 | Focus-1

暂时了解下吧,后续有实际应用场景时再补充。

InternetSocket和UnixSocket 

以前一直以为socket只是两个网络主机之间的进程通信,原来socket也可以用在一个主机上的两个进程之间的通信。

参考:

Internet socket和Unix socket_socket类型 internet unix-CSDN博客

unix socket通信,UNIX Domain SOCKET 是在Socket架构上发展起来的用于同一台主机的进程间通信(IPC)。它不需要经过网络协议栈,不需要打包拆包、计算校验和、维护序列号应答等。只是将应用层数据从一个进程拷贝到另一个进程。

使用UNIX Domain Socket的过程和网络socket十分相似,也要先调用socket()创建一个socket文件描述符,address family指定为AF_UNIX,type可以选择SOCK_DGRAM或SOCK_STREAM,protocol参数仍然指定为0即可。

UNIX Domain Socket与网络socket编程最明显的不同在于地址格式不同,用结构体sockaddr_un表示,网络编程的socket地址是IP地址加端口号,而UNIX Domain Socket的地址是一个socket类型的文件在文件系统中的路径,这个socket文件由bind()调用创建,如果调用bind()时该文件已存在,则bind()错误返回。

无论是internet socket还是unix socket,服务端和客户端创建socket并进行通信的步骤都是一致的

服务端: socket -> bind -> listen -> accept -> write/send或read/recv -> close

客户端: socket -> connect -> write/send或read/recv -> close

并且每一步骤调用的函数也都是同样的函数,不同点主要在于

  1. socket函数domain字段的值不同,internet socket值为AF_INET,unix socket值为AF_UNIX
  2. socket函数的type参数的值都是SOCK_STREAM和SOCK_DGRAM,但是对于unix socket通信来说,由于是进程间的通信,这两种类型提供的服务都是可靠的
  3. bind函数中,internet socket传入的结构体是sockaddr_in,并且会绑定ip地址和端口号,unix socket传入的结构体是sockaddr_un,会绑定一个用于进程间通信的文件,但是都是需要进行强制转换的
  4. accept函数中,internet socket需要传入sockaddr_in结构体来保存发出请求的客户端的地址,但是在unix socket中,这一项为NULL,在进程通信中,在服务端上无法确定出来这个客户端进程是哪一个

后边的收发数据的调用过程都是一样的,总体来看,两种socket通信的步骤是一致的,但是用于是不一样的,一个是用于网络中两个主机之间的通信,另一个则是一个主机中两个进程之间的通信,这种一致性我觉得可能是因为linux系统中的一切皆文件的思想,无论是进程间通信或者是主机间通信,通过socket函数就能返回一个文件描述符,这个文件描述符的背后可能是用于网络传输的网络连接,或者是进程间的通信,但是对于编程者而言就是用于输入输出的一个文件描述符,这样进程间的通信和主机间的通信都被抽象成了对一个文件的输入输出操作,所以才造成了internet socket和unix socket通信的这种一致性;在细节方面,也正是因为有bind(int sockfd, const struct sockaddr *addr, socklen_t addrlen)函数,它提供了一个同一的结构体,在不同类型的socket编程时可以让sockaddr_un和sockaddr_in结构体强制类型转换,所以能够让编写不同类型的socket程序的时候可以调用同样的接口,才造成了在编程时候的这种一致性。

注意,UNIX计算机配置了一个只包含它自身的回路(loopback)网络,网络中只有一台计算机localhost,它有一个标准的IP地址127.0.0.1。

更多参考:

Linux的套接字——进程间通信的另一种方法_进程内部可以通过套接字进行通信吗-CSDN博客

套接字是一种通信机制,通过使用套接字接口,一台机器上的进程可以和另一台机器上的进程进行网络通信,同一台机器之间的进程也可以互相通信。

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

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

相关文章

cv2.cvtColor的示例用法

-------------OpenCV教程集合------------- Python教程99&#xff1a;一起来初识OpenCV&#xff08;一个跨平台的计算机视觉库&#xff09; OpenCV教程01&#xff1a;图像的操作&#xff08;读取显示保存属性获取和修改像素值&#xff09; OpenCV教程02&#xff1a;图像处理…

德国威步的技术演进之路(下):从云端许可管理到硬件加密狗的创新

从单机用户许可证到WkNET网络浮点授权的推出&#xff0c;再到引入使用次数和丰富的时间许可证管理&#xff0c;德国威步产品不断满足市场对灵活性和可扩展性的需求。TCP/IP浮动网络许可证进一步展示了威步技术在网络时代的创新应用。借助于2009年推出的借用许可证以及2015年推出…

CV- 人工智能-深度学习基础知识

一, 深度学习基础知识 1,什么是深度学习?机器学习是实现人工智能的一种途径,深度学习是机器学习的一个子集,也就是说深度学习是实现机器学习的一种方法。2, 传统机器学习算术依赖人工设计特征,并进行特征提取,而深度学习方法不需要人工,而是依赖算法自动提取特征。深度…

llm学习-4(llm和langchain)

langchain说明文档&#xff1a;langchain 0.2.6 — &#x1f99c;&#x1f517; langChain 0.2.6https://api.python.langchain.com/en/latest/langchain_api_reference.html#module-langchain.chat_models 1&#xff1a;模型 &#xff08;1&#xff09;自定义模型导入&#x…

代码随想录-Day46

121. 买卖股票的最佳时机 给定一个数组 prices &#xff0c;它的第 i 个元素 prices[i] 表示一支给定股票第 i 天的价格。 你只能选择 某一天 买入这只股票&#xff0c;并选择在 未来的某一个不同的日子 卖出该股票。设计一个算法来计算你所能获取的最大利润。 返回你可以从…

pmp顺利通关总结

目录 一、背景二、总结三、过程 一、背景 人活着总是想去做一些事情&#xff0c;通过这些事情来证明自己还活着。 而我证明自己还会活着并且活得很好的方式和途径&#xff0c;是通过这些东西去让自己有一个明确的边界节点&#xff1b;借此知识来验证自己的学习能力。 我坚定认…

掌握Go语言邮件发送:net/smtp实用教程与最佳实践

掌握Go语言邮件发送&#xff1a;net/smtp实用教程与最佳实践 概述基本配置与初始化导入net/smtp包设置SMTP服务器基本信息创建SMTP客户端实例身份验证 发送简单文本邮件配置发件人信息构建邮件头部信息编写邮件正文使用SendMail方法发送邮件示例代码 发送带附件的邮件邮件多部分…

硅纪元视角 | 1 分钟搞定 3D 创作,Meta 推出革命性 3D Gen AI 模型

在数字化浪潮的推动下&#xff0c;人工智能&#xff08;AI&#xff09;正成为塑造未来的关键力量。硅纪元视角栏目紧跟AI科技的最新发展&#xff0c;捕捉行业动态&#xff1b;提供深入的新闻解读&#xff0c;助您洞悉技术背后的逻辑&#xff1b;汇聚行业专家的见解&#xff0c;…

服务器之BIOS基础知识总结

1.BIOS是什么&#xff1f; BIOS全称Basic Input Output System&#xff0c;即基本输入输出系统&#xff0c;是固化在服务器主板的专用ROM上&#xff0c;加载在服务器硬件系统上最基本的运行程序&#xff0c;它位于服务器硬件和OS之间&#xff0c;在服务器启动过程中首先运行&am…

《亚马逊搬运亚马逊产品》配合跟卖采集爬取跟卖店铺高质量

亚马逊高质量产品如何搬运&#xff1f;亚马逊采集亚马逊。 哈喽大家好&#xff0c;大家讲一下做亚马逊是发货、铺货这块的功能。目前这款软件做跟卖大家都知道&#xff0c;同时也支持做铺货。铺货可以采集国内的1688、淘宝、京东都可以采&#xff0c;采完之后也可以采速卖通&a…

flutter开发实战-Webview及dispose关闭背景音

flutter开发实战-Webview及dispose关闭背景音 当在使用webview的时候&#xff0c;dispose需要关闭网页的背景音或者音效。 一、webview的使用 在工程的pubspec.yaml中引入插件 webview_flutter: ^4.4.2webview_cookie_manager: ^2.0.6Webview的使用代码如下 初始化WebView…

UiPath+Appium实现app自动化测试

一、环境准备工作 1.1 完成appium环境的搭建 参考&#xff1a;pythonappiumpytestallure模拟器(MuMu)自动化测试环境搭建_appium mumu模拟器-CSDN博客 1.2 完成uipath的安装 登录官网&#xff0c;完成注册与软件下载安装。 UiPath业务自动化平台&#xff1a;先进的RPA及自动…

Linux操作系统学习:day08

内容来自&#xff1a;Linux介绍 视频推荐&#xff1a;Linux基础入门教程-linux命令-vim-gcc/g -动态库/静态库 -makefile-gdb调试 目录 day0853、命令和编辑模式之间的切换54、命令模式到末行模式的切换与末行模式下的保存退出命令模式到末行模式的切换保存退出 55、末行模式…

大模型训练优化方法

写在前面 在训练模型尤其是大模型的时候&#xff0c;如何加快训练速度以及优化显存利用率是一个很关键的问题。本文主要参考HF上的一篇文章&#xff1a;https://huggingface.co/docs/transformers/perf_train_gpu_one&#xff0c;以及笔者在实际训练中的一些经验&#xff0c;给…

SpringBoot 整合 Minio 实现文件切片极速上传技术

Centos7安装Minio 创建目标文件夹 mkdir minio使用docker查看目标镜像状况 大家需要注意&#xff0c;此处我们首先需要安装docker&#xff0c;对于相关安装教程&#xff0c;大家可以查看我之前的文章&#xff0c;按部就班就可以&#xff0c;此处不再赘述&#xff01;&#x…

【电商指标详解】

前言&#xff1a; &#x1f49e;&#x1f49e;大家好&#xff0c;我是书生♡&#xff0c;本篇文章主要和大家分享一下电商行业中常见指标的详解&#xff01;存在的原因和作用&#xff01;&#xff01;&#xff01;希望对大家有所帮助。 &#x1f49e;&#x1f49e;代码是你的画…

论文学习笔记1:Federated Graph Neural Networks: Overview, Techniques, and Challenges

文章目录 一、introduction二、FedGNN术语与分类2.1主要分类法2.2辅助分类法 三、GNN-ASSISTED FL3.1Centralized FedGNNs3.2Decentralized FedGNNs 四、FL-ASSISTED GNNS4.1horizontal FedGNNs4.1.1Clients Without Missing Edges4.1.1.1Non-i.i.d. problem4.1.1.2Graph embed…

Navicat和MySQL的安装

1、下载 Navicat Navicat 官网&#xff1a;www.navicat.com.cn/ 在产品中可以看到很多的产品&#xff0c;点击免费试用 Navicat Premium 即可&#xff0c;是一套多连数据库开发工具&#xff0c;其他的只能连接单一类型数据库 点击试用 选择系统直接下载 二、安装 Navicat 安…

天诚长租公寓智能门锁管理解决方案

人才是区域创新发展的第一资源&#xff0c;如何解决人才的住房问题&#xff0c;让人才“流进来”、“留下来”、“融进来”&#xff0c;就需要优先安排优质人才公寓、人才优租房和公共租赁住房房源&#xff0c;并为青年人才群体提供智能化、信息化的租住体验及通行服务。 一、…

Hive查询优化 - 面试工作不走弯路

引言&#xff1a;Hive作为一种基于Hadoop的数据仓库工具&#xff0c;广泛应用于大数据分析。然而&#xff0c;由于其依赖于MapReduce框架&#xff0c;查询的性能可能会受到影响。为了确保Hive查询能够高效运行&#xff0c;掌握查询优化技巧至关重要。在日常工作中&#xff0c;高…