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

news2025/1/28 10:28:10

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

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

socket_server_support_one_clientconnect.c


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


*/
#include "wrap.h"
#define SERVER_PORT 8888

int main(int argc, const char* argv[]) {

	int lfd;
	// 第一步:创建 lfd
	lfd = 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;
		else
			perr_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;
		else
			return -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;
		else
			return -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;
			else
				return -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;
			else
				return -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;
		}
		else
			return -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 8888

void 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;
	// 第一步:创建 lfd
	lfd = 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功能.因此先要关闭 cfd
			close(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的交换数据发送信息功能。因此先要关闭 lfd
			close(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 模型

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

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

相关文章

PCIe 个人理解专栏——【2】LTSSM(Link Training and Status State Machine)

前言&#xff1a; 链路训练和状况状态机LTSSM&#xff08;Link Training and Status State Machine&#xff09;是整个链路训练和运行中状态的状态转换逻辑关系图&#xff0c;总共有11个状态。 正文&#xff1a; 包括检测&#xff08;Detect&#xff09;&#xff0c;轮询&…

《DiffIR:用于图像修复的高效扩散模型》学习笔记

paper&#xff1a;2303.09472 GitHub&#xff1a;GitHub - Zj-BinXia/DiffIR: This project is the official implementation of Diffir: Efficient diffusion model for image restoration, ICCV2023 目录 摘要 1、介绍 2、相关工作 2.1 图像恢复&#xff08;Image Rest…

[笔记] 极狐GitLab实例 : 手动备份步骤总结

官方备份文档 : 备份和恢复极狐GitLab 一. 要求 为了能够进行备份和恢复&#xff0c;请确保您系统已安装 Rsync。 如果您安装了极狐GitLab&#xff1a; 如果您使用 Omnibus 软件包&#xff0c;则无需额外操作。如果您使用源代码安装&#xff0c;您需要确定是否安装了 rsync。…

switch组件的功能与用法

文章目录 1 概念介绍2 使用方法3 示例代码 我们在上一章回中介绍了PageView这个Widget,本章回中将介绍Switch Widget.闲话休提&#xff0c;让我们一起Talk Flutter吧。 1 概念介绍 我们在这里介绍的Switch是指左右滑动的开关&#xff0c;常用来表示某项设置是打开还是关闭。Fl…

mac 电脑上安装adb命令

在Mac下配置android adb命令环境&#xff0c;配置方式如下&#xff1a; 1、下载并安装IDE &#xff08;android studio&#xff09; Android Studio官网下载链接 详细的安装连接请参考 Mac 安装Android studio 2、配置环境 在安装完成之后&#xff0c;将android的adb工具所在…

Couchbase UI: Dashboard

以下是 Couchbase UI Dashboard 页面详细介绍&#xff0c;包括页面布局和功能说明&#xff0c;帮助你更好地理解和使用。 1. 首页&#xff08;Overview&#xff09; 功能&#xff1a;提供集群的整体健康状态和性能摘要 集群状态 节点健康状况&#xff1a;绿色&#xff08;正…

[极客大挑战 2019]Knife1

题目 蚁剑直接连接密码是Syc 拿下flag flag{1d373584-fc74-4a2c-a6d4-3691314be4ab}

【Maui】提示消息的扩展

文章目录 前言一、问题描述二、解决方案三、软件开发&#xff08;源码&#xff09;3.1 消息扩展库3.2 消息提示框使用3.3 错误消息提示使用3.4 问题选择框使用 四、项目展示 前言 .NET 多平台应用 UI (.NET MAUI) 是一个跨平台框架&#xff0c;用于使用 C# 和 XAML 创建本机移…

【2025最新计算机毕业设计】基于SSM房屋租赁平台【提供源码+答辩PPT+文档+项目部署】(高质量源码,可定制,提供文档,免费部署到本地)

作者简介&#xff1a;✌CSDN新星计划导师、Java领域优质创作者、掘金/华为云/阿里云/InfoQ等平台优质作者、专注于Java技术领域和学生毕业项目实战,高校老师/讲师/同行前辈交流。✌ 主要内容&#xff1a;&#x1f31f;Java项目、Python项目、前端项目、PHP、ASP.NET、人工智能…

(开源)基于Django+Yolov8+Tensorflow的智能鸟类识别平台

1 项目简介&#xff08;开源地址在文章结尾&#xff09; 系统旨在为了帮助鸟类爱好者、学者、动物保护协会等群体更好的了解和保护鸟类动物。用户群体可以通过平台采集野外鸟类的保护动物照片和视频&#xff0c;甄别分类、实况分析鸟类保护动物&#xff0c;与全世界各地的用户&…

【转帖】eclipse-24-09版本后,怎么还原原来版本的搜索功能

【1】原贴地址&#xff1a;eclipse - 怎么还原原来版本的搜索功能_eclipse打开类型搜索类功能失效-CSDN博客 https://blog.csdn.net/sinat_32238399/article/details/145113105 【2】原文如下&#xff1a; 更新eclipse-24-09版本后之后&#xff0c;新的搜索功能&#xff08;CT…

git基础指令大全

版本控制 git管理文件夹 进入要管理的文件夹 — 进入 初始化&#xff08;提名&#xff09; git init 管理文件夹 生成版本 .git ---- git在管理文件夹时&#xff0c;版本控制的信息 生成版本 git status 检测当前文件夹下的文件状态 (检测&#xff0c;检测之后就要管理了…

Android实训九 数据存储和访问

实训9 数据存储和访问 一、【实训目的】 1、 SharedPreferences存储数据; 2、 借助Java的I/O体系实现文件的存储&#xff0c; 3、使用Android内置的轻量级数据库SQLite存储数据; 二、【实训内容】 1、实现下图所示的界面&#xff0c;实现以下功能&#xff1a; 1&#xff…

[STM32 标准库]定时器输出PWM配置流程 PWM模式解析

前言&#xff1a; 本文内容基本来自江协&#xff0c;整理起来方便日后开发使用。MCU&#xff1a;STM32F103C8T6。 一、配置流程 1、开启GPIO&#xff0c;TIM的时钟 /*开启时钟*/RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM2, ENABLE); //开启TIM2的时钟RCC_APB2PeriphClockC…

如何跨互联网adb连接到远程手机-蓝牙电话集中维护

如何跨互联网adb连接到远程手机-蓝牙电话集中维护 --ADB连接专题 一、前言 随便找一个手机&#xff0c;安装一个App并简单设置一下&#xff0c;就可以跨互联网的ADB连接到这个手机&#xff0c;从而远程操控这个手机做各种操作。你敢相信吗&#xff1f;而这正是本篇想要描述的…

将 OneLake 数据索引到 Elasticsearch - 第二部分

作者&#xff1a;来自 Elastic Gustavo Llermaly 及 Jeffrey Rengifo 本文分为两部分&#xff0c;第二部分介绍如何使用自定义连接器将 OneLake 数据索引并搜索到 Elastic 中。 在本文中&#xff0c;我们将利用第 1 部分中学到的知识来创建 OneLake 自定义 Elasticsearch 连接器…

Android Studio:视图绑定的岁月变迁(2/100)

一、博文导读 本文是基于Android Studio真实项目&#xff0c;通过解析源码了解真实应用场景&#xff0c;写文的视角和读者是同步的&#xff0c;想到看到写到&#xff0c;没有上帝视角。 前期回顾&#xff0c;本文是第二期。 private Unbinder mUnbinder; 只是声明了一个 接口…

私有包上传maven私有仓库nexus-2.9.2

一、上传 二、获取相应文件 三、最后修改自己的pom文件

Qt监控系统辅屏预览/可以同时打开4个屏幕预览/支持5x64通道预览/onvif和rtsp接入/性能好

一、前言说明 在监控系统中&#xff0c;一般主界面肯定带了多个通道比如16/64通道的画面预览&#xff0c;随着电脑性能的增强和多屏幕的发展&#xff0c;再加上现在监控摄像头数量的增加&#xff0c;越来越多的用户希望在不同的屏幕预览不同的实时画面&#xff0c;一个办法是打…

LabVIEW心音心电同步采集与实时播放

开发了一个基于LabVIEW开发的心音心电同步采集与实时播放系统。该系统可以同时采集心音和心电信号&#xff0c;并通过LabVIEW的高级功能实现这些信号的实时显示和播放。系统提升心脏疾病诊断的准确性和效率&#xff0c;使医生能够在观察心音图的同时进行听诊。 ​ 项目背景 心…