linux应用编程--网络编程(socket编程基础)

news2024/9/20 18:01:50

介绍:套接字(socket)是 Linux 下的一种进程间通信机制(socket IPC),在前面的内容中已经给大家提到过, 使用 socket IPC 可以使得在不同主机上的应用程序之间进行通信(网络通信),当然也可以是同一台主机上 的不同应用程序。socket IPC 通常使用客户端服务器这种模式完成通信,多个客户端可以同时连接到服 务器中,与服务器之间完成数据交互。 当前网络中的主流程序设计都是使用 socket 进行编程的,因为它简单易用,它还是一个标准(BSD socket),能在不同平台很方便移植。

socket 编程接口介绍:

socket()函数:

函数原型如下:

#include <sys/types.h> /* See NOTES */
#include <sys/socket.h>

int socket(int domain, int type, int protocol);

该函数类似于open()函数,它的作用是创建一个网络通信端点(打开一个网络通信),如果成功则返回一个网络文件描述符,通常把这个文件描述符称为socket描述符,后续操作都需要把它作为参数。

函数参数作用如下:

domian:

该参数用于指定一个通信域,这将选择用于通信的协议簇,可选的协议簇如下表:

对于TCP/IP协议来说,通常选择AF_INET就可以了,如果IP协议支持IPv6,那么可以选择AF_INET6.

type:

该参数指定套接字的类型:

protocol:

该参数通常设置为0,表示为给定的 通信域和套接字类型选择默认协议。。当对同一域和套接字类 型支持多个协议时,可以使用 protocol 参数选择一个特定协议。在 AF_INET 通信域中,套接字类型为 SOCK_STREAM 的默认协议是传输控制协议(Transmission Control Protocol,TCP 协议)。在 AF_INET 通 信域中,套接字类型为 SOCK_DGRAM 的默认协议时 UDP。 调用 socket()与调用 open()函数很类似,调用成功情况下,均会返回用于文件 I/O 的文件描述符,只不 过对于 socket()来说,其返回的文件描述符一般称为 socket 描述符。

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

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

bind()函数

函数原型如下:

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

