当前位置: 首页 > news >正文

150 Linux 网络编程6 ,从socket 到 epoll整理。listen函数参数再研究

一 . 只能被一个client 链接 socket例子

此例子用于socket 例子,
该例子只能用于一个客户端连接server。
不能用于多个client 连接  server

socket_server_support_one_clientconnect.c


/*
此例子用于socket 例子,
该例子只能用于一个客户端连接server。
不能用于多个client 连接  server*/
#include "wrap.h"
#define SERVER_PORT 8888int main(int argc, const char* argv[]) {int lfd;// 第一步:创建 lfdlfd = Socket(AF_INET, SOCK_STREAM, 0);// 第二步: 给lfd 绑定 IP 和 PORT,这里需要将 port 和IP 从小端转成大端struct sockaddr_in sockaddrin_server;sockaddrin_server.sin_family = AF_INET;sockaddrin_server.sin_port = htons(SERVER_PORT);sockaddrin_server.sin_addr.s_addr = htonl(INADDR_ANY);Bind(lfd,(struct sockaddr *)&sockaddrin_server,sizeof(sockaddrin_server));//第三步:设定监听上限,这个监听上限的意思是:同时只能有8个客户端和此 server连接,我们这个server只能要一个客户端和我们连接,因此这个值在这里没有具体意义Listen(lfd, 8);// 第四步 :阻塞,并等待客户端连接. accept的第二个参数是传入传出参数,代表的是:成功于服务器连接的客户端的 地址结构struct sockaddr_in sockaddrin_client;socklen_t sockaddrin_clientsocklen_t = sizeof(sockaddrin_client);int  cfd = Accept(lfd, (struct sockaddr *)&sockaddrin_client, &sockaddrin_clientsocklen_t);char clientip[33] = { 0 };const char* clientipresult = inet_ntop(AF_INET, &sockaddrin_client.sin_addr.s_addr, clientip, 32);if (clientipresult == NULL) {printf("inet_ntop error\n");}else {printf("client addr port = %d,addr ip = %s \n", ntohs(sockaddrin_client.sin_port), clientipresult);}printf("client addr port = %d,addr ip = %s \n",ntohs(sockaddrin_client.sin_port), clientip);//只要客户端连接上了, 没有错误,就会走到这一步。//第五步:连接上后,server端就可以循环的 使用read函数 读取客户端发送过来的数据了。//注意的是,read函数默认是阻塞读取的char readbuffer[256] = { 0 };int readbufferlen = 0;while (1) {readbufferlen = Read(cfd, readbuffer, 256);printf("read buffer from client %s \n", readbuffer);for (int i = 0; i < readbufferlen; ++i) {readbuffer[i] = toupper(readbuffer[i]);}Write(cfd, readbuffer, readbufferlen);Write(STDOUT_FILENO, readbuffer, readbufferlen);}
}

wrap.h

#ifndef __WRAP_H_
#define __WRAP_H_#include <stdlib.h>
#include <stdio.h>
#include <unistd.h>
#include <errno.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <ctype.h>
#include <signal.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <pthread.h>
#include <netinet/ip.h> 
#include <strings.h>void perr_exit(const char* s);
int Accept(int fd, struct sockaddr* sa, socklen_t* salenptr);
int Bind(int fd, const struct sockaddr* sa, socklen_t salen);
int Connect(int fd, const struct sockaddr* sa, socklen_t salen);
int Listen(int fd, int backlog);
int Socket(int family, int type, int protocol);
ssize_t Read(int fd, void* ptr, size_t nbytes);
ssize_t Write(int fd, const void* ptr, size_t nbytes);
int Close(int fd);
ssize_t Readn(int fd, void* vptr, size_t n);
ssize_t Writen(int fd, const void* vptr, size_t n);
static ssize_t my_read(int fd, char* ptr);
ssize_t Readline(int fd, void* vptr, size_t maxlen);
int tcp4bind(short port, const char* IP);#endif

wrap.c

