网络通信模型

news2025/7/3 18:49:01

网络IO的通信原理

        首先,对于TCP通信来说,每个TCP Socket的内核中都有一个发送缓冲区和一个接收缓冲区 接收缓冲区把数据缓存到内核,若应用进程一直没有调用Socket的read方法进行读取,那么该数据会一 直被缓存在接收缓冲区内。不管进程是否读取Socket,对端发来的数据都会经过内核接收并缓存到 Socket的内核接收缓冲区。

        read所要做的工作,就是把内核接收缓冲区中的数据复制到应用层用户的Buffer里。

        进程调用Socket的send发送数据的时候,一般情况下是将数据从应用层用户的Buffer里复制到Socket的 内核发送缓冲区,然后send就会在上层返回。换句话说,send返回时,数据不一定会被发送到对端。

        网卡中的缓冲区既不属于内核空间,也不属于用户空间。它属于硬件缓冲,允许网卡与操作系统之间有 个缓冲; 内核缓冲区在内核空间,在内存中,用于内核程序,做为读自或写往硬件的数据缓冲区; 用 户缓冲区在用户空间,在内存中,用于用户程序,做为读自或写往硬件的数据缓冲区。

        网卡芯片收到网络数据会以中断的方式通知CPU,我有数据了,存在我的硬件缓冲里了,来读我啊。 CPU收到这个中断信号后,会调用相应的驱动接口函数从网卡的硬件缓冲里把数据读到内核缓冲区,正 常情况下会向上传递给TCP/IP模块一层一层的处理。

1、BIO(阻塞IO模型)

ServerSocket和Socket,阻塞体现在两个地方:连接阻塞和IO阻塞。

        在Java中,如果要实现网络通信,我们会采用Socket套接字来完成。 Socket这不是一个协议,而是一个通信模型。其实它最初是BSD发明的,主要用来一台电脑的两个进程 间通信,然后把它用到了两台电脑的进程间通信。所以,可以把它简单理解为进程间通信,不是什么高 级的东西。主要做的事情不就是:

  •         A发包:发请求包给某个已经绑定的端口(所以我们经常会访问这样的地址182.13.15.16:1235, 1235就是端口);收到B的允许;然后正式发送;发送完了,告诉B要断开链接;收到断开允许, 马上断开,然后发送已经断开信息给B。
  •         B收包:绑定端口和IP;然后在这个端口监听;接收到A的请求,发允许给A,并做好接收准备,主 要就是清理缓存等待接收新数据;然后正式接收;接受到断开请求,允许断开;确认断开后,继续 监听其它请求。

可见,Socket其实就是I/O操作,Socket并不仅限于网络通信,在网络通信中,它涵盖了网络层、传输 层、会话层、表示层、应用层——其实这都不需要记,因为Socket通信时候用到了IP和端口,仅这两个 就表明了它用到了网络层和传输层;而且它无视多台电脑通信的系统差别,所以它涉及了表示层;一般 Socket都是基于一个应用程序的,所以会涉及到会话层和应用层。

单线程BIO

        我们通过对BIOServerSocket进行改造,关注case1和case2部分。 case1: 增加了while循环,实现重复监听 case2: 当服务端收到客户端的请求后,不直接返回,而是等待20s。

import java.io.*;
import java.net.ServerSocket;
import java.net.Socket;

public class BIOServerSocket {
	//先定义一个端口号,这个端口的值是可以自己调整的。
	static final int DEFAULT_PORT = 8080;

	public static void main(String[] args) throws IOException, InterruptedException {
		ServerSocket serverSocket = null;
		serverSocket = new ServerSocket(DEFAULT_PORT);
		System.out.println("启动服务,监听端口:" + DEFAULT_PORT);
		while (true) { //case1: 增加循环,允许循环接收请求
			Socket socket = serverSocket.accept();
			System.out.println("客户端:" + socket.getPort() + "已连接");
			BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(socket.getInputStream()));
			String clientStr = bufferedReader.readLine(); //读取一行信息
			System.out.println("客户端发了一段消息:" + clientStr);
			Thread.sleep(20000); //case2: 修改:增加等待时间
			BufferedWriter bufferedWriter = new BufferedWriter(new OutputStreamWriter(socket.getOutputStream()));
			bufferedWriter.write("我已经收到你的消息了\n");
			bufferedWriter.flush(); //清空缓冲区触发消息发送
		}
	}
}