bind()函数用于将一个 IP 地址或端口号与一个套接字进行绑定(将套接字与地址进行关联)。将一个客 户端的套接字关联上一个地址没有多少新意,可以让系统选一个默认的地址。一般来讲,会将一个服务器的 套接字绑定到一个众所周知的地址---即一个固定的与服务器进行通信的客户端应用程序提前就知道的地址 (注意这里说的地址包括 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 字节保留未用。 最后一个参数 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 并不是函数,只是一个宏定义,主要的作用在于为了避免大小端的问题, 需要这些宏需要在我们的应用程序代码中包含头文件。

listen()函数

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

int listen(int sockfd, int backlog);

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

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

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 以指示错误原因。

发送和接收函数:

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

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,当然,你可以根据自己的需求设置该参数。 当指定 MSG_PEEK 标志时,可以查看下一个要读取的数据但不真正取走它,当再次调用 read 或 recv 函数时,会返回刚才查看的数据。 对于 SOCK_STREAM 类型套接字,接收的数据可以比指定的字节大小少。MSG_WAITALL 标志会阻 止这种行为,知道所请求的数据全部返回,recv 函数才会返回。对于 SOCK_DGRAM 和 SOCK_SEQPACKET 套接字,MSG_WAITALL 标志并不会改变什么行为,因为这些基于报文的套接字类型一次读取就返回整个 报文。 如果发送者已经调用 shutdown 来结束传输,或者网络协议支持按默认的顺序关闭并且发送端已经关闭, 那么当所有的数据接收完毕后,recv 会返回 0。 recv 在调用成功情况下返回实际读取到的字节数。

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()函数来关闭套接字,释放相应的资源

IP地址转换函数

对于人来说,我们更容易阅读的是点分十进制的 IP 地址,譬如 192.168.1.110、192.168.1.50,这其实是 一种字符串的形式,但是计算机所需要理解的是二进制形式的 IP 地址,所以我们就需要在点分十进制字符 串和二进制地址之间进行转换。 点分十进制字符串和二进制地址之间的转换函数主要有:inet_aton、inet_addr、inet_ntoa、inet_ntop、 inet_pton 这五个,在我们的应用程序中使用它们需要包含头文件

<sys/socket.h>
<arpa/inet.h>
<netinet/in.h>

inet_aton、inet_addr、inet_ntoa 函数:

这些函数可将一个 IP 地址在点分十进制表示形式和二进制表示形式之间进行转换,这些函数已经废弃 了,基本不用这些函数了,但是在一些旧的代码中可能还会看到这些函数。完成此类转换工作我们应该使用 下面介绍的这些函数。

inet_ntop、inet_pton 函数:

inet_ntop()、inet_pton()与 inet_ntoa()、inet_aton()类似,但它们还支持 IPv6 地址。它们将二进制 Ipv4 或 Ipv6 地址转换成以点分十进制表示的字符串形式,或将点分十进制表示的字符串形式转换成二进制 Ipv4 或 Ipv6 地址。使用这两个函数只需包含头文件即可!

inet_pton()函数

<arpa/inet.h>
int inet_pton(int af, const char *src, void *dst);

inet_pton()函数将点分十进制表示的字符串形式转换成二进制 Ipv4 或 Ipv6 地址。

将字符串 src 转换为二进制地址,参数 af 必须是 AF_INET 或 AF_INET6,AF_INET 表示待转换的 Ipv4 地址,AF_INET6 表示待转换的是 Ipv6 地址;并将转换后得到的地址存放在参数 dst 所指向的对象中,如果 参数 af 被指定为 AF_INET,则参数 dst 所指对象应该是一个 struct in_addr 结构体的对象;如果参数 af 被指 定为 AF_INET6,则参数 dst 所指对象应该是一个 struct in6_addr 结构体的对象。

inet_pton()转换成功返回 1(已成功转换)。如果 src 不包含表示指定地址族中有效网络地址的字符串, 则返回 0。如果 af 不包含有效的地址族,则返回-1 并将 errno 设置为 EAFNOSUPPORT。

使用实例:

#include <stdio.h>
#include <stdlib.h>
#include <arpa/inet.h>
#define IPV4_ADDR "192.168.1.222"
int main(void)
{
 struct in_addr addr;
 inet_pton(AF_INET, IPV4_ADDR, &addr);
 printf("ip addr: 0x%x\n", addr.s_addr);
 exit(0);
}

inet_ntop()函数:

inet_ntop()函数执行与 inet_pton()相反的操作,函数原型如下所示:

const char *inet_ntop(int af, const void *src, char *dst, socklen_t size);

参数 af 与 inet_pton()函数的 af 参数意义相同。 参数 src 应指向一个 struct in_addr 结构体对象或 struct in6_addr 结构体对象,依据参数 af 而定。函数 inet_ntop()会将参数 src 指向的二进制 IP 地址转换为点分十进制形式的字符串,并将字符串存放在参数 dts 所指的缓冲区中,参数 size 指定了该缓冲区的大小。

inet_ntop()在成功时会返回 dst 指针。如果 size 的值太小了,那么将会返回 NULL 并将 errno 设置为 ENOSPC。

使用示例:

#include <stdio.h>
#include <stdlib.h>
#include <arpa/inet.h>
int main(void)
{
 struct in_addr addr;
 char buf[20] = {0};
 addr.s_addr = 0xde01a8c0;
 inet_ntop(AF_INET, &addr, buf, sizeof(buf));
 printf("ip addr: %s\n", buf);
 exit(0);
}

编写服务器程序

编写服务器应用程序的流程如下: ①、调用 socket()函数打开套接字,得到套接字描述符; ②、调用 bind()函数将套接字与 IP 地址、端口号进行绑定; ③、调用 listen()函数让服务器进程进入监听状态; ④、调用 accept()函数获取客户端的连接请求并建立连接; ⑤、调用 read/recv、write/send 与客户端进行通信; ⑥、调用 close()关闭套接字。

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <netinet/in.h>

#define SERVER_PORT 8888 //端口号不能发生冲突,不常用的端口号通常大于 5000
int main(void)
{
 struct sockaddr_in server_addr = {0};
 struct sockaddr_in client_addr = {0};
 char ip_str[20] = {0};
 int sockfd, connfd;
 int addrlen = sizeof(client_addr);
 char recvbuf[512];
 int ret;
 /* 打开套接字,得到套接字描述符 */
 sockfd = socket(AF_INET, SOCK_STREAM, 0);
 if (0 > sockfd) {
 perror("socket error");
 exit(EXIT_FAILURE);
 }
 /* 将套接字与指定端口号进行绑定 */
 server_addr.sin_family = AF_INET;
 server_addr.sin_addr.s_addr = htonl(INADDR_ANY);
 server_addr.sin_port = htons(SERVER_PORT);
 ret = bind(sockfd, (struct sockaddr *)&server_addr, sizeof(server_addr));
 if (0 > ret) {
 perror("bind error");
 close(sockfd);
 exit(EXIT_FAILURE);
 }
 /* 使服务器进入监听状态 */
 ret = listen(sockfd, 50);
 if (0 > ret) {
 perror("listen error");
 close(sockfd);
 exit(EXIT_FAILURE);
 }
 /* 阻塞等待客户端连接 */
 connfd = accept(sockfd, (struct sockaddr *)&client_addr, &addrlen);
 if (0 > connfd) {
perror("accept error");
 close(sockfd);
 exit(EXIT_FAILURE);
 }
 printf("有客户端接入...\n");
 inet_ntop(AF_INET, &client_addr.sin_addr.s_addr, ip_str, sizeof(ip_str));
 printf("客户端主机的 IP 地址: %s\n", ip_str);
 printf("客户端进程的端口号: %d\n", client_addr.sin_port);
 /* 接收客户端发送过来的数据 */
 for ( ; ; ) {
 // 接收缓冲区清零
 memset(recvbuf, 0x0, sizeof(recvbuf));
 // 读数据
 ret = recv(connfd, recvbuf, sizeof(recvbuf), 0);
 if(0 >= ret) {
 perror("recv error");
 close(connfd);
 break;
 }
 // 将读取到的数据以字符串形式打印出来
 printf("from client: %s\n", recvbuf);
 // 如果读取到"exit"则关闭套接字退出程序
 if (0 == strncmp("exit", recvbuf, 4)) {
 printf("server exit...\n");
 close(connfd);
 break;
 }
 }
 /* 关闭套接字 */
 close(sockfd);
 exit(EXIT_SUCCESS);
}

编写客户端程序:

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <netinet/in.h>
#define SERVER_PORT 8888 //服务器的端口号
#define SERVER_IP "192.168.1.150" //服务器的 IP 地址
int main(void)
{
     struct sockaddr_in server_addr = {0};
     char buf[512];
     int sockfd;
     int ret;
     /* 打开套接字,得到套接字描述符 */
     sockfd = socket(AF_INET, SOCK_STREAM, 0);
     if (0 > sockfd) {
     perror("socket error")
exit(EXIT_FAILURE);
 }
     /* 调用 connect 连接远端服务器 */
     server_addr.sin_family = AF_INET;
     server_addr.sin_port = htons(SERVER_PORT); //端口号
     inet_pton(AF_INET, SERVER_IP, &server_addr.sin_addr);//IP 地址
     ret = connect(sockfd, (struct sockaddr *)&server_addr, sizeof(server_addr));
     if (0 > ret) {
     perror("connect error");
     close(sockfd);
     exit(EXIT_FAILURE);
     }
     printf("服务器连接成功...\n\n");
     /* 向服务器发送数据 */
     for ( ; ; ) {
     // 清理缓冲区
     memset(buf, 0x0, sizeof(buf));
     // 接收用户输入的字符串数据
     printf("Please enter a string: ");
     fgets(buf, sizeof(buf), stdin);
     // 将用户输入的数据发送给服务器
     ret = send(sockfd, buf, strlen(buf), 0);
     if(0 > ret){
     perror("send error");
     break;
     }
     //输入了"exit",退出循环
     if(0 == strncmp(buf, "exit", 4))
     break;
 }
 close(sockfd);
 exit(EXIT_SUCCESS);
}

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

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

相关文章

Python(PyTorch)多语言图像感知质量指标算法

&#x1f3af;要点 &#x1f3af;算法实现&#xff1a;&#x1f58a;PyTorch单尺度和多尺度质量指标算法 | &#x1f58a;C单尺度质量指标算法 | &#x1f58a;Rust多尺度质量指标算法 | &#x1f58a;LabVIEW单尺度质量指标算法 | &#x1f58a;MATLAB单尺度质量指标算法 | &…

TCP+UDP通信

一、UDP协议 1.1、recvfrom() 参数说明 int sockfd, //socket 的fd void *buf, // 保存数据的一块空间的地址 size_t len, //这块空间的大小 int flags,// 0 默认的接收方式 -----阻塞方式 默认行为是阻塞 MSG_DONTWAIT 不阻塞方式&#xff0c;用他的话代表读的时候是非…

宠物掉毛、有异味怎么办?怎么选择宠物空气净化器?

每当我和朋友提起我家养猫养狗之后&#xff0c;不少朋友总会带着好奇与担忧的表情&#xff0c;半开玩笑地说&#xff1a;“你家里岂不是充满了‘特别’的味道&#xff1f;”这也不怪她们会有这种印象&#xff0c;因为大部分养宠家庭可能都会遇到浮毛满天飞的情况&#xff0c;但…

深入探讨SD NAND的SD模式与SPI模式初始化

在嵌入式系统和存储解决方案中&#xff0c;SD NAND的广泛应用是显而易见的。CS创世推出的SD NAND支持SD模式和SPI模式&#xff0c;这两种模式在功能和实现上各有优劣。在本文中&#xff0c;我们将深入探讨这两种模式的初始化过程&#xff0c;并比较它们在不同应用场景下的优劣&…

什么是上网行为管理呢?【上网行为管理系统功能介绍 】

小编想跟大家介绍上网行为管理系统的功能&#xff0c;可以提高工作效率和安全性。如果你也和部分管理者一样&#xff0c;经常为网络管理头疼&#xff0c;不妨看看小编解析的上网行为管理功能&#xff0c;会让眼前一亮哦&#xff01;上网行管控 应用场景&#xff1a;过滤非法网站…

Tomcat类加载机制详解

1.Tomcat类加载机制详解 1.1 JVM类加载器 Java中有 3 个类加载器&#xff0c;另外你也可以自定义类加载器 引导&#xff08;启动&#xff09;类加载器&#xff1a;负责加载支撑JVM运行的位于JRE的lib目录下的核心类库&#xff0c;比如rt.jar、charsets.jar等扩展类加载器&am…

流量掘金付费进群源码,最新无人直播变现玩法

外面1800流量掘金付费进群搭建 最新无人直播变现玩法 流量掘金付费进群网站源码 源码下载&#xff1a;https://download.csdn.net/download/m0_66047725/89662670 更多资源下载&#xff1a;关注我。

消化学科的领军人物陈烨教授在会议上作了《幽门螺杆菌的规范检测与质控》的专题报告

由广东省药学会主办的“第十九届消化疾病诊疗会暨胃肠疾病药物临床研究交流会”于2024年8月8日-9日在广东省深圳市召开。陈烨教授&#xff0c;作为消化学科的领军人物、中华医学会消化病学分会的常务委员&#xff0c;以及全国幽门螺杆菌学组的组长&#xff0c;在会议上作了《幽…

用爬虫玩转石墨文档细解

​ ​ 您好&#xff0c;我是程序员小羊&#xff01; 前言 石墨文档是一款受欢迎的在线协作工具&#xff0c;它允许多人实时编辑和共享文档。通过爬虫技术&#xff0c;我们可以自动化地获取石墨文档中的内容&#xff0c;进行数据分析或备份。不过&#xff0c;在使用爬虫技术时&a…

Nmap扫描六种端口状态介绍

传统意义上的端口状态只有 open 或 close 两种。网络发现扫描最常用的工具是Nmap&#xff0c;可以提供更细分的端口状态&#xff0c;共计六种&#xff0c;分别为open, closed, filtered, unfiltered, open|filtered, or closed|filtered。 1. 端口状态介绍 开放的&#xff08;o…

艾多美携手三星SDS,共筑物流优化与数字化转型新篇章!

8月20日下午&#xff0c;艾多美公司董事长朴炳宽受邀出席了由山东省人民政府、韩国产业通商资源部联合主办的“山东省—韩国经贸合作交流会”。在此次盛会上&#xff0c;艾多美中国与全球领先的IT解决方案提供商三星SDS达成了具有重要里程碑意义的战略合作&#xff0c;双方将共…

安防监控EasyCVR视频监控汇聚管理平台登录1分钟之后自动退出是什么原因?

EasyCVR视频监控汇聚管理平台是一款针对大中型项目设计的跨区域网络化视频监控集中管理平台。该平台不仅具备视频资源管理、设备管理、用户管理、网络管理和安全管理等功能&#xff0c;还支持多种主流标准协议&#xff0c;如GB28181、RTSP/Onvif、RTMP、部标JT808、GA/T 1400协…

docker基本环境搭建

前面在虚拟机centos中搭建的fastdfs和minio分布式文件存储服务都是手动编译安装的&#xff0c;为了方便后续学习&#xff0c;本地开发环境的中间件服务部署&#xff0c;我们将交给docker来部署。下面先进行docker环境搭建。 后续相关教程&#xff08;待更新&#xff09;&#…

QT中通过TCP协议多线程的文件传输(客户端)

首先&#xff0c;新建一个项目&#xff0c;我命名为了SendFileClient 首先我们要在pro文件中 代码第一行加入network的后缀 一、窗口搭建 如图所示&#xff0c;在第一个QWidget中让客户端输入IP&#xff0c;端口号 连接服务器 第二个Qwidget 设置一个LineEdit,供客户端选择要…

二维中,若直线上两点q1和q2,输入一个点P1,求P1在直线上的垂点

一、计算过程 在二维空间中&#xff0c;若给定直线上两点Q1和Q2以及一个点P1&#xff0c;要求出点P1在直线上的垂点&#xff0c;可以通过以下步骤进行&#xff1a; ‌1、判断点P1是否在直线q1-q2上‌&#xff1a; 首先&#xff0c;需要判断点P1是否位于直线Q1-Q2上。这可以通过…

【Linux】实现三个迷你小程序(倒计时,旋转指针,进度条)

&#x1f984;个人主页:修修修也 &#x1f38f;所属专栏:Linux ⚙️操作环境:Xshell (操作系统:CentOS 7.9 64位) 目录 &#x1f4cc;倒计时小程序 &#x1f38f;项目效果展示 &#x1f38f;项目实现思路 &#x1f38f;项目完整代码 &#x1f4cc;旋转指针小程序 &#x…

视频监控接入汇聚平台如何根据客户要求定制资源树结构和资源的任意排序

目录 一、需求描述 1.1视频监控资源树 1.2客户要求 二、市场上产品常用处理方法 2.1 常用处理方法 &#xff08;1&#xff09;按笔画数排序 &#xff08;2&#xff09;按拼音排序 &#xff08;3&#xff09;按字典序排序 &#xff08;4&#xff09;按首字母排序 2.2 …

使用预训练的 ONNX 格式的目标检测模型(基于 YOLOv8n-pose)姿态监测

具体步骤如下&#xff1a; 加载图像&#xff1a; 从指定路径读取一张图像&#xff08;这里假设图像名为bus.jpg&#xff09;。将图像从 BGR 颜色空间转换为 RGB 颜色空间。 图像预处理&#xff1a; 计算图像的高度、宽度&#xff0c;并确定其中的最大值作为新图像的边长。创建一…

C语言新手小白详细教程:冒泡排序

&#x1f30f;个人博客主页&#xff1a;意疏-CSDN博客 希望文章能够给到初学的你一些启发&#xff5e; 如果觉得文章对你有帮助的话&#xff0c;点赞 关注 收藏支持一下笔者吧&#xff5e; 阅读指南&#xff1a; 开篇说明冒泡排序简介冒泡排序代码 开篇说明 本文我们来介绍冒…

编码器精度

系列文章目录 1.元件基础 2.电路设计 3.PCB设计 4.元件焊接 5.板子调试 6.程序设计 7.算法学习 8.编写exe 9.检测标准 10.项目举例 11.职业规划 文章目录 前言一、影响因素二、编码器精度三、位置因素四、环境因素五、磁编码器 前言 送给大学毕业后找不到奋斗方向的你&…