#include "wrap.h"void perr_exit(const char* s)
{perror(s);exit(-1);
}int Accept(int fd, struct sockaddr* sa, socklen_t* salenptr)
{int n;again:if ((n = accept(fd, sa, salenptr)) < 0) {if ((errno == ECONNABORTED) || (errno == EINTR))goto again;elseperr_exit("accept error");}return n;
}int Bind(int fd, const struct sockaddr* sa, socklen_t salen)
{int n;if ((n = bind(fd, sa, salen)) < 0)perr_exit("bind error");return n;
}int Connect(int fd, const struct sockaddr* sa, socklen_t salen)
{int n;if ((n = connect(fd, sa, salen)) < 0)perr_exit("connect error");return n;
}int Listen(int fd, int backlog)
{int n;if ((n = listen(fd, backlog)) < 0)perr_exit("listen error");return n;
}int Socket(int family, int type, int protocol)
{int n;if ((n = socket(family, type, protocol)) < 0)perr_exit("socket error");return n;
}ssize_t Read(int fd, void* ptr, size_t nbytes)
{ssize_t n;again:if ((n = read(fd, ptr, nbytes)) == -1) {if (errno == EINTR)goto again;elsereturn -1;}return n;
}ssize_t Write(int fd, const void* ptr, size_t nbytes)
{ssize_t n;again:if ((n = write(fd, ptr, nbytes)) == -1) {if (errno == EINTR)goto again;elsereturn -1;}return n;
}int Close(int fd)
{int n;if ((n = close(fd)) == -1)perr_exit("close error");return n;
}/*参三: 应该读取的字节数*/
ssize_t Readn(int fd, void* vptr, size_t n)
{size_t  nleft;              //usigned int 剩余未读取的字节数ssize_t nread;              //int 实际读到的字节数char* ptr;ptr = vptr;nleft = n;while (nleft > 0) {if ((nread = read(fd, ptr, nleft)) < 0) {if (errno == EINTR)nread = 0;elsereturn -1;}else if (nread == 0)break;nleft -= nread;ptr += nread;}return n - nleft;
}ssize_t Writen(int fd, const void* vptr, size_t n)
{size_t nleft;ssize_t nwritten;const char* ptr;ptr = vptr;nleft = n;while (nleft > 0) {if ((nwritten = write(fd, ptr, nleft)) <= 0) {if (nwritten < 0 && errno == EINTR)nwritten = 0;elsereturn -1;}nleft -= nwritten;ptr += nwritten;}return n;
}static ssize_t my_read(int fd, char* ptr)
{static int read_cnt;static char* read_ptr;static char read_buf[100];if (read_cnt <= 0) {again:if ((read_cnt = read(fd, read_buf, sizeof(read_buf))) < 0) {if (errno == EINTR)goto again;return -1;}else if (read_cnt == 0)return 0;read_ptr = read_buf;}read_cnt--;*ptr = *read_ptr++;return 1;
}ssize_t Readline(int fd, void* vptr, size_t maxlen)
{ssize_t n, rc;char    c, * ptr;ptr = vptr;for (n = 1; n < maxlen; n++) {if ((rc = my_read(fd, &c)) == 1) {*ptr++ = c;if (c == '\n')break;}else if (rc == 0) {*ptr = 0;return n - 1;}elsereturn -1;}*ptr = 0;return n;
}int tcp4bind(short port, const char* IP)
{struct sockaddr_in serv_addr;int lfd = Socket(AF_INET, SOCK_STREAM, 0);bzero(&serv_addr, sizeof(serv_addr));if (IP == NULL) {//如果这样使用 0.0.0.0,任意ip将可以连接serv_addr.sin_addr.s_addr = INADDR_ANY;}else {if (inet_pton(AF_INET, IP, &serv_addr.sin_addr.s_addr) <= 0) {perror(IP);//转换失败exit(1);}}serv_addr.sin_family = AF_INET;serv_addr.sin_port = htons(port);//端口复用代码,在bind之前int opt = 1;setsockopt(lfd, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof(opt));Bind(lfd, (struct sockaddr*)&serv_addr, sizeof(serv_addr));return lfd;
}

编译命令

gcc socket_server_support_one_clientconnect.c wrap.c -o socket_server_support_one_clientconnect.out
这里使用的是 gcc build, 如果使用g++,则会有build error,因为g++的语法检查,要比 gcc 严格一些。
error 信息如下g++ socket_server_support_one_clientconnect.c wrap.c -o socket_server_support_one_clientconnect.out
wrap.c: In function ‘ssize_t Readn(int, void*, size_t)’:
wrap.c:111:8: error: invalid conversion from ‘void*’ to ‘char*’ [-fpermissive]ptr = vptr;^~~~
wrap.c: In function ‘ssize_t Writen(int, const void*, size_t)’:
wrap.c:136:8: error: invalid conversion from ‘const void*’ to ‘const char*’ [-fpermissive]ptr = vptr;^~~~
wrap.c: In function ‘ssize_t Readline(int, void*, size_t)’:
wrap.c:180:8: error: invalid conversion from ‘void*’ to ‘char*’ [-fpermissive]ptr = vptr;

二 . 可以被多个 client 链接 socket 例子,使用 父子进程完成。

2.1 添加 将 SIGCHLD 信号屏蔽

    //信号屏蔽是指进程主动阻止某些信号的递达,直到屏蔽解除,被屏蔽的信号在屏蔽期间仍然会产生,但不会被处理,直到屏蔽解除后才会被处理,信号屏蔽通常用于保护临界区代码,防止信号处理程序与普通代码同时执行导致的数据不一致问题。
 

            sigset_t set;
            sigemptyset(&set);
            sigaddset(&set, SIGCHLD);
            sigprocmask(SIG_BLOCK, &set, NULL);

2.2 端口复用

        server IP 和 port可以复用

        setsockopt(lfd, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof(opt));
 

2.3 父进程完成的任务有:

        2.3.1.负责监听lfd功能.因此先要关闭 cfd

        2.3.2 父进程还需要回收子进程,以避免子进程完成后变成僵尸进程

                sigaction 在windows 上使用vs2019 编写linux代码,即使添加了#include <signal.h>,也无法识别,原因未知,但是在 linux环境下 build 是可以pass 的。

                            struct sigaction act;
                            act.sa_handler = catchchild;
                            sigemptyset(&act.sa_mask);
                            act.sa_flags = 0;
                            sigaction(SIGCHLD, &act, NULL);

        2.3.3 屏蔽解除 sigprocmask(SIG_UNBLOCK, &set, NULL);

2.4 子进程完成的任务有:

        2.4.1 子进程,负责 cfd 和 client的交换数据发送信息功能。因此先要关闭 lfd

                close(lfd);

        2.4.2 连接上后,循环的 使用read函数 读取客户端发送过来的数据了。注意的是,read函数默认是阻塞读取的
 

            char readbuffer[256] = { 0 };
            int readbufferlen = 0;

            while (1) {
                readbufferlen = Read(cfd, readbuffer, 256);
                printf("read buffer from client %s \n", readbuffer);
                for (int i = 0; i < readbufferlen; ++i) {
                    readbuffer[i] = toupper(readbuffer[i]);
                }
                Write(cfd, readbuffer, readbufferlen);
                Write(STDOUT_FILENO, readbuffer, readbufferlen);
            }

2.5 完整代码:

socket_server_support_more_clientconnect_use_fork.c


/*
此例子用于socket 例子,
该例子只能用于多个客户端连接server。基于 父子进程 实现
父进程 用于 lfd ,
子进程 用于 cfd.测试的问题1: listen(lfd,num) 监听的上限数量问题。*/
#include "wrap.h"
#define SERVER_PORT 8888void catchchild(int single) {pid_t pid;int status;while (1) {pid = waitpid(-1, &status, WNOHANG);if (pid <=0 ) {break;}else if (pid > 0) {printf("child pid = %\d exit \n",pid);if (WIFEXITED(status)) {//WIFEXITED(status)为真,说明是子线程是正常结束的。。使用WEXITSTATUS(status)  ---  获取进程退出状态 (exit的参数)printf("child normal die ,die information : %d \n", WEXITSTATUS(status));}if (WIFSIGNALED(status)) {//WIFSIGNALED(status)为真,说明子线程异常终止。。使用WTERMSIG(status) --- 取得使进程终止的那个信号的编号printf("child not normal die ,die information : %d \n", WTERMSIG(status));}}}
}int main(int argc, const char* argv[]) {//第零步:将 SIGCHLD 信号屏蔽,这样做的原因是,有可能当父进程还没有 注册 完成 SIGCHID 的时候,子进程已经执行完毕了,因此在main 函数的最前面,将信号屏蔽了//信号屏蔽是指进程主动阻止某些信号的递达,直到屏蔽解除,被屏蔽的信号在屏蔽期间仍然会产生,但不会被处理,直到屏蔽解除后才会被处理,信号屏蔽通常用于保护临界区代码,防止信号处理程序与普通代码同时执行导致的数据不一致问题。sigset_t set;sigemptyset(&set);sigaddset(&set, SIGCHLD);sigprocmask(SIG_BLOCK, &set, NULL);int lfd;// 第一步:创建 lfdlfd = Socket(AF_INET, SOCK_STREAM, 0);// 第一步 让 server IP 和 port可以复用。int opt = 1;setsockopt(lfd, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof(opt));// 第二步: 给lfd 绑定 IP 和 PORT,这里需要将 port 和IP 从小端转成大端struct sockaddr_in sockaddrin_server;sockaddrin_server.sin_family = AF_INET;sockaddrin_server.sin_port = htons(SERVER_PORT);sockaddrin_server.sin_addr.s_addr = htonl(INADDR_ANY);Bind(lfd, (struct sockaddr*)&sockaddrin_server, sizeof(sockaddrin_server));//第三步:设定监听上限,这个监听上限的意思是:同时只能有2个客户端和此 server连接,我们这个server只能要2个客户端和我们连接,测试如果有3个的客户端连接的情况下,server端发生的现象 和 client端发生的现象,因此还需要写一个客户端Listen(lfd, 2);// 第四步 :accept 阻塞监听客户端连接,并 fork出子进程。// 4.1 阻塞,并等待客户端连接. accept的第二个参数是传入传出参数,代表的是:成功于服务器连接的客户端的 地址结构struct sockaddr_in sockaddrin_client;socklen_t sockaddrin_clientsocklen_t = sizeof(sockaddrin_client);pid_t pid;while (1) {int  cfd = Accept(lfd, (struct sockaddr*)&sockaddrin_client, &sockaddrin_clientsocklen_t);char clientip[33] = { 0 };const char* clientipresult = inet_ntop(AF_INET, &sockaddrin_client.sin_addr.s_addr, clientip, 32);if (clientipresult == NULL) {printf("inet_ntop error\n");}else {printf("client addr port = %d,addr ip = %s \n", ntohs(sockaddrin_client.sin_port), clientipresult);}pid = fork();if (pid == -1) {//fork 函数的 error 处理perr_exit("fork error");}else if (pid > 0) {//4.1 父进程,负责监听lfd功能.因此先要关闭 cfdclose(cfd);//4.2 父进程还需要回收子进程,以避免子进程完成后变成僵尸进程。// 那么如何回收呢? 如果是 阻塞回收,那么while(1)的循环在父进程这里就卡在这里了// 如果是 不阻塞忙轮询 回收呢?也不行,怎么忙轮询呢?// 因此这里只能用信号捕捉函数 ,捕捉 SIGCHID 信号//struct sigaction {//	void     (*sa_handler)(int);//	void     (*sa_sigaction)(int, siginfo_t*, void*);//	sigset_t   sa_mask;//	int        sa_flags;//	void     (*sa_restorer)(void);//};//int sigaction(int signum, const struct sigaction* act,struct sigaction* oldact);struct sigaction act;act.sa_handler = catchchild;sigemptyset(&act.sa_mask);act.sa_flags = 0;sigaction(SIGCHLD, &act, NULL);//4.3 屏蔽解除sigprocmask(SIG_UNBLOCK, &set, NULL);}else if (pid == 0 ) {//4.4 子进程,负责 cfd 和 client的交换数据发送信息功能。因此先要关闭 lfdclose(lfd);//4.5 连接上后,循环的 使用read函数 读取客户端发送过来的数据了。注意的是,read函数默认是阻塞读取的char readbuffer[256] = { 0 };int readbufferlen = 0;while (1) {readbufferlen = Read(cfd, readbuffer, 256);printf("read buffer from client %s \n", readbuffer);for (int i = 0; i < readbufferlen; ++i) {readbuffer[i] = toupper(readbuffer[i]);}Write(cfd, readbuffer, readbufferlen);Write(STDOUT_FILENO, readbuffer, readbufferlen);}}}
}

