从0实现基于Linux socket聊天室-多线程服务器模型-1

2023-10-29

前言

Socket在实际系统程序开发当中,应用非常广泛,也非常重要。实际应用中服务器经常需要支持多个客户端连接,实现高并发服务器模型显得尤为重要。高并发服务器从简单的循环服务器模型处理少量网络并发请求,演进到解决C10K,C10M问题的高并发服务器模型。

C/S架构

服务器-客户机,即Client-Server(C/S)结构。C/S结构通常采取两层结构。服务器负责数据的管理,客户机负责完成与用户的交互任务。

在C/S结构中,应用程序分为两部分:服务器部分和客户机部分。服务器部分是多个用户共享的信息与功能,执行后台服务,如控制共享数据库的操作等;客户机部分为用户所专有,负责执行前台功能,在出错提示、在线帮助等方面都有强大的功能,并且可以在子程序间自由切换。
C/S架构
如上图所示:这是基于套接字实现客户端和服务器相连的函数调用关系,socket API资料比较多,本文不再过多叙述。

pthread线程库:(POSIX)

pthread线程库是Linux下比较常用的一个线程库,关于他的用法和特性大家可以自行搜索相关文章,下面只简单介绍他的用法和编译。

线程标识

线程有ID, 但不是系统唯一, 而是进程环境中唯一有效.
线程的句柄是pthread_t类型, 该类型不能作为整数处理, 而是一个结构.
下面介绍两个函数:

头文件: <pthread.h>
原型: int pthread_equal(pthread_t tid1, pthread_t tid2);
返回值: 相等返回非0, 不相等返回0.
说明: 比较两个线程ID是否相等.

头文件: <pthread.h>
原型: pthread_t pthread_self();
返回值: 返回调用线程的线程ID.

线程创建

在执行中创建一个线程, 可以为该线程分配它需要做的工作(线程执行函数), 该线程共享进程的资源. 创建线程的函数pthread_create()

头文件: <pthread.h>
原型: int pthread_create(pthread_t *restrict tidp, const pthread_attr_t *restrict attr, void *(start_rtn)(void), void *restrict arg);
返回值: 成功则返回0, 否则返回错误编号.
参数:
tidp: 指向新创建线程ID的变量, 作为函数的输出.
attr: 用于定制各种不同的线程属性, NULL为默认属性(见下).
start_rtn: 函数指针, 为线程开始执行的函数名.该函数可以返回一个void *类型的返回值,
而这个返回值也可以是其他类型,并由 pthread_join()获取
arg: 函数的唯一无类型(void)指针参数, 如要传多个参数, 可以用结构封装.

编译

因为pthread的库不是linux系统的库,所以在进行编译的时候要加上     -lpthread
# gcc filename -lpthread  //默认情况下gcc使用c库,要使用额外的库要这样选择使用的库

常见的网络服务器模型

本文结合自己的理解,主要以TCP为例,总结了几种常见的网络服务器模型的实现方式,并最终实现一个简单的命令行聊天室。

单进程循环

单线进程循环原理就是主进程没和客户端通信,客户端都要先连接服务器,服务器接受一个客户端连接后从客户端读取数据,然后处理并将处理的结果返还给客户端,然后再接受下一个客户端的连接请求。

优点
单线程循环模型优点是简单、易于实现,没有同步、加锁这些麻烦事,也没有这些开销。

缺点

  1. 阻塞模型,网络请求串行处理;
  2. 没有利用多核cpu的优势,网络请求串行处理;
  3. 无法支持同时多个客户端连接;
  4. 程序串行操作,服务器无法实现同时收发数据。
    在这里插入图片描述

单线程IO复用

linux高并发服务器中常用epoll作为IO复用机制。线程将需要处理的socket读写事件都注册到epoll中,当有网络IO发生时,epoll_wait返回,线程检查并处理到来socket上的请求。

优点

  1. 实现简单, 减少锁开销,减少线程切换开销。

缺点

  1. 只能使用单核cpu,handle时间过长会导致整个服务挂死;
  2. 当有客户端数量超过一定数量后,性能会显著下降;
  3. 只适用高IO、低计算,handle处理时间短的场景。

在这里插入图片描述

多线程/多进程

