当前位置: 首页 > 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;这期我们一起学习出贪吃蛇以外另一个基础的项目——扫雷…...

隧道裂缝剥落病害AI识别系统

我国现有公路隧道超2.5万座&#xff0c;总里程超2.8万公里&#xff0c;其中运营超过15年的老旧隧道占比达35%。据交通运输部2025年统计&#xff0c;年均因隧道结构病害导致的交通中断超1200次&#xff0c;直接经济损失超45亿元。传统检测模式暴露四大核心痛点&#xff1a;检测周…...

DeepSeek代码质量评估实战手册:7步完成从混沌到可度量的质变跃迁

更多请点击&#xff1a; https://kaifayun.com 第一章&#xff1a;DeepSeek代码质量评估的底层逻辑与核心价值 DeepSeek代码质量评估并非简单地统计行数或检测语法错误&#xff0c;而是基于多维语义理解构建的推理系统。其底层逻辑融合了静态分析、符号执行与大语言模型生成式…...

Yokogawa AAI835-H50/K4A00模拟输入/输出模块

Yokogawa AAI835-H50/K4A00 模拟输入/输出模块产品特点&#xff1a;通道配置&#xff1a;共8个通道&#xff0c;含4路模拟输入和4路模拟输出。信号类型&#xff1a;所有通道均支持4-20mA标准电流信号。HART通信&#xff1a;支持HART协议&#xff0c;可与智能现场设备双向数字通…...

森优时铁锌维发根养黑用三个月真实效果实测:内服营养养黑的客观测评

"森优时铁锌维发根养黑用三个月真实效果实测显示&#xff0c;针对压力、熬夜引发的早白问题&#xff0c;通过内服补充毛囊所需营养的方式&#xff0c;多数使用者能感受到发根韧性提升、新生发色素沉淀改善&#xff0c;整体改善效果因人而异&#xff0c;合规的营养补充是目…...

基于ESP8266与MQTT的家庭水压自动控制系统设计与实现

1. 项目概述与核心需求解析家里水压不稳、供水时断时续&#xff0c;这大概是很多朋友都遇到过的烦心事。我所在的城市供水情况就很不理想&#xff0c;为了解决这个问题&#xff0c;我不得不自己动手&#xff0c;搭建了一套基于ESP8266微控制器的家庭水压增压与储水自动控制系统…...

ShrinkBox后门攻击:如何让自动驾驶模型“看错”距离,威胁ML-ADAS安全

1. 项目概述在自动驾驶和高级驾驶辅助系统&#xff08;ADAS&#xff09;领域&#xff0c;基于机器学习的目标检测模型&#xff0c;如YOLO系列&#xff0c;已成为感知环境、实现碰撞预警的核心组件。这些模型通过实时识别和定位道路上的车辆、行人等目标&#xff0c;为后续的距离…...

php有什么版本,php语言有几个版本

php有什么版本,php语言有几个版本PHP的大版本主要分四支&#xff1a;PHP4/PHP5/PHP6/PHP7 其中&#xff0c;PHP4由于太古老、对OO支持不力已基本被淘汰&#xff0c;请无视PHP4。 PHP6由于基本没有生产线上的应用&#xff0c;还基本只是一款概念产品&#xff0c;很多功能已在PHP…...

Unlock-Music:浏览器中一键解锁加密音乐文件的完整指南

Unlock-Music&#xff1a;浏览器中一键解锁加密音乐文件的完整指南 【免费下载链接】unlock-music 在浏览器中解锁加密的音乐文件。原仓库&#xff1a; 1. https://github.com/unlock-music/unlock-music &#xff1b;2. https://git.unlock-music.dev/um/web 项目地址: http…...

基于LSTM自编码器的家用电器功耗异常检测系统构建指南

1. 项目概述&#xff1a;从能耗洞察到智能干预我们每天都在和各种家用电器打交道&#xff0c;从清晨唤醒你的咖啡机&#xff0c;到深夜还在默默工作的路由器。你有没有想过&#xff0c;这些看似微不足道的设备&#xff0c;其背后隐藏的能耗模式&#xff0c;其实大有文章&#x…...

8款网盘直链下载助手:彻底告别限速烦恼,实现高速下载自由

8款网盘直链下载助手&#xff1a;彻底告别限速烦恼&#xff0c;实现高速下载自由 【免费下载链接】Online-disk-direct-link-download-assistant 一个基于 JavaScript 的网盘文件下载地址获取工具。基于【网盘直链下载助手】修改 &#xff0c;支持 百度网盘 / 阿里云盘 / 中国移…...