2.6 编译命令

gcc wrap.c socket_server_support_more_clientconnect_use_fork.c -o socket_server_support_more_clientconnect_use_fork.out

*****关于 listen 第二个参数的 问题研究

在代码中,设定的listen(lfd,2); 那么这个参数2刚开始的自己的想法是:最多有两个客户端连接上server,但是实际测试中,用了5客户端连接,都没有问题,这说明啥呢?说明自己对 listen的第二个参数理解是不对的。
 

listen(lfd,2); 这个listen的第二个参数是啥意思呢?赋值多少比较合理呢?

我们先来看 listen参数的说明:

       The backlog argument defines the maximum length to which the  queue  of
       pending  connections  for  sockfd  may  grow.   If a connection request
       arrives when the queue is full, the client may receive an error with an
       indication  of  ECONNREFUSED  or,  if  the underlying protocol supports
       retransmission, the request may be ignored so that a later reattempt at
       connection succeeds.

  backlog :就是你设置的参数值

中文翻译就是:

backlog 参数定义了待处理连接队列的最大长度,sockfd 可能会增长。如果连接请求在队列已满时到达,客户端可能会收到带有 ECONNREFUSED 指示的错误,或者如果底层协议支持重传,该请求可能会被忽略,以便稍后重新尝试连接成功。