多线程、多进程模型主要特点是每个网络请求由一个进程/线程处理,线程内部使用阻塞式系统调用,在线程的职能划分上,可以由一个单独的线程处理accept连接,其余线程处理具体的网络请求(收包,处理,发包);还可以多个进程单独listen、accept网络连接。

优点:

1、实现相对简单;
2、利用到CPU多核资源。

缺点:

1、线程内部还是阻塞的,举个极端的例子,如果一个线程在handle的业务逻辑中sleep了,这个线程也就挂住了。
在这里插入图片描述

多线程/多进程IO复用

多线程、多进程IO服用模型,每个子进程都监听服务,并且都使用epoll机制来处理进程的网络请求,子进程 accept() 后将创建已连接描述符,然后通过已连接描述符来与客户端通信。该机制适用于高并发的场景。

优点:

  1. 支撑较高并发。

缺点:

  1. 异步编程不直观、容易出错

在这里插入图片描述

多线程划分IO角色

多线程划分IO角色主要功能有:一个accept thread处理新连接建立;一个IO thread pool处理网络IO;一个handle thread pool处理业务逻辑。使用场景如:电销应用,thrift TThreadedSelectorServer。

优点:

  1. 按不同功能划分线程,各线程处理固定功能,效率更高
  2. 可以根据业务特点配置线程数量来性能调优

缺点:

  1. 线程间通信需要引入锁开销
  2. 逻辑较复杂,实现难度大

在这里插入图片描述

小结

上面介绍了常见的网络服务器模型,还有AIO、协程,甚至还有其他的变型,在这里不再讨论。重要的是理解每种场景中所面临的问题和每种模型的特点,设计出符合应用场景的方案才是好方案。

多线程并发服务器模型

下面我们主要讨论多线程并发服务器模型。

代码结构

并发服务器代码结构如下:

thread_func()
{
	 while(1) {
			 recv(...);
			 process(...);
			 send(...);
		}
		close(...);
}
main(
	socket(...); 
	bind(...);
	listen(...);
	while(1) { 
		accept(...);
		pthread_create();
	}
}

由上可以看出,服务器分为两部分:主线程、子线程。

主线程

main函数即主线程,它的主要任务如下:

  1. socket()创建监听套字;
  2. bind()绑定端口号和地址;
  3. listen()开启监听;
  4. accept()等待客户端的连接,
  5. 当有客户端连接时,accept()会创建一个新的套接字new_fd;
  6. 主线程会创建子线程,并将new_fd传递给子线程。

子线程

  1. 子线程函数为thread_func(),他通过new_fd处理和客户端所有的通信任务。

客户端连接服务器详细步骤

下面我们分步骤来看客户端连接服务器的分步说明。

1. 客户端连接服务器

  1. 服务器建立起监听套接字listen_fd,并初始化;
  2. 客户端创建套接字fd1;
  3. 客户端client1通过套接字fd1连接服务器的listen_fd;

在这里插入图片描述

2. 主线程创建子线程thread1

  1. server收到client1的连接请求后,accpet函数会返回一个新的套接字newfd1;
  2. 后面server与client1的通信就依赖newfd1,监听套接字listen_fd会继续监听其他客户端的连接;
  3. 主线程通过pthead_create()创建一个子线程thread1,并把newfd1传递给thread1;
  4. server与client1的通信就分别依赖newfd1、fd1。
  5. client1为了能够实时收到server发送的信息,同时还要能够从键盘上读取数据,这两个操作都是阻塞的,没有数据的时候进程会休眠,所以必须创建子线程read_thread;
  6. client1的主线负责从键盘上读取数据并发送给,子线程read_thread负责从server接受信息。

在这里插入图片描述

3. client2连接服务器

  1. 客户端client2创建套接字fd2;
  2. 通过connect函数连接server的listen_fd;
    在这里插入图片描述

4. 主线程创建子线程thread2

  1. server收到client2的连接请求后,accpet函数会返回一个新的套接字newfd2;
  2. 后面server与client2的通信就依赖newfd2,监听套接字listen_fd会继续监听其他客户端的连接;
  3. 主线程通过pthead_create()创建一个子线程thread2,并把newfd2传递给thread2;
  4. server与client1的通信就分别依赖newfd2、fd2。
  5. 同样client2为了能够实时收到server发送的信息,同时还要能够从键盘上读取数据必须创建子线程read_thread;
  6. client1的主线负责从键盘上读取数据并发送给,子线程read_thread负责从server接受信息。