接着,把BIOClientSocket复制两份(client1、client2),同时向BIOServerSocket发起请求。 运行后看到的现象应该是: client1先发送请求到Server端,由于Server端等待20s才返回,导致 client2的请求一直被阻塞。 这个情况会导致一个问题,如果服务端在同一个时刻只能处理一个客户端的连接,而如果一个网站同时 有1000个用户访问,那么剩下的999个用户都需要等待,而这个等待的耗时取决于前面的请求的处理时长。

基于多线程优化BIO 

为了让服务端能够同时处理更多的客户端连接,避免因为某个客户端连接阻塞导致后续请求被阻塞,于 是引入多线程技术,当引入了多线程之后,每个客户端的链接(Socket),我们可以直接给到线程池去执 行,而由于这个过程是异步的,所以并不会同步阻塞影响后续链接的监听,因此在一定程度上可以提升 服务端链接的处理数量。

import java.io.*;
import java.net.ServerSocket;
import java.net.Socket;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

/**
 * 服务器端
 **/
public class BIOServerSocketWithThread {

    static ExecutorService executorService= Executors.newFixedThreadPool(10);
    public static void main(String[] args) {
        ServerSocket serverSocket=null;
        try {
            serverSocket=new ServerSocket(8080);
            System.out.println("启动服务:监听端口:8080");
            //表示阻塞等待监听一个客户端连接,返回的socket表示连接的客户端信息
            while(true) {
                Socket socket = serverSocket.accept(); //连接阻塞
                System.out.println("客户端:" + socket.getPort());
                //IO变成了异步执行
                executorService.submit(new SocketThread(socket));
            }
        } catch (IOException e) {
            e.printStackTrace();
        }finally {
            if(serverSocket!=null){
                try {
                    serverSocket.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }
    }
}


import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.InputStreamReader;
import java.io.OutputStreamWriter;
import java.net.Socket;

/**
 * 处理socket连接
 **/
public class SocketThread implements Runnable{

    private Socket socket;

    public SocketThread(Socket socket) {
        this.socket = socket;
    }

    @Override
    public void run() {
        try {
            //inputstream是阻塞的(***)
            BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(socket.getInputStream())); //表示获取客户端的请求报文
            String clientStr = bufferedReader.readLine();
            System.out.println("收到客户端发送的消息:" + clientStr);
            BufferedWriter bufferedWriter = new BufferedWriter(new OutputStreamWriter(socket.getOutputStream()));
            bufferedWriter.write("receive a message:" + clientStr + "\n");
            bufferedWriter.flush();
        }catch (Exception e){
            e.printStackTrace();
        }finally {
            //TODO 关闭IO流
        }
    }
}

2、NIO(非阻塞IO)

连接阻塞和IO阻塞都是非阻塞。

        使用多线程的方式来解决这个问题,仍然有一个缺点,线程的数量取决于硬件配置,所以线程数量是有 限的,如果请求量比较大的时候,线程本身会收到限制从而并发量也不会太高。那怎么办呢,我们可以 采用非阻塞IO。 NIO 从JDK1.4 提出的,本意是New IO,它的出现为了弥补原本IO的不足,提供了更高效的方式,提出 一个通道(channel)的概念,在IO中它始终以流的形式对数据的传输和接受,下面我们演示一下NIO 的使用。

import java.io.IOException;
import java.net.InetSocketAddress;
import java.nio.ByteBuffer;
import java.nio.channels.ServerSocketChannel;
import java.nio.channels.SocketChannel;

public class NIOServerSocket {
    //NIO中的核心
    //channel
    //buffer
    //selector