这两个博客说的很仔细:

https://zhuanlan.zhihu.com/p/634606981

listen()函数的第二个参数详解_listen函数的第二个参数-CSDN博客

整理核心:所以,backlog 目前的真正含义就是:

(1)在linux 2.2 内核之前,backlog是指半连接队列(syns_queue)的长度。

(2)在linux2.2及之后,backlog是指已经完全建立连接,但是还没有被应用层accept之前,socket所处全连接队列(accetp_queue)的长度。

全连接队列是什么?

全连接队列存储3次握手成功并已建立的连接,将其称为全连接队列,也可称为接收队列(Accept队列),本文中的描述将称为Accept队列或全连接队列。如下红框中所示,全连已成功建立三次握手,当前的TCP状态为ESTABLISHED,但是服务端还未Accept的队列。

要核心学习这,实际上要学习linux 内核的整体课程才能有深入的了解,这里只是对这个知识点比较迷惑,记录一下。

三. 可以被多个 client 链接 socket 例子,使用 子线程完成

socket_server_support_more_clientconnect_use_pthread.c

/*
使用线程完成 socket 并发服务器
主线程socket,bind ,listen,
在循环中,accept得到 cfd ,并创建 pthread,在pthread函数中 */#include "wrap.h"
#include <pthread.h>typedef struct c_info
{int cfd;struct sockaddr_in cliaddr;}CINFO;void* client_fun(void* arg);int main(int argc, char* argv[]) {//当 pthread_attr_t 是全局变量的时候,可以直接使用静态方式初始化,目的是 设置pthread的属性为 分离pthread_attr_t attr;pthread_attr_init(&attr);pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED);short port = 8888;int lfd = tcp4bind(port, NULL);//创建套接字 绑定 Listen(lfd, 128);struct sockaddr_in cliaddr;socklen_t len = sizeof(cliaddr);CINFO* info;while (1){int cfd = Accept(lfd, (struct sockaddr*)&cliaddr, &len);char ip[16] = "";pthread_t pthid;//将accpet后的 cfd 做为参数传递到 子线程。info = malloc(sizeof(CINFO));info->cfd = cfd;info->cliaddr = cliaddr;//在前面已经将 attr 设置为分离态了,因此不存在 主线程回收的问题pthread_create(&pthid, &attr, client_fun, info);}return 0;}//子线程启动调用函数
void* client_fun(void* arg)
{CINFO* info = (CINFO*)arg;char ip[16] = "";printf("new client ip=%s port=%d\n", inet_ntop(AF_INET, &(info->cliaddr.sin_addr.s_addr), ip, 16),ntohs(info->cliaddr.sin_port));while (1){char buf[1024] = "";int count = 0;count = read(info->cfd, buf, sizeof(buf));if (count < 0){perror("");break;}else if (count == 0){printf("client close\n");break;}else{printf("%s\n", buf);write(info->cfd, buf, count);}}close(info->cfd);//记得最后要释放info,避免内存泄漏free(info);
}

编译命令

gcc wrap.c socket_server_support_more_clientconnect_use_pthread.c -lpthread -o socket_server_support_more_clientconnect_use_pthread.out

四. select 模型

五 poll 模型

六 epoll 模型

相关文章:

150 Linux 网络编程6 ,从socket 到 epoll整理。listen函数参数再研究

一 . 只能被一个client 链接 socket例子 此例子用于socket 例子&#xff0c; 该例子只能用于一个客户端连接server。 不能用于多个client 连接 server socket_server_support_one_clientconnect.c /* 此例子用于socket 例子&#xff0c; 该例子只能用于一个客户端连接server。…...

深入浅出 SQLSugar:快速掌握高效 .NET ORM 框架

SQLSugar 是一个高效、易用的 .NET ORM 框架&#xff0c;支持多种数据库&#xff08;如 SQL Server、MySQL、PostgreSQL 等&#xff09;。它提供了丰富的功能&#xff0c;包括 CRUD 操作、事务管理、动态表名、多表联查等&#xff0c;开发者可以通过简单的链式操作实现复杂的数…...

ESP8266 NodeMCU与WS2812灯带:实现多种花样变换

在现代电子创意项目中&#xff0c;LED灯带的应用已经变得极为广泛。通过结合ESP8266 NodeMCU的强大处理能力和FastLED库的高效功能&#xff0c;我们可以轻松实现多达100种灯带变换效果。本文将详细介绍如何使用Arduino IDE编程&#xff0c;实现从基础到高级的灯光效果&#xff…...

MacOS安装Docker battery-historian

文章目录 需求安装battery-historian实测配置国内源相关文章 需求 分析Android电池耗电情况、唤醒、doze状态等都要用battery-historian&#xff0c; 在 MacOS 上安装 battery-historian&#xff0c;可以使用 Docker 进行安装runcare/battery-historian:latest。装完不需要做任…...

Linux的基本指令(上)

1.ls指令 语法&#xff1a;ls [选项] [目录或文件] 功能&#xff1a;对于⽬录&#xff0c;该命令列出该⽬录下的所有⼦⽬录与⽂件。对于⽂件&#xff0c;将列出⽂件名以及其他信息。 常用选项&#xff1a; -a 列出⽬录下的所有⽂件&#xff0c;包括以 . 开头的隐含⽂件。 -d 将…...

【Numpy核心编程攻略:Python数据处理、分析详解与科学计算】1.1 从零搭建NumPy环境:安装指南与初体验

1. 从零搭建NumPy环境&#xff1a;安装指南与初体验 NumPy核心能力图解&#xff08;架构图&#xff09; NumPy 是 Python 中用于科学计算的核心库&#xff0c;它提供了高效的多维数组对象以及用于处理这些数组的各种操作。NumPy 的核心能力可以概括为以下几个方面&#xff1a…...

ASP .NET Core 学习(.NET9)部署(一)windows

在windows部署 ASP .NET Core 的时候IIS是不二选择 一、IIS安装 不论是在window7 、w10还是Windows Server&#xff0c;都是十分简单的&#xff0c;下面以Windows10为例 打开控制面版—程序—启用或关闭Windows功能 勾选图中的两项&#xff0c;其中的子项看需求自行勾选&am…...

百日计划(2025年1月22日-4月30日,以完成ue4.0 shader抄写为目标)

目前遇到三个现象&#xff1a; 1&#xff0c;以前都是以跳槽为目标学习技术&#xff0c;但是目前工作难找&#xff0c;所以失去方向&#xff0c;有点迷茫了。 2&#xff0c;对于一项完整的内容&#xff0c;月计划时间不够用&#xff0c;如果工作上一扰乱&#xff0c;就又虎头蛇…...

AIGC视频生成模型:慕尼黑大学、NVIDIA等的Video LDMs模型

大家好&#xff0c;这里是好评笔记&#xff0c;公主号&#xff1a;Goodnote&#xff0c;专栏文章私信限时Free。本文详细介绍慕尼黑大学携手 NVIDIA 等共同推出视频生成模型 Video LDMs。NVIDIA 在 AI 领域的卓越成就家喻户晓&#xff0c;而慕尼黑大学同样不容小觑&#xff0c;…...

类与对象(中)

类的6个默认成员函数 如果一个类中什么成员都没有&#xff0c;简称为空类。 空类中真的什么都没有吗&#xff1f;并不是&#xff0c;任何类在什么都不写时&#xff0c;编译器会自动生成以下 6 个默认成员函数。默认成员函数&#xff1a;用户没有显式实现&#xff0c;编译器会生…...

如何移植ftp服务器到arm板子?

很多厂家提供的sdk&#xff0c;一般都不自带ftp服务器功能&#xff0c; 需要要发人员自己移植ftp服务器程序。 本文手把手教大家如何移植ftp server到arm板子。 环境 sdk&#xff1a;复旦微 Buildroot 2018.02.31. 解压 $ mkdir ~/vsftpd $ cp vsftpd-3.0.2.tar.gz ~/vs…...

npm常见报错整理

npm install时报UNMET PEER DEPENDENCY 现象 npm install时报UNMET PEER DEPENDENCY,且执行npm install好几遍仍报这个。 原因 不是真的缺少某个包,而是安装的依赖版本不对,警告你应该安装某一个版本。 真的缺少某个包。 解决 看了下package.json文件,我的react是有的…...

苍穹外卖—订单模块

该模块分为地址表的增删改查、用户下单、订单支付三个部分。 第一部分地址表的增删改查无非就是对于单表的增删改查&#xff0c;较基础&#xff0c;因此直接导入代码。 地址表 一个用户可以有多个地址&#xff0c;同时有一个地址为默认地址。用户还可为地址添加例如&q…...

MQ的可靠消息投递机制

确保消息在发送、传递和消费过程中不会丢失、重复消费或错乱。 1. 消息的可靠投递 消息持久化&#xff1a; 消息被发送到队列后会存储在磁盘上&#xff0c;即使消息队列崩溃&#xff0c;消息也不会丢失。例如&#xff1a;Kafka、RabbitMQ等都支持持久化消息。Kafka通过将消息存…...

视频多模态模型——视频版ViT

大家好&#xff0c;这里是好评笔记&#xff0c;公主号&#xff1a;Goodnote&#xff0c;专栏文章私信限时Free。本文详细解读多模态论文《ViViT: A Video Vision Transformer》&#xff0c;2021由google 提出用于视频处理的视觉 Transformer 模型&#xff0c;在视频多模态领域有…...

w179基于Java Web的流浪宠物管理系统的设计与实现

&#x1f64a;作者简介&#xff1a;多年一线开发工作经验&#xff0c;原创团队&#xff0c;分享技术代码帮助学生学习&#xff0c;独立完成自己的网站项目。 代码可以查看文章末尾⬇️联系方式获取&#xff0c;记得注明来意哦~&#x1f339;赠送计算机毕业设计600个选题excel文…...

MyBatis框架基础学习(1)

目录 一、MyBatis框架介绍。 &#xff08;1&#xff09;简化开发。 &#xff08;2&#xff09;持久层&#xff1f; &#xff08;3&#xff09;框架的解释&#xff01; 二、JDBC开发缺点。 &#xff08;1&#xff09;硬编码。 &#xff08;2&#xff09;操作繁琐。 三、MyBatis框…...

arm-linux平台、rk3288 SDL移植

一、所需环境资源 1、arm-linux交叉编译器&#xff0c;这里使用的是gcc-linaro-6.3.1 2、linux交叉编译环境&#xff0c;这里使用的是Ubuntu 20.04 3、sdl2源码 https://github.com/libsdl-org/SDL/archive/refs/tags/release-2.30.11.tar.gz 二、代码编译 1、解压sdl2源码…...

51单片机入门_01_单片机(MCU)概述(使用STC89C52芯片)

文章目录 1. 什么是单片机1.1 微型计算机的组成1.2 微型计算机的应用形态1.3 单板微型计算机1.4 单片机(MCU)1.4.1 单片机内部结构1.4.2 单片机应用系统的组成 1.5 80C51单片机系列1.5.1 STC公司的51单片机1.5.1 STC公司单片机的命名规则 2. 单片机的特点及应用领域2.1 单片机的…...

基础项目——扫雷(c++)

目录 前言一、环境配置二、基础框架三、关闭事件四、资源加载五、初始地图六、常量定义七、地图随机八、点击排雷九、格子类化十、 地图类化十一、 接口优化十二、 文件拆分十三、游戏重开 前言 各位小伙伴们&#xff0c;这期我们一起学习出贪吃蛇以外另一个基础的项目——扫雷…...

使用docker在3台服务器上搭建基于redis 6.x的一主两从三台均是哨兵模式

一、环境及版本说明 如果服务器已经安装了docker,则忽略此步骤,如果没有安装,则可以按照一下方式安装: 1. 在线安装(有互联网环境): 请看我这篇文章 传送阵>> 点我查看 2. 离线安装(内网环境):请看我这篇文章 传送阵>> 点我查看 说明&#xff1a;假设每台服务器已…...

树莓派超全系列教程文档--(62)使用rpicam-app通过网络流式传输视频

使用rpicam-app通过网络流式传输视频 使用 rpicam-app 通过网络流式传输视频UDPTCPRTSPlibavGStreamerRTPlibcamerasrc GStreamer 元素 文章来源&#xff1a; http://raspberry.dns8844.cn/documentation 原文网址 使用 rpicam-app 通过网络流式传输视频 本节介绍来自 rpica…...

spring:实例工厂方法获取bean

spring处理使用静态工厂方法获取bean实例&#xff0c;也可以通过实例工厂方法获取bean实例。 实例工厂方法步骤如下&#xff1a; 定义实例工厂类&#xff08;Java代码&#xff09;&#xff0c;定义实例工厂&#xff08;xml&#xff09;&#xff0c;定义调用实例工厂&#xff…...

ServerTrust 并非唯一

NSURLAuthenticationMethodServerTrust 只是 authenticationMethod 的冰山一角 要理解 NSURLAuthenticationMethodServerTrust, 首先要明白它只是 authenticationMethod 的选项之一, 并非唯一 1 先厘清概念 点说明authenticationMethodURLAuthenticationChallenge.protectionS…...

微服务商城-商品微服务

数据表 CREATE TABLE product (id bigint(20) UNSIGNED NOT NULL AUTO_INCREMENT COMMENT 商品id,cateid smallint(6) UNSIGNED NOT NULL DEFAULT 0 COMMENT 类别Id,name varchar(100) NOT NULL DEFAULT COMMENT 商品名称,subtitle varchar(200) NOT NULL DEFAULT COMMENT 商…...

工业自动化时代的精准装配革新:迁移科技3D视觉系统如何重塑机器人定位装配

AI3D视觉的工业赋能者 迁移科技成立于2017年&#xff0c;作为行业领先的3D工业相机及视觉系统供应商&#xff0c;累计完成数亿元融资。其核心技术覆盖硬件设计、算法优化及软件集成&#xff0c;通过稳定、易用、高回报的AI3D视觉系统&#xff0c;为汽车、新能源、金属制造等行…...

【笔记】WSL 中 Rust 安装与测试完整记录

#工作记录 WSL 中 Rust 安装与测试完整记录 1. 运行环境 系统&#xff1a;Ubuntu 24.04 LTS (WSL2)架构&#xff1a;x86_64 (GNU/Linux)Rust 版本&#xff1a;rustc 1.87.0 (2025-05-09)Cargo 版本&#xff1a;cargo 1.87.0 (2025-05-06) 2. 安装 Rust 2.1 使用 Rust 官方安…...

AI+无人机如何守护濒危物种?YOLOv8实现95%精准识别

【导读】 野生动物监测在理解和保护生态系统中发挥着至关重要的作用。然而&#xff0c;传统的野生动物观察方法往往耗时耗力、成本高昂且范围有限。无人机的出现为野生动物监测提供了有前景的替代方案&#xff0c;能够实现大范围覆盖并远程采集数据。尽管具备这些优势&#xf…...

uniapp 字符包含的相关方法

在uniapp中&#xff0c;如果你想检查一个字符串是否包含另一个子字符串&#xff0c;你可以使用JavaScript中的includes()方法或者indexOf()方法。这两种方法都可以达到目的&#xff0c;但它们在处理方式和返回值上有所不同。 使用includes()方法 includes()方法用于判断一个字…...

MySQL 主从同步异常处理

阅读原文&#xff1a;https://www.xiaozaoshu.top/articles/mysql-m-s-update-pk MySQL 做双主&#xff0c;遇到的这个错误&#xff1a; Could not execute Update_rows event on table ... Error_code: 1032是 MySQL 主从复制时的经典错误之一&#xff0c;通常表示&#xff…...