在这里插入图片描述

由上图可见,每一个客户端连接server后,server都要创建一个专门的thread负责和该客户端的通信;每一个客户端和server都有一对固定的fd组合用于连接。

实例

好了,理论讲完了,根据一口君的惯例,也继承祖师爷的教诲:talk is cheap,show you my code.不上代码,只写理论的文章都是在耍流氓。

本例的主要功能描述如下:

  1. 实现多个客户端可以同时连接服务器;
  2. 客户端可以实现独立的收发数据;
  3. 客户端发送数据给服务器后,服务器会将数据原封不动返回给客户端。
服务器端
/*********************************************
           服务器程序  TCPServer.c  
           公众号:一口Linux
*********************************************/
#include <stdio.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <errno.h>
#include <string.h>
#include <pthread.h>
#include <stdlib.h>

#define RECVBUFSIZE 2048
void *rec_func(void *arg)
{
	int sockfd,new_fd,nbytes;
	char buffer[RECVBUFSIZE];
	int i;
	new_fd = *((int *) arg);
	free(arg);	
	
	while(1)
	{
		if((nbytes=recv(new_fd,buffer, RECVBUFSIZE,0))==-1)
		{
			fprintf(stderr,"Read Error:%s\n",strerror(errno));
			exit(1);
		}
		if(nbytes == -1)
		{//客户端出错了 返回值-1
			close(new_fd);
			break;			
		}
		if(nbytes == 0)
		{//客户端主动断开连接,返回值是0
			close(new_fd);
			break;
		}
		buffer[nbytes]='\0';	
		printf("I have received:%s\n",buffer);	
		
		
		if(send(new_fd,buffer,strlen(buffer),0)==-1)
		{
			fprintf(stderr,"Write Error:%s\n",strerror(errno));
			exit(1);
		}
			
	}

}

int main(int argc, char *argv[])
{
	char buffer[RECVBUFSIZE];
	int sockfd,new_fd,nbytes;
	struct sockaddr_in server_addr;
	struct sockaddr_in client_addr;
	int sin_size,portnumber;
	char hello[]="Hello! Socket communication world!\n";
	pthread_t tid;
	int *pconnsocke = NULL;
	int ret,i;
	
	if(argc!=2)
	{
		fprintf(stderr,"Usage:%s portnumber\a\n",argv[0]);
		exit(1);
	}
	/*端口号不对,退出*/
	if((portnumber=atoi(argv[1]))<0)
	{
		fprintf(stderr,"Usage:%s portnumber\a\n",argv[0]);
		exit(1);
	}

	/*服务器端开始建立socket描述符  sockfd用于监听*/
	if((sockfd=socket(AF_INET,SOCK_STREAM,0))==-1)  
	{
		fprintf(stderr,"Socket error:%s\n\a",strerror(errno));
		exit(1);
	}
 
	/*服务器端填充 sockaddr结构*/ 
	bzero(&server_addr,sizeof(struct sockaddr_in));
	server_addr.sin_family     =AF_INET;
	/*自动填充主机IP*/
	server_addr.sin_addr.s_addr=htonl(INADDR_ANY);//自动获取网卡地址
	server_addr.sin_port       =htons(portnumber);
 
	/*捆绑sockfd描述符*/ 
	if(bind(sockfd,(struct sockaddr *)(&server_addr),sizeof(struct sockaddr))==-1)
	{
		fprintf(stderr,"Bind error:%s\n\a",strerror(errno));
		exit(1);
	}
 
	/*监听sockfd描述符*/
	if(listen(sockfd, 10)==-1)
	{
		fprintf(stderr,"Listen error:%s\n\a",strerror(errno));
		exit(1);
	}

	while(1)
	{
		/*服务器阻塞,直到客户程序建立连接*/
		sin_size=sizeof(struct sockaddr_in);
		if((new_fd = accept(sockfd,(struct sockaddr *)&client_addr,&sin_size))==-1)
		{
			fprintf(stderr,"Accept error:%s\n\a",strerror(errno));
			exit(1);
		}
		
		pconnsocke = (int *) malloc(sizeof(int));
		*pconnsocke = new_fd;
		
		ret = pthread_create(&tid, NULL, rec_func, (void *) pconnsocke);
		if (ret < 0) 
		{
			perror("pthread_create err");
			return -1;
		}	
	}
	//close(sockfd);
	exit(0);
}