    public static void main(String[] args) {
        try {
            ServerSocketChannel serverSocketChannel=ServerSocketChannel.open();
            serverSocketChannel.configureBlocking(false); //设置连接非阻塞
            serverSocketChannel.socket().bind(new InetSocketAddress(8080));
            while(true){
                //是非阻塞的
                SocketChannel socketChannel=serverSocketChannel.accept(); //获得一个客户端连接
//                socketChannel.configureBlocking(false);//IO非阻塞
                if(socketChannel!=null){
                    ByteBuffer byteBuffer=ByteBuffer.allocate(1024);
                    int i=socketChannel.read(byteBuffer);
                    Thread.sleep(10000);
                    byteBuffer.flip(); //反转
                    socketChannel.write(byteBuffer);
                }else{
                    Thread.sleep(1000);
                    System.out.println("连接位就绪");
                }
            }
        } catch (IOException | InterruptedException e) {
            e.printStackTrace();
        }

    }
}

所谓的NIO(非阻塞IO),其实就是取消了IO阻塞和连接阻塞,当服务端不存在阻塞的时候,就可以不 断轮询处理客户端的请求,如图4-4所示,表示NIO下的运行流程。

上述这种NIO的使用方式,仍然存在一个问题,就是客户端或者服务端需要通过一个线程不断轮询才能 获得结果,而这个轮询过程中会浪费线程资源。

多路复用IO 

我们回到NIOClientSocket中下面这段代码,当客户端通过 read 方法去读取服务端返回的数据时,如果 此时服务端数据未准备好,对于客户端来说就是一次无效的轮询。 我们能不能够设计成,当客户端调用 read 方法之后,不仅仅不阻塞,同时也不需要轮询。而是等到服 务端的数据就绪之后, 告诉客户端。然后客户端再去读取服务端返回的数据呢?

I/O多路复用的本质是通过一种机制(系统内核缓冲I/O数据),让单个进程可以监视多个文件描述符, 一旦某个描述符就绪(一般是读就绪或写就绪),能够通知程序进行相应的读写操作。

常见的IO多路复用方式有【select、poll、epoll】,都是Linux API提供的IO复用方式,那么接下来重 点讲一下select、和epoll这两个模型

  • select:进程可以通过把一个或者多个fd传递给select系统调用,进程会阻塞在select操作上,这 样select可以帮我们检测多个fd是否处于就绪状态,这个模式有两个缺点

               1.由于他能够同时监听多个文件描述符,假如说有1000个,这个时候如果其中一个fd 处 于 就绪 状态了,那么当前进程需要线性轮询所有的fd,也就是监听的fd越多,性能开销越大。

             2.同时,select在单个进程中能打开的fd是有限制的,默认是1024,对于那些需要支持单机 上万的TCP连接来说确实有点少

  • epoll:linux还提供了epoll的系统调用,epoll是基于事件驱动方式来代替顺序扫描,因此性能相 对来说更高,主要原理是,当被监听的fd中,有fd就绪时,会告知当前进程具体哪一个fd就绪,那 么当前进程只需要去从指定的fd上读取数据即可,另外,epoll所能支持的fd上线是操作系统的最 大文件句柄,这个数字要远远大于1024。【由于epoll能够通过事件告知应用进程哪个fd是可读的,所以我们也称这种IO为异步非阻塞IO, 当然它是伪异步的,因为它还需要去把数据从内核同步复制到用户空间中,真正的异步非阻塞, 应该是数据已经完全准备好了,我只需要从用户空间读就行】

I/O多路复用的好处是可以通过把多个I/O的阻塞复用到同一个select的阻塞上,从而使得系统在单线程 的情况下可以同时处理多个客户端请求。它的最大优势是系统开销小,并且不需要创建新的进程或者线 程,降低了系统的资源开销,它的整体实现思想如图所示。 

客户端请求到服务端后,此时客户端在传输数据过程中,为了避免Server端在read客户端数据过程中阻 塞,服务端会把该请求注册到Selector复路器上,服务端此时不需要等待,只需要启动一个线程,通过 selector.select()阻塞轮询复路器上就绪的channel即可,也就是说,如果某个客户端连接数据传输完 成,那么select()方法会返回就绪的channel,然后执行相关的处理即可。

单线程Reactor 模型(高性能I/O设计模式)

了解了NIO多路复用后,就有必要再和大家说一下Reactor多路复用高性能I/O设计模式,Reactor本质 上就是基于NIO多路复用机制提出的一个高性能IO设计模式,它的核心思想是把响应IO事件和业务处理 进行分离,通过一个或者多个线程来处理IO事件,然后将就绪得到事件分发到业务处理handlers线程去 异步非阻塞处理,如图所示。

Reactor模型有三个重要的组件:

  • Reactor :将I/O事件发派给对应的Handler
  • Acceptor :处理客户端连接请求
  • Handlers :执行非阻塞读/写