客户端
/*********************************************
           服务器程序  TCPServer.c  
           公众号:一口Linux
*********************************************/
#include <stdio.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <errno.h>
#include <string.h>
#include <pthread.h>
#include <stdlib.h>
#define RECVBUFSIZE 1024

void *func(void *arg)
{
	int sockfd,new_fd,nbytes;
	char buffer[RECVBUFSIZE];
	
	new_fd = *((int *) arg);
	free(arg);
	
	while(1)
	{
		if((nbytes=recv(new_fd,buffer, RECVBUFSIZE,0))==-1)
		{
			fprintf(stderr,"Read Error:%s\n",strerror(errno));
			exit(1);
		}
		buffer[nbytes]='\0';
		printf("I have received:%s\n",buffer);	
	}

}

int main(int argc, char *argv[])
{
	int sockfd;
	char buffer[RECVBUFSIZE];
	struct sockaddr_in server_addr;
	struct hostent *host;
	int portnumber,nbytes;	
	pthread_t tid;
	int *pconnsocke = NULL;
	int ret;
	
	//检测参数个数
	if(argc!=3)
	{
		fprintf(stderr,"Usage:%s hostname portnumber\a\n",argv[0]);
		exit(1);
	}
	//argv2 存放的是端口号 ,读取该端口,转换成整型变量
	if((portnumber=atoi(argv[2]))<0)
	{
		fprintf(stderr,"Usage:%s hostname portnumber\a\n",argv[0]);
		exit(1);
	}
	//创建一个 套接子
	if((sockfd=socket(AF_INET,SOCK_STREAM,0))==-1)
	{
		fprintf(stderr,"Socket Error:%s\a\n",strerror(errno));
		exit(1);
	}

	//填充结构体,ip和port必须是服务器的
	bzero(&server_addr,sizeof(server_addr));
	server_addr.sin_family=AF_INET;
	server_addr.sin_port=htons(portnumber);
	server_addr.sin_addr.s_addr = inet_addr(argv[1]);//argv【1】 是server ip地址

	/*¿Í»§³ÌÐò·¢ÆðÁ¬œÓÇëÇó*/ 
	if(connect(sockfd,(struct sockaddr *)(&server_addr),sizeof(struct sockaddr))==-1)
	{
		fprintf(stderr,"Connect Error:%s\a\n",strerror(errno));
		exit(1);
	}
	
	//创建线程
	pconnsocke = (int *) malloc(sizeof(int));
	*pconnsocke = sockfd;
	
	ret = pthread_create(&tid, NULL, func, (void *) pconnsocke);
	if (ret < 0) 
	{
		perror("pthread_create err");
		return -1;
	}	
	while(1)
	{
	#if 1
		printf("input msg:");
		scanf("%s",buffer);
		if(send(sockfd,buffer,strlen(buffer),0)==-1)
		{
			fprintf(stderr,"Write Error:%s\n",strerror(errno));
			exit(1);
		}
		#endif
	}
	close(sockfd);
	exit(0);
}

编译
编译线程,需要用到pthread库,编译命令如下:

  1. gcc s.c -o s -lpthread
  2. gcc cli.c -o c -lpthread
    先本机测试
  3. 开启一个终端 ./s 8888
  4. 再开一个终端 ./cl 127.0.0.1 8888,输入一个字符串"qqqqqqq"
  5. 再开一个终端 ./cl 127.0.0.1 8888,输入一个字符串"yikoulinux"
    在这里插入图片描述

有读者可能会注意到,server创建子线程的时候用的是以下代码:

	pconnsocke = (int *) malloc(sizeof(int));
		*pconnsocke = new_fd;
		
		ret = pthread_create(&tid, NULL, rec_func, (void *) pconnsocke);
		if (ret < 0) 
		{
			perror("pthread_create err");
			return -1;
		}	

为什么必须要malloc一块内存专门存放这个新的套接字呢?
这个是一个很隐蔽,很多新手都容易犯的错误。下一章,我会专门给大家讲解。

本系列文章预计会更新4-5篇。最终目的是写出一个带登录注册公聊私聊等功能的聊天室。喜欢的话请收藏关注。

图片参考网络文章:https://cloud.tencent.com/developer/article/1376352