 最基本的单Reactor单线程模型(整体的I/O操作是由同一个线程完成的)。 其中Reactor线程,负责多路分离套接字,有新连接到来触发connect 事件之后,交由Acceptor进行处 理,有IO读写事件之后交给hanlder 处理。 Acceptor主要任务就是构建handler ,在获取到和client相关的SocketChannel之后 ,绑定到相应的 hanlder上,对应的SocketChannel有读写事件之后,基于racotor 分发,hanlder就可以处理了(所有的 IO事件都绑定到selector上,有Reactor分发)。

import java.io.IOException;
import java.net.InetSocketAddress;
import java.nio.channels.SelectionKey;
import java.nio.channels.Selector;
import java.nio.channels.ServerSocketChannel;
import java.util.Iterator;
import java.util.Set;


public class Reactor implements Runnable{

	private final Selector selector;
	private final ServerSocketChannel serverSocketChannel;

	public Reactor(int port) throws IOException {
		selector=Selector.open();
		serverSocketChannel= ServerSocketChannel.open();
		serverSocketChannel.socket().bind(new InetSocketAddress(port));
		serverSocketChannel.configureBlocking(false);
		serverSocketChannel.register(selector, SelectionKey.OP_ACCEPT,new Acceptor(selector,serverSocketChannel));
	}

	@Override
	public void run() {
		while(!Thread.interrupted()){
			try {
				selector.select();
				Set<SelectionKey> selectionKeys = selector.selectedKeys();
				Iterator<SelectionKey> iterator = selectionKeys.iterator();
				while(iterator.hasNext()){
					dispatch(iterator.next());
					iterator.remove();
				}
			} catch (IOException e) {
				e.printStackTrace();
			}
		}
	}
	private void dispatch(SelectionKey key){
		//可能拿到的对象有两个
		// Acceptor
		// Handler
		Runnable runnable=(Runnable)key.attachment();
		if(runnable!=null){
			runnable.run(); //
		}
	}
}


import java.io.IOException;
import java.nio.channels.SelectionKey;
import java.nio.channels.Selector;
import java.nio.channels.ServerSocketChannel;
import java.nio.channels.SocketChannel;

public class Acceptor implements Runnable{

	private final Selector selector;
	private final ServerSocketChannel serverSocketChannel;

	public Acceptor(Selector selector, ServerSocketChannel serverSocketChannel) {
		this.selector = selector;
		this.serverSocketChannel = serverSocketChannel;
	}

	@Override
	public void run() {
		SocketChannel channel;

		try {
			channel=serverSocketChannel.accept();//得到一个客户端连接
			System.out.println(channel.getRemoteAddress()+":收到一个客户端连接");
			channel.configureBlocking(false);
			channel.register(selector, SelectionKey.OP_READ,new Handler(channel));
		} catch (IOException e) {
			e.printStackTrace();
		}

	}
}

import java.io.IOException;
import java.nio.ByteBuffer;
import java.nio.channels.SocketChannel;

public class Handler implements Runnable{
	SocketChannel channe;

	public Handler(SocketChannel channe) {
		this.channe = channe;
	}

	@Override
	public void run() {
		System.out.println(Thread.currentThread().getName()+"------");
		ByteBuffer buffer=ByteBuffer.allocate(1024);
        /*try {
            Thread.sleep(1000000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }*/
		int len=0,total=0;
		String msg="";
		try {
			do {
				len = channe.read(buffer);
				if(len>0){
					total+=len;
					msg+=new String(buffer.array());
				}
			} while (len > buffer.capacity());
			System.out.println("total:"+total);

			//msg=表示通信传输报文
			//耗时2s
			//登录: username:password
			//ServetRequets: 请求信息
			//数据库的判断
			//返回数据,通过channel写回到客户端

			System.out.println(channe.getRemoteAddress()+": Server receive Msg:"+msg);
		}catch (Exception e){
			e.printStackTrace();
		}finally {
			if(channe!=null){
				try {
					channe.close();
				} catch (IOException e) {
					e.printStackTrace();
				}
			}
		}
	}
}

 多线程单Reactor模型

单线程Reactor这种实现方式有存在着缺点,从实例代码中可以看出,handler的执行是串行的,如果其 中一个handler处理线程阻塞将导致其他的业务处理阻塞。由于handler和reactor在同一个线程中的执 行,这也将导致新的无法接收新的请求,我们做一个小实验: 在上述Reactor代码的DispatchHandler的run方法中,增加一个Thread.sleep()。 打开多个客户端窗口连接到Reactor Server端,其中一个窗口发送一个信息后被阻塞,另外一个窗 口再发信息时由于前面的请求阻塞导致后续请求无法被处理。 为了解决这种问题,有人提出使用多线程的方式来处理业务,也就是在业务处理的地方加入线程池异步 处理,将reactor和handler在不同的线程来执行,如图4-7所示。

在多线程Reactor模型中,添加了一个工作者线程池,并将非I/O操作从Reactor线程中移出转交给工作 者线程池来执行。这样能够提高Reactor线程的I/O响应,不至于因为一些耗时的业务逻辑而延迟对后面 I/O请求的处理。

import java.io.IOException;
import java.net.InetSocketAddress;
import java.nio.channels.SelectionKey;
import java.nio.channels.Selector;
import java.nio.channels.ServerSocketChannel;
import java.util.Iterator;
import java.util.Set;

public class MultiReactor implements Runnable{

	private final Selector selector;
	private final ServerSocketChannel serverSocketChannel;

	public MultiReactor(int port) throws IOException {
		selector=Selector.open();
		serverSocketChannel= ServerSocketChannel.open();
		serverSocketChannel.socket().bind(new InetSocketAddress(port));
		serverSocketChannel.configureBlocking(false);
		serverSocketChannel.register(selector, SelectionKey.OP_ACCEPT,new MultiAcceptor(selector,serverSocketChannel));
	}

	@Override
	public void run() {
		while(!Thread.interrupted()){
			try {
				selector.select();
				Set<SelectionKey> selectionKeys = selector.selectedKeys();
				Iterator<SelectionKey> iterator = selectionKeys.iterator();
				while(iterator.hasNext()){
					dispatch(iterator.next());
					iterator.remove();
				}
			} catch (IOException e) {
				e.printStackTrace();
			}
		}
	}
	private void dispatch(SelectionKey key){
		//可能拿到的对象有两个
		// Acceptor
		// Handler
		Runnable runnable=(Runnable)key.attachment();
		if(runnable!=null){
			runnable.run(); //
		}
	}
}


import java.io.IOException;
import java.nio.channels.SelectionKey;
import java.nio.channels.Selector;
import java.nio.channels.ServerSocketChannel;
import java.nio.channels.SocketChannel;

public class MultiAcceptor implements Runnable{

	private final Selector selector;
	private final ServerSocketChannel serverSocketChannel;

	public MultiAcceptor(Selector selector, ServerSocketChannel serverSocketChannel) {
		this.selector = selector;
		this.serverSocketChannel = serverSocketChannel;
	}

	@Override
	public void run() {
		SocketChannel channel;

		try {
			channel=serverSocketChannel.accept();//得到一个客户端连接
			System.out.println(channel.getRemoteAddress()+":收到一个客户端连接");
			channel.configureBlocking(false);
			channel.register(selector, SelectionKey.OP_READ,new MutilDispatchHandler(channel));
		} catch (IOException e) {
			e.printStackTrace();
		}

	}
}


import java.io.IOException;
import java.nio.ByteBuffer;
import java.nio.channels.SocketChannel;
import java.util.concurrent.Executor;
import java.util.concurrent.Executors;

public class MutilDispatchHandler implements Runnable{

	SocketChannel channel;

	private Executor executor= Executors.newFixedThreadPool(Runtime.getRuntime().availableProcessors());

	public MutilDispatchHandler(SocketChannel channel) {
		this.channel = channel;
	}

	@Override
	public void run() {
		processor();
	}
	private void processor(){
		executor.execute(new ReaderHandler(channel));
	}
	static class ReaderHandler implements Runnable{
		private SocketChannel channel;

		public ReaderHandler(SocketChannel channel) {
			this.channel = channel;
		}

		@Override
		public void run() {
			System.out.println(Thread.currentThread().getName()+":-----");
			ByteBuffer buffer=ByteBuffer.allocate(1024);
			try {
				Thread.sleep(100000);
			} catch (InterruptedException e) {
				e.printStackTrace();
			}
			int len=0,total=0;
			String msg="";
			try {
				do {
					len = channel.read(buffer);
					if(len>0){
						total+=len;
						msg+=new String(buffer.array());
					}
				} while (len > buffer.capacity());
				System.out.println("total:"+total);

				//msg=表示通信传输报文
				//耗时2s
				//登录: username:password
				//ServetRequets: 请求信息
				//数据库的判断
				//返回数据,通过channel写回到客户端
				System.out.println(channel.getRemoteAddress()+": Server receive Msg:"+msg);
			}catch (Exception e){
				e.printStackTrace();
			}finally {
				if(channel!=null){
					try {
						channel.close();
					} catch (IOException e) {
						e.printStackTrace();
					}
				}
			}
		}
	}
}

 

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

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

相关文章

防火墙基础配置

需求&#xff1a; AR1可ping防火墙FW1的G0/0/0口 AR1、AR2、AR3在不同区域 AR1可访问AR2 实现&#xff1a; 配置各路由器的IP地址、静态路由 FW配置策略并将端口加入相关区域 防火墙代码如下&#xff1a; interface GigabitEthernet0/0/0undo shutdownip address 1.1.1.…

通俗易懂!495页看漫画学Python入门教程(全彩版)Git首发破万Star

前言 在编程的世界里&#xff0c;Python无疑是一颗璀璨的明星。从最初作为打发圣诞节闲暇时间的项目&#xff0c;到如今成为最受欢迎的程序设计语言之一&#xff0c;Python以其简洁、易学、强大的特点吸引了无数编程爱好者。然而&#xff0c;对于初学者来说&#xff0c;编程的…

高效转换,尽在掌握 —— 介绍这款免费的PDF转Word在线工具

引言 在日常工作中&#xff0c;我们经常会遇到需要将PDF文件转换成Word文档的情况。无论是为了编辑文档内容还是进行格式调整&#xff0c;一款好用的转换工具都是必不可少的。今天&#xff0c;我们将为大家推荐一款简单易用、功能强大的在线PDF转Word工具——AI智慧引擎&#…

浅谈KMP算法(c++)

目录 前缀函数应用【模板】KMP题目描述输入格式输出格式样例 #1样例输入 #1样例输出 #1 提示样例 1 解释数据规模与约定 思路AC代码 本质不同子串数 例题讲解[NOI2014] 动物园题目描述输入格式输出格式样例 #1样例输入 #1样例输出 #1 提示思路AC代码 [POI2006] OKR-Periods of …

使用 vLLM 为多个 LoRA 适配器提供服务

欢迎来到雲闪世界。使用 LoRA 适配器&#xff0c;我们可以针对某项任务或领域专门设计大型语言模型 (LLM)。适配器必须加载到 LLM 之上才能用于推理。对于某些应用&#xff0c;为用户提供多个适配器可能会很有用。例如&#xff0c;一个适配器可以执行函数调用&#xff0c;另一个…

【Android Studio】 创建第一个Android应用HelloWorld

文章目录 创建项目查看AndroidManifest.xml&#xff08;清单&#xff09;查看MainActivity.java&#xff08;Activity&#xff09;查看activity_main.xml(布局) 创建项目 查看AndroidManifest.xml&#xff08;清单&#xff09; 查看MainActivity.java&#xff08;Activity&…

HarmonyOS NEXT Developer Beta2 端云一体化 运行报错

HarmonyOS NEXT Developer Beta2 端云一体化 运行报错 1 错误提示&#xff1a;Before launch task execute failed! details: npm install failed! 如上图所示&#xff1a; 错误原因&#xff1a; 右击“cloudfunctions”目录&#xff0c;选择“Debug Cloud Functions” 或 “…

锂离子电池健康状态预测(Part1,Python)

所用数据集&#xff1a;Oxford dataset:https://ora.ox.ac.uk/objects/uuid:03ba4b01-cfed-46d3-9b1a-7d4a7bdf6fac/files/m5ac36a1e2073852e4f1f7dee647909a7 # importing libraries import matplotlib.pyplot as plt import pandas as pd import scipy.io import math impor…

TypeScript 类型断言、类型推论

类型断言 类型断言是一种 TypeScript 特性&#xff0c;用于告诉编译器将一个值视为特定的类型&#xff0c;即使编译器本身的类型推断可能不同。 类型断言并不会改变变量的实际运行时类型&#xff0c;而是在编译阶段告知TypeScript编译器开发者期望该表达式具有某种类型。 注…

RAG:如何与您的数据对话

ChatGPT 进行主题建模&#xff0c;我们的任务是分析客户对不同连锁酒店的评论&#xff0c;并确定每家酒店提到的主要主题。通过这种主题建模&#xff0c;我们知道每个客户评论的主题&#xff0c;并且可以轻松地过滤它们并进行更深入的研究。然而&#xff0c;在现实生活中&#…

PMP--冲刺--敏捷中的角色职责与3个工件--题干关键词

文章目录 敏 捷 中 的 角 色 职 责 与 3 个 工 件--题干关键词说明题目 敏 捷 中 的 角 色 职 责 与 3 个 工 件–题干关键词 角色职责 1、产品负责人&#xff1a;题干关键词 “优先级排序、与客户沟通、下次迭代做什么、接受或拒绝用户故事”。 2、Scrum Master&#xff1a;题…

AI测试入门:认识RAG(检索增强生成)

AI测试入门&#xff1a;认识RAG&#xff08;检索增强生成&#xff09; 前言1. RAG概述1.1 RAG的优势 2. RAG的工作原理2.1 信息检索2.2 上下文构建2.3 生成回答 3. RAG的应用场景4. 实现一个简单的RAG系统4.1 环境准备4.2 代码实现4.3 进一步优化 5. RAG的挑战与未来发展6. RAG…

[论文精读] StyleGAN2 论文代码理解 (上)

文章目录 一、前言二、简要介绍三、详细解析1、归一化的修改1.1生成器结构的修改1.2重新审视实例归一化(Instance normalization) 2、图像质量和生成器平滑(PPL相关内容)3、关于渐进式增长Progressive growing3.1采用替换结构(Alternative network)3.2不同分辨率的使用 4、图像…

未授权访问漏洞

一. Redis Redis 默认情况下&#xff0c;会绑定在 0.0.0.0:6379 &#xff0c;如果没有进⾏采⽤相关的策略&#xff0c;⽐如添加防 ⽕墙规则避免其他⾮信任来源 ip 访问等&#xff0c;这样将会将 Redis 服务暴露到公⽹上&#xff0c;如果在没有设 置密码认证&#xff08;⼀般为…

学习分享电商 API 接口接入指南:关键技术要点与实践

在当今数字化的商业环境中&#xff0c;电商 API 接口的接入对于企业拓展业务、提升运营效率以及优化用户体验具有重要意义。本文将详细探讨电商 API 接口接入的关键技术要点&#xff0c;并结合实践经验为您提供一份实用的接入指南。 一、电商 API 接口概述 电商 API 接口是电商…

【文献阅读】Accou2vec: A Social Bot Detection Model Based on Community Walk

Abstract Accou2vec。首先&#xff0c;为了切断人类账户和机器人账户之间的攻击边&#xff0c;利用类似深度自动编码器的非负矩阵分解社区检测算法将社交图划分为多个子图。然后设计了社区漫游规则&#xff0c;分别控制社区内部和社区间的漫游&#xff0c;同时考虑社区中的节点…

基于JSP、java、Tomcat三者的项目实战--校园交易网(3)主页--添加商品功能

技术支持&#xff1a;JAVA、JSP 服务器&#xff1a;TOMCAT 7.0.86 编程软件&#xff1a;IntelliJ IDEA 2021.1.3 x64 前文三篇登录和注册功能的实现 基于JSP、java、Tomcat、mysql三层交互的项目实战--校园交易网&#xff08;1&#xff09;-项目搭建&#xff08;前期准备工作…

cjson

文章目录 概述编译cjson_test 小结 概述 在网络传输中&#xff0c;网络数据序列化&#xff0c;常用的有那么几种&#xff0c;json&#xff0c;protobuf都是很常用的&#xff0c;这一篇来写下json。 Json常用的有几个&#xff0c;rapidjson&#xff0c;jsoncpp&#xff0c;还有…

HarmonyOS开发:路由容器Navigation的使用详解

​目录 前言路由容器NavigationNavigation组成路由跳转操作Navigation下的页面生命周期最后 前言 众所周知&#xff0c;HarmonyOS作为华为推出的新一代操作系统&#xff0c;其开发框架提供了全新的能力和组件&#xff0c;以支持跨平台应用开发&#xff0c;越来越多的开发者加…

红黑树实现详解

实践意义 在各方面&#xff0c;红黑树要比AVL树性能更好&#xff0c;用途也更广泛 map&set底层都主要靠红黑树 概念 性质 插入时&#xff0c;抽象图 cur为新插入 插入时颜色更新逻辑图 板书