获取更多关于Linux的资料,请关注公众号「一口Linux」,回复"进群",带你加入大咖云集的技术讨论群。

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

从0实现基于Linux socket聊天室-多线程服务器模型-1 的相关文章

  • 如何在perl中使用O_ASYNC和fcntl?

    我想使用 O ASYNC 选项 当管道可以读取时 SIGIO 的处理程序将运行 但以下代码不起作用 任何人都可以帮助我吗 bin env perl use Fcntl SIG IO sub print catch SIGIO n my fl
  • 运行 shell 命令并将输出发送到文件?

    我需要能够通过 php 脚本修改我的 openvpn 身份验证文件 我已将我的 http 用户设置为免通 sudoer 因为这台机器仅在我的家庭网络中可用 我目前有以下命令 echo shell exec sudo echo usernam
  • pthread_self() 返回的线程 ID 与调用 gettid(2) 返回的内核线程 ID 不同

    这句话来自于pthread self 的手册页 http linux die net man 3 pthread self 那么 我应该根据什么来决定是否应该使用pthread self or gettid确定哪个线程正在运行该函数 两者都
  • Ctrl-p 和 Ctrl-n 在 Docker 下表现异常

    For the life of me I can t seem to figure out why ctrl p and ctrl n don t work like they re supposed to under the Docker
  • 在嵌入式系统上将内核控制台发送到哪里?

    我正在开发一个嵌入式系统 该系统当前通过串行端口 1 上的控制台输出启动 Linux 使用启动加载程序中的控制台启动参数 然而 最终我们将使用这个串行端口 内核控制台输出的最佳解决方案是什么 dev null 能否以某种方式将其放在 pty
  • 亚马逊 Linux - 安装 openjdk-debuginfo?

    我试图使用jstack在 ec2 实例上amazon linux 所以我安装了openjdk devel包裹 sudo yum install java 1 7 0 openjdk devel x86 64 但是 jstack 引发了异常j
  • 如何从程序内部获取指向程序的特定可执行文件部分的指针? (也许是诽谤)

    我在 Linux 环境中 需要编写一个程序来检索放置在其可执行文件的某个部分中的一些数据 那么 如何从程序内部获取指向程序某个部分 通过其名称 的指针呢 我知道可以使用elf getdata 将节的索引作为参数传递给 get 和Elf Da
  • 通过名称获取进程ID

    我想在 Linux 下获得一个给定其名称的进程 ID 有没有一种简单的方法可以做到这一点 我还没有在 C 上找到任何可以轻松使用的东西 如果追求 易于使用 char buf 512 FILE cmd pipe popen pidof s p
  • 如何在 Ubuntu/Linux 发行版中安装 Tesseract-OCR 3.03?

    我和一个朋友有兴趣为 CV 项目训练 tesseract OCR 引擎 我们尝试使用一些包装器 例如 PyTesser 和 pyocr 但结果目前不如我们需要的那么准确 因此 我们希望尝试训练超立方体以更好地实现我们的目的 即识别食品标签上
  • 打印 STDOUT/STDERR 并将它们写入 Bash 中的文件?

    有没有办法让 Bash 将 STDOUT STDERR 重定向到文件 但仍然将它们打印到终端 这会将 STDOUT 和 STDERR 重定向到同一个文件 some command 2 gt 1 tee file log Example to
  • PHP mail() 函数不发送邮件

    我有一个小问题 我正在使用一个工作脚本 在我的测试帐户 共享服务器上工作 使用 mail 函数通过 PHP 发送邮件 我刚刚得到了一个专用服务器 但我还无法让该功能发挥作用 在过去的 10 个小时左右的时间里 我阅读了有关 BIND 用于
  • gpg:抱歉,根本没有请求终端 - 无法获取输入

    解密时出现以下错误 eyaml decrypt s ENC and the key goes on here gnupg quiet no secmem warning no permission warning no tty yes de
  • SIGHUP 用于重新加载配置

    根据signal 7 SIGHUP用于检测控制终端的挂起或控制进程的死亡 然而 我遇到过很多 OSS 守护进程 服务 其中SIGHUP用于启动配置的重新加载 这里有一些例子 hostapd sshd snort etc 这是实现重新加载的标
  • 进程如何知道它已收到信号

    如果我错了 请纠正我 以下是我对信号的理解 据我所知 信号生成 和信号传递有2个不同 事物 为了产生信号 操作系统只是在位数组中设置一个位 在过程控制中维护 工艺块 PCB 每一位 对应于特定信号 当设置一个位时 这意味着 该位对应的信号为
  • 使用 Vala 和 GLib 的正则表达式

    有没有一个函数 比如http php net manual en function preg match all php http php net manual en function preg match all php 使用 GLibh
  • 为什么我的代码在编译用于分析 (-pg) 时在多线程下运行比在单线程下运行慢?

    我正在写一个光线追踪器 最近 我在程序中添加了线程 以利用 i5 四核上的附加内核 奇怪的是 应用程序的调试版本现在运行速度变慢 但优化后的构建运行速度比添加线程之前更快 我将 g pg 标志传递给 gcc 以进行调试构建 并将 O3 标志
  • 删除 Python 中某些操作的 root 权限

    在我的 Python 脚本中 我执行了一些需要 root 权限的操作 我还创建并写入文件 我不想由 root 独占所有 而是由运行我的脚本的用户独占所有 通常 我使用以下命令运行脚本sudo 有办法做到上述吗 您可以使用以下方式在 uid
  • “./somescript.sh”和“. ./somescript.sh”有什么区别

    今天我按照一些说明在 Linux 中安装软件 有一个需要首先运行的脚本 它设置一些环境变量 指令告诉我执行 setup sh 但是我执行时犯了一个错误 setup sh 所以环境没有设置 最后我注意到了这一点并继续进行 我想知道这两种调用脚
  • tar.gz 和 tgz 是同一个东西吗? [关闭]

    Closed 这个问题不符合堆栈溢出指南 help closed questions 目前不接受答案 我创建了 tgz 文件tar czvf filecommand then 我最终得到了一个 tgz 文件 我想知道它和tar gz 之间的
  • PHP 日志文件颜色

    我正在编写一个 PHP 日志文件类 但我想为写入文件的行添加颜色 我遇到的问题是颜色也会改变终端的颜色 我想要实现的是仅更改写入日志文件的行的颜色 class logClass extends Singleton private funct

随机推荐

  • 微信小程序uview图标不显示

    问题 之前还好好的 图标能显示 不知何时 图标突然不显示了 百度后发现 原来是因为我引入的iconfont css 导致的css覆盖导致的 字体图标不显示一般由于字体图标的名称被覆盖导致 如下图 问题解决参考 https www kancl
  • 一看就懂!如何将Zabbix监测到的数据导出?

    前言 Zabbix是一款服务器监测应用 有良好的web界面 美中不足的是缺乏导出报表的功能 但是Zabbix同时也提供了强有力的API支持 https www zabbix com documentation 3 4 zh manual a
  • 一键分块,轻松拼图!使用Segment Anything让你的设计更加高效!

    目录 第一步 第二步 第三步 第四步 如果你是一名设计师或者摄影爱好者 那么你一定经常需要将图片分割成不同的块 以便在不同的场合下使用 例如 在网页设计中 你可能需要将图片分割成不同的块 以便在不同的区域中使用 而分块的难度不小 需要一定的
  • java easypoi导出word时添加空白页导出多组相同类型的数据

    1 需求 导出用户信息 模板如下 要求 导出多个用户信息时 另起一页 根据模板生成下一条用户信息 2 模板变化 easypoi不能直接创建空白页 下一页 但可以使用apache的poi中的document createParagraph c
  • 【系统架构】什么是集群?为什么要使用集群架构?

    什么是集群 为什么要使用集群架构 1 什么是集群 2 为什么要使用集群 2 1 高性能 2 2 价格有效性 2 3 可伸缩性 2 4 高可用性 2 5 透明性 2 6 可管理性 2 7 可编程性 3 集群的常见分类 3 1 负载均衡集群 3
  • 提速科研,AI 正引领一场新革命

    By 超神经 内容一览 五一国际劳动节假期 HyperAI超神经为大家汇总了往期 ScienceAI 相关报道 对该领域感兴趣的读者可以来一探究竟了 关键词 ScienceAI 盘点 整理 缓缓 作为近两年的技术热点 ScienceAI 在
  • mysql实现if语句判断功能的六种使用形式

    文章目录 前言 一 ifnull函数 二 nullif函数 三 if函数 四 if语句 多用于存储过程 五 if else语句 多用于存储过程 六 if elseif else语句 多用于存储过程 总结 前言 在Mysql数据库中实现判断功
  • 2023-6-1第三十二天

    reserved矜持的 预定的 专用的 reserve预定 保留 储备 discard丢弃 抛弃 ensemble整体 全体 assemble集合 收集 组装 tutorial教程 辅导材料 convention习俗 惯例 distract
  • Git cherry-pick 指令总结

    使用场景 在某些开发中 可能会有多个分支 比如 你负责的项目在生成环境出现一个致命Bug 公司项目管理领导需要你立即解决修复 并且针对本次修改拉取了一个临时分支 dev 20221109 分支 但你在解决问题时没有及时把自己本地分支由dev
  • mysql使用存储过程将一个表中的数据导入另一个表

    存储过程 本人也是第一次使用存储过程 中间遇到了很多坑 在此记录一下 希望能帮助使用的人少踩点坑 先说一下我使用存储过程的需要 其实需求很简单就是把一个表中的数据导入一个新的表中 并且删除原有表中的数据 说白了就是一个数据的备份 我是使用n
  • Python实现Excel办公自动化之openpyxl

    目录 Excel安装库的对比 安装 基本操作 1 引入库 2 工作簿 创建或者加载工作簿 获取当前工作表 3 工作表 创建工作表和获取工作表 获取所有工作表 删除工作表 复制工作表 过滤和排序 使用公式 4 单元格 访问单元格 合并单元格
  • Django资料之ORM查询操作

    条件查询 models User objects filter id 1 name root 等于 models User objects filter id gt 1 name root 大于 models User objects fi
  • xilinx ip 高斯滤波

    功能 1 单axi stream 接口输入 10bit raw输入 axis输出 10bit输出 使用5x5 的卷积进行滤波 2 使用xilinx hls 编写 3 配置寄存器有 pragma HLS INTERFACE s axilite
  • Spring学习笔记:AOP

    学习内容 AOP 文章目录 学习内容 AOP 一 什么是AOP 二 AOP中的相关概念 三 使用Spring实现AOP 1 使用原生Spring API接口 2 自定义类来实现AOP 3 使用注解实现 四 AOP中的五类通知 一 什么是AO
  • PCL 1.13.0——中文显示乱码解决办法

    目录 一 问题概述 二 解决方法 一 问题概述 PCL1 13 0中使用的是VTK9 2进行点云的可视化 在使用中发现PCL1 13 0中的setWindowName 进行中文名称显示的时候会出现乱码现象 如下所示 可视化投影结果 boos
  • 特殊矩阵的压缩存储(对称矩阵,三角矩阵和三对角矩阵)

    目录 1 对阵矩阵 2 三角矩阵 3 三对角矩阵 带状矩阵 均假设数组的下标从0开始 1 对阵矩阵 定义 若对一个n阶矩阵A中的任意一个元素 a 都有a a 1 i j n 则称其为对称矩阵 存储策略 只存储主对角线 下三角区 或主对角线
  • shardingsphere的坑。。。

    前面有说到defaultDataSource设置不生效 手动修改后 此项还勉强能用 如今又遇到一个奇葩的问题 本地调试时一切正常 丢到服务器后就一直报错 重点是 只是说空指针 就是不说具体哪里有毛病 报错信息如下 Creating a ne
  • 数据库连接用法

    1 内联接 典型的联接运算 使用像 或 lt gt 之类的比较运算符 包括相等联接和自然联接 内联接使用比较运算符根据每个表共有的列的值匹配两个表中的行 例如 检索 students和courses表中学生标识号相同的所有行 2 外联接 外
  • Java编程工具哪种好?

    Java强大的功能需要依托在软件工具上实现 所以Java编程开发需要用到编程工具 市面上有哪些Java编程工具呢 那种编程工具好呢 该怎么选择呢 接下来罗列了一系列的Java编程开发工具 一 idea 这是jetbrains公司的产品 也是
  • 从0实现基于Linux socket聊天室-多线程服务器模型-1

    前言 Socket在实际系统程序开发当中 应用非常广泛 也非常重要 实际应用中服务器经常需要支持多个客户端连接 实现高并发服务器模型显得尤为重要 高并发服务器从简单的循环服务器模型处理少量网络并发请求 演进到解决C10K C10M问题的高并