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

tomcat请求数据解析过程

前面提到tomcat请求处理的io交互过程,现在开始看下经过io交互后tomcat是怎么处理请求数据的。首先到AbstractProtocol.java中的process方法,注意这个方法是在tomcat线程池分配的线程调用的。生成用来对请求字节流数据进行解析的Http11Processor。

        public SocketState process(SocketWrapperBase<S> wrapper, SocketEvent status) {......Processor processor = (Processor) wrapper.takeCurrentProcessor();......try {......if (processor == null) {processor = recycledProcessors.pop();......}if (processor == null) {processor = getProtocol().createProcessor();register(processor);......}do {state = processor.process(wrapper, status);......} while (state == SocketState.UPGRADING);}
//默认为 Http11Processor    protected Processor createProcessor() {Http11Processor processor = new Http11Processor(this, adapter);return processor;}

然后在生成的Http11Processor的service方法中对请求字节流数据进行正式的解析。

  • 请求行解析
    请求行格式如GET / HTTP/1.1,首先跳过空行,然后读取请求方式,再读取请求url,之后解析协议版本,逻辑很清晰。注意这里是在fill(false)方法中读取nio的ByteBuffer的数据。
boolean parseRequestLine(boolean keptAlive, int connectionTimeout, int keepAliveTimeout) throws IOException {// check stateif (!parsingRequestLine) {return true;}//// Skipping blank lines// 跳过空行if (parsingRequestLinePhase < 2) {do {// Read new bytes if neededif (byteBuffer.position() >= byteBuffer.limit()) {if (keptAlive) {// Haven't read any request data yet so use the keep-alive// timeout.wrapper.setReadTimeout(keepAliveTimeout);}//读取nio中ByteBuffer中数据if (!fill(false)) {// A read is pending, so no longer in initial stateparsingRequestLinePhase = 1;return false;}// At least one byte of the request has been received.// Switch to the socket timeout.wrapper.setReadTimeout(connectionTimeout);}if (!keptAlive && byteBuffer.position() == 0 && byteBuffer.limit() >= CLIENT_PREFACE_START.length) {boolean prefaceMatch = true;for (int i = 0; i < CLIENT_PREFACE_START.length && prefaceMatch; i++) {if (CLIENT_PREFACE_START[i] != byteBuffer.get(i)) {prefaceMatch = false;}}if (prefaceMatch) {// HTTP/2 preface matchedparsingRequestLinePhase = -1;return false;}}// Set the start time once we start reading data (even if it is// just skipping blank lines)if (request.getStartTime() < 0) {request.setStartTime(System.currentTimeMillis());}chr = byteBuffer.get();} while (chr == Constants.CR || chr == Constants.LF);byteBuffer.position(byteBuffer.position() - 1);parsingRequestLineStart = byteBuffer.position();parsingRequestLinePhase = 2;}//解析请求方式if (parsingRequestLinePhase == 2) {//// Reading the method name// Method name is a token//boolean space = false;while (!space) {// Read new bytes if neededif (byteBuffer.position() >= byteBuffer.limit()) {if (!fill(false)) {return false;}}// Spec says method name is a token followed by a single SP but// also be tolerant of multiple SP and/or HT.int pos = byteBuffer.position();chr = byteBuffer.get();//http请求的开头字符为请求方式,以 空格(' ')或制表('\t')结尾if (chr == Constants.SP || chr == Constants.HT) {space = true;request.method().setBytes(byteBuffer.array(), parsingRequestLineStart,pos - parsingRequestLineStart);} else if (!HttpParser.isToken(chr)) {// Avoid unknown protocol triggering an additional errorrequest.protocol().setString(Constants.HTTP_11);String invalidMethodValue = parseInvalid(parsingRequestLineStart, byteBuffer);throw new IllegalArgumentException(sm.getString("iib.invalidmethod", invalidMethodValue));}}parsingRequestLinePhase = 3;}// 去除空字符if (parsingRequestLinePhase == 3) {// Spec says single SP but also be tolerant of multiple SP and/or HTboolean space = true;while (space) {// Read new bytes if neededif (byteBuffer.position() >= byteBuffer.limit()) {if (!fill(false)) {return false;}}chr = byteBuffer.get();if (chr != Constants.SP && chr != Constants.HT) {space = false;byteBuffer.position(byteBuffer.position() - 1);}}parsingRequestLineStart = byteBuffer.position();parsingRequestLinePhase = 4;}//解析urlif (parsingRequestLinePhase == 4) {// Mark the current buffer positionint end = 0;//// Reading the URI//boolean space = false;while (!space) {// Read new bytes if neededif (byteBuffer.position() >= byteBuffer.limit()) {if (!fill(false)) {return false;}}int pos = byteBuffer.position();prevChr = chr;chr = byteBuffer.get();if (prevChr == Constants.CR && chr != Constants.LF) {// CR not followed by LF so not an HTTP/0.9 request and// therefore invalid. Trigger error handling.// Avoid unknown protocol triggering an additional errorrequest.protocol().setString(Constants.HTTP_11);String invalidRequestTarget = parseInvalid(parsingRequestLineStart, byteBuffer);throw new IllegalArgumentException(sm.getString("iib.invalidRequestTarget", invalidRequestTarget));}if (chr == Constants.SP || chr == Constants.HT) {space = true;end = pos;} else if (chr == Constants.CR) {// HTTP/0.9 style request. CR is optional. LF is not.} else if (chr == Constants.LF) {// HTTP/0.9 style request// Stop this processing loopspace = true;// Set blank protocol (indicates HTTP/0.9)request.protocol().setString("");// Skip the protocol processingparsingRequestLinePhase = 7;if (prevChr == Constants.CR) {end = pos - 1;} else {end = pos;}} else if (chr == Constants.QUESTION && parsingRequestLineQPos == -1) {parsingRequestLineQPos = pos;} else if (parsingRequestLineQPos != -1 && !httpParser.isQueryRelaxed(chr)) {// Avoid unknown protocol triggering an additional errorrequest.protocol().setString(Constants.HTTP_11);// %nn decoding will be checked at the point of decodingString invalidRequestTarget = parseInvalid(parsingRequestLineStart, byteBuffer);throw new IllegalArgumentException(sm.getString("iib.invalidRequestTarget", invalidRequestTarget));} else if (httpParser.isNotRequestTargetRelaxed(chr)) {// Avoid unknown protocol triggering an additional errorrequest.protocol().setString(Constants.HTTP_11);// This is a general check that aims to catch problems early// Detailed checking of each part of the request target will// happen in Http11Processor#prepareRequest()String invalidRequestTarget = parseInvalid(parsingRequestLineStart, byteBuffer);throw new IllegalArgumentException(sm.getString("iib.invalidRequestTarget", invalidRequestTarget));}}if (parsingRequestLineQPos >= 0) {request.queryString().setBytes(byteBuffer.array(), parsingRequestLineQPos + 1,end - parsingRequestLineQPos - 1);request.requestURI().setBytes(byteBuffer.array(), parsingRequestLineStart,parsingRequestLineQPos - parsingRequestLineStart);} else {//把解析到的url记下来request.requestURI().setBytes(byteBuffer.array(), parsingRequestLineStart,end - parsingRequestLineStart);}// HTTP/0.9 processing jumps to stage 7.// Don't want to overwrite that here.if (parsingRequestLinePhase == 4) {parsingRequestLinePhase = 5;}}if (parsingRequestLinePhase == 5) {// Spec says single SP but also be tolerant of multiple and/or HTboolean space = true;while (space) {// Read new bytes if neededif (byteBuffer.position() >= byteBuffer.limit()) {if (!fill(false)) {return false;}}byte chr = byteBuffer.get();if (chr != Constants.SP && chr != Constants.HT) {space = false;byteBuffer.position(byteBuffer.position() - 1);}}parsingRequestLineStart = byteBuffer.position();parsingRequestLinePhase = 6;// Mark the current buffer positionend = 0;}//协议版本if (parsingRequestLinePhase == 6) {//// Reading the protocol// Protocol is always "HTTP/" DIGIT "." DIGIT//while (!parsingRequestLineEol) {// Read new bytes if neededif (byteBuffer.position() >= byteBuffer.limit()) {if (!fill(false)) {return false;}}int pos = byteBuffer.position();prevChr = chr;chr = byteBuffer.get();if (chr == Constants.CR) {// Possible end of request line. Need LF next else invalid.} else if (prevChr == Constants.CR && chr == Constants.LF) {// CRLF is the standard line terminatorend = pos - 1;parsingRequestLineEol = true;} else if (chr == Constants.LF) {// LF is an optional line terminatorend = pos;parsingRequestLineEol = true;} else if (prevChr == Constants.CR || !HttpParser.isHttpProtocol(chr)) {String invalidProtocol = parseInvalid(parsingRequestLineStart, byteBuffer);throw new IllegalArgumentException(sm.getString("iib.invalidHttpProtocol", invalidProtocol));}}if (end - parsingRequestLineStart > 0) {request.protocol().setBytes(byteBuffer.array(), parsingRequestLineStart, end - parsingRequestLineStart);parsingRequestLinePhase = 7;}// If no protocol is found, the ISE below will be triggered.}if (parsingRequestLinePhase == 7) {// Parsing is complete. Return and clean-up.parsingRequestLine = false;parsingRequestLinePhase = 0;parsingRequestLineEol = false;parsingRequestLineStart = 0;return true;}throw new IllegalStateException(sm.getString("iib.invalidPhase", Integer.valueOf(parsingRequestLinePhase)));}
  • 请求头解析
    http请求头格式如name:value,解析先按行读取字符串,读取到":“则认为读到一个请求头,使用MimeHeaderField封装请求头字段,并返回当前请求头的value值到headerData.headerValue,再继续读取字节,读到”\r"为止,然后转换成字符串headerData.headerValue.setBytes(byteBuffer.array(), headerData.start,headerData.lastSignificantChar - headerData.start);保存到headerData.headerValue引用指向的MimeHeaderField对象。

private HeaderParseStatus parseHeader() throws IOException {// Read new bytes if neededif (byteBuffer.position() >= byteBuffer.limit()) {if (!fill(false)) {return HeaderParseStatus.NEED_MORE_DATA;}}prevChr = chr;chr = byteBuffer.get();if (chr == Constants.CR && prevChr != Constants.CR) {// Possible start of CRLF - process the next byte.} else if (chr == Constants.LF) {// CRLF or LF is an acceptable line terminatorreturn HeaderParseStatus.DONE;} else {if (prevChr == Constants.CR) {// Must have read two bytes (first was CR, second was not LF)byteBuffer.position(byteBuffer.position() - 2);} else {// Must have only read one bytebyteBuffer.position(byteBuffer.position() - 1);}break;}}if (headerParsePos == HeaderParsePosition.HEADER_START) {// Mark the current buffer positionheaderData.start = byteBuffer.position();headerData.lineStart = headerData.start;headerParsePos = HeaderParsePosition.HEADER_NAME;}//// Reading the header name// Header name is always US-ASCII//while (headerParsePos == HeaderParsePosition.HEADER_NAME) {// Read new bytes if neededif (byteBuffer.position() >= byteBuffer.limit()) {if (!fill(false)) { // parse headerreturn HeaderParseStatus.NEED_MORE_DATA;}}int pos = byteBuffer.position();chr = byteBuffer.get();if (chr == Constants.COLON) {if (headerData.start == pos) {// Zero length header name - not valid.// skipLine() will handle the errorreturn skipLine(false);}headerParsePos = HeaderParsePosition.HEADER_VALUE_START;headerData.headerValue = headers.addValue(byteBuffer.array(), headerData.start, pos - headerData.start);pos = byteBuffer.position();// Mark the current buffer positionheaderData.start = pos;headerData.realPos = pos;headerData.lastSignificantChar = pos;break;} else if (!HttpParser.isToken(chr)) {// Non-token characters are illegal in header names// Parsing continues so the error can be reported in contextheaderData.lastSignificantChar = pos;byteBuffer.position(byteBuffer.position() - 1);// skipLine() will handle the errorreturn skipLine(false);}// chr is next byte of header name. Convert to lowercase.if (chr >= Constants.A && chr <= Constants.Z) {byteBuffer.put(pos, (byte) (chr - Constants.LC_OFFSET));}}// Skip the line and ignore the headerif (headerParsePos == HeaderParsePosition.HEADER_SKIPLINE) {return skipLine(false);}//// Reading the header value (which can be spanned over multiple lines)//while (headerParsePos == HeaderParsePosition.HEADER_VALUE_START ||headerParsePos == HeaderParsePosition.HEADER_VALUE ||headerParsePos == HeaderParsePosition.HEADER_MULTI_LINE) {if (headerParsePos == HeaderParsePosition.HEADER_VALUE_START) {// Skipping spaceswhile (true) {// Read new bytes if neededif (byteBuffer.position() >= byteBuffer.limit()) {if (!fill(false)) {// parse header// HEADER_VALUE_STARTreturn HeaderParseStatus.NEED_MORE_DATA;}}chr = byteBuffer.get();if (chr != Constants.SP && chr != Constants.HT) {headerParsePos = HeaderParsePosition.HEADER_VALUE;byteBuffer.position(byteBuffer.position() - 1);// Avoids prevChr = chr at start of header value// parsing which causes problems when chr is CR// (in the case of an empty header value)chr = 0;break;}}}if (headerParsePos == HeaderParsePosition.HEADER_VALUE) {// Reading bytes until the end of the lineboolean eol = false;while (!eol) {// Read new bytes if neededif (byteBuffer.position() >= byteBuffer.limit()) {if (!fill(false)) {// parse header// HEADER_VALUEreturn HeaderParseStatus.NEED_MORE_DATA;}}prevChr = chr;chr = byteBuffer.get();if (chr == Constants.CR && prevChr != Constants.CR) {// CR is only permitted at the start of a CRLF sequence.// Possible start of CRLF - process the next byte.} else if (chr == Constants.LF) {// CRLF or LF is an acceptable line terminatoreol = true;} else if (prevChr == Constants.CR) {// Invalid value - also need to delete headerreturn skipLine(true);} else if (HttpParser.isControl(chr) && chr != Constants.HT) {// Invalid value - also need to delete headerreturn skipLine(true);} else if (chr == Constants.SP || chr == Constants.HT) {byteBuffer.put(headerData.realPos, chr);headerData.realPos++;} else {byteBuffer.put(headerData.realPos, chr);headerData.realPos++;headerData.lastSignificantChar = headerData.realPos;}}// Ignore whitespaces at the end of the lineheaderData.realPos = headerData.lastSignificantChar;// Checking the first character of the new line. If the character// is a LWS, then it's a multiline headerheaderParsePos = HeaderParsePosition.HEADER_MULTI_LINE;}// Read new bytes if neededif (byteBuffer.position() >= byteBuffer.limit()) {if (!fill(false)) {// parse header// HEADER_MULTI_LINEreturn HeaderParseStatus.NEED_MORE_DATA;}}byte peek = byteBuffer.get(byteBuffer.position());if (headerParsePos == HeaderParsePosition.HEADER_MULTI_LINE) {if (peek != Constants.SP && peek != Constants.HT) {headerParsePos = HeaderParsePosition.HEADER_START;break;} else {// Copying one extra space in the buffer (since there must// be at least one space inserted between the lines)byteBuffer.put(headerData.realPos, peek);headerData.realPos++;headerParsePos = HeaderParsePosition.HEADER_VALUE_START;}}}// Set the header valueheaderData.headerValue.setBytes(byteBuffer.array(), headerData.start,headerData.lastSignificantChar - headerData.start);headerData.recycle();return HeaderParseStatus.HAVE_MORE_HEADERS;}
  • 请求体解析
    请求体获取方式如下,从请求request中拿到BufferedReader,然后调用readLine就行。
        BufferedReader reader = request.getReader();String line;while ((line=reader.readLine())!=null){System.out.println(line);}

tomcat会用CoyoteReader去封装内置的inputBuffer,生成BufferedReader。

    public BufferedReader getReader() throws IOException {if (usingInputStream) {throw new IllegalStateException(sm.getString("coyoteRequest.getReader.ise"));}if (coyoteRequest.getCharacterEncoding() == null) {Context context = getContext();if (context != null) {String enc = context.getRequestCharacterEncoding();if (enc != null) {setCharacterEncoding(enc);}}}usingReader = true;inputBuffer.checkConverter();if (reader == null) {reader = new CoyoteReader(inputBuffer);}return reader;}

其实也很简答,就是到内置的缓冲区中读取数据,然后使用编码成字符串即可。

    public String readLine() throws IOException {if (lineBuffer == null) {lineBuffer = new char[MAX_LINE_LENGTH];}String result = null;int pos = 0;int end = -1;int skip = -1;StringBuilder aggregator = null;while (end < 0) {mark(MAX_LINE_LENGTH);while ((pos < MAX_LINE_LENGTH) && (end < 0)) {int nRead = read(lineBuffer, pos, MAX_LINE_LENGTH - pos);if (nRead < 0) {if (pos == 0 && aggregator == null) {return null;}end = pos;skip = pos;}for (int i = pos; (i < (pos + nRead)) && (end < 0); i++) {if (lineBuffer[i] == LINE_SEP[0]) {end = i;skip = i + 1;char nextchar;if (i == (pos + nRead - 1)) {nextchar = (char) read();} else {nextchar = lineBuffer[i + 1];}if (nextchar == LINE_SEP[1]) {skip++;}} else if (lineBuffer[i] == LINE_SEP[1]) {end = i;skip = i + 1;}}if (nRead > 0) {pos += nRead;}}if (end < 0) {if (aggregator == null) {aggregator = new StringBuilder();}aggregator.append(lineBuffer);pos = 0;} else {reset();// No need to check return value. We know there are at least skip characters available.skip(skip);}}if (aggregator == null) {result = new String(lineBuffer, 0, end);} else {aggregator.append(lineBuffer, 0, end);result = aggregator.toString();}return result;}

相关文章:

tomcat请求数据解析过程

前面提到tomcat请求处理的io交互过程&#xff0c;现在开始看下经过io交互后tomcat是怎么处理请求数据的。首先到AbstractProtocol.java中的process方法&#xff0c;注意这个方法是在tomcat线程池分配的线程调用的。生成用来对请求字节流数据进行解析的Http11Processor。 public…...

《扑克牌游戏》

描述 有一个扑克牌游戏&#xff0c;游戏规则是不断地摸牌&#xff0c;尽可能地使手上的牌的点数接近于10&#xff0c;最完美的情况是总点数为10&#xff0c;不可以超过10&#xff0c;否则就爆了。输入10张牌的点数&#xff0c;(每张点数不超过10)&#xff0c;请你输出用户应该抓…...

kali模块及字典介绍

1. 基本模块介绍 模块 类型 使用模式 功能 dmitry 信息收集 命令行 whois查询/子域名收集/端口扫描 dnmap 信息收集 命令行 用于组建分布式nmap&#xff0c;dnmap_server为服务端;dnmap_client为客户端 i…...

交换排序、归并排序、计数排序

冒泡排序&#xff1a; void BubbleSort(int* a, int n) {//第一层循环是趟数&#xff0c;第二层是交换for (int i 0; i < n-2; i){int flag 0;for (int j 0; j < n - 2 - i; j){if (a[j] > a[j 1]){swap(&a[j], &a[j 1]);flag 1;}}if (flag 0){break;…...

怎么查看 iOS ipa包 mobileprovision 改动

查看 iOS .ipa 包中的 .mobileprovision 文件&#xff08;即配置文件或描述文件&#xff09;的改动&#xff0c;可以通过以下步骤进行&#xff1a; 重命名 .ipa 文件&#xff1a;将 .ipa 文件扩展名改为 .zip。例如&#xff0c;如果文件名为 MyApp.ipa&#xff0c;则重命名为 M…...

【Unitydemo制作】音游制作—控制器与特效

&#x1f468;‍&#x1f4bb;个人主页&#xff1a;元宇宙-秩沅 &#x1f468;‍&#x1f4bb; hallo 欢迎 点赞&#x1f44d; 收藏⭐ 留言&#x1f4dd; 加关注✅! &#x1f468;‍&#x1f4bb; 本文由 秩沅 原创 &#x1f468;‍&#x1f4bb; 收录于专栏&#xff1a;就业…...

[程序员] 最近的感悟,错误处理占大头?

根据昨天分享的一个问题&#xff0c;想到的。 在代码里&#xff0c;异常处理的代码也算是占大头&#xff0c;扑面而来的就是发生错误时怎么处理的大片代码&#xff1b;而且出现问题的概率是绝对的占大头。所以&#xff0c;异常代码出现bug的概率也在不知不觉中增加&#xff01…...

vue3(一) - 结构认识

通过之前博客应该已经完成了vue脚手架的认识和创建&#xff08;地址&#xff09;&#xff0c;这节我们简单介绍一下需要使用的一些关键技术&#xff0c;后续在详细介绍 结构目录 创建脚手架时&#xff0c;我选择了TypeScript,store,route这三个选项 index.html入口 node_mo…...

数据库迁移——kettle开发01

背景&#xff1a;数据库的多种多样&#xff0c;在搭建项目之初&#xff0c;并没有详细考虑到数据库的建设&#xff0c;当增加配置不能满足业务场景需要时&#xff0c;这时候考虑到使用更高性能的数据库&#xff0c;如将MySQL更换为oracle数据库。或者在搭建新项目时&#xff0c…...

Netty: Netty中的组件

文章目录 一、EventLoop1、EventLoop2、EventLoopGroup&#xff08;1&#xff09;处理普通时间和定时任务&#xff08;2&#xff09;处理IO任务 二、Channel三、Future&Promise四、Handler&Pipeline五、ByteBuf 一、EventLoop 1、EventLoop EventLoop本质是一个单线程…...

Julia编程01:Julia语言介绍

在2020上半年&#xff0c;因为疫情无法返校&#xff0c;所以在家待了半年&#xff0c;期间学习一点了R语言、Python、Julia、linux和C语言&#xff0c;只是学习基础语法并没有项目练习&#xff0c;因此返校半年后差不多都不记得了&#xff0c;现在重新捡起Julia丰富下当时写的笔…...

二叉树顺序结构及链式结构

一.二叉树的顺序结构 1.定义&#xff1a;使用数组存储数据&#xff0c;一般使用数组只适合表示完全二叉树&#xff0c;此时不会有空间的浪费 注&#xff1a;二叉树的顺序存储在逻辑上是一颗二叉树&#xff0c;但是在物理上是一个数组&#xff0c;此时需要程序员自己想清楚调整…...

【Python】pandas连续变量分箱

路过了学校花店 荒野到海边 有一种浪漫的爱 是浪费时间 徘徊到繁华世界 才发现你背影 平凡得特别 绕过了城外边界 还是没告别 爱错过了太久 反而错得完美无缺 幸福兜了一个圈 &#x1f3b5; 林宥嘉《兜圈》 import pandas as pd import numpy as np from sklearn.model_selecti…...

Qt 打卡小程序总结

1.Qt::Alignment&#xff08;枚举类型&#xff09;用于指定控件或文本的对齐方式 Qt::AlignLeft&#xff1a;左对齐。Qt::AlignRight&#xff1a;右对齐。Qt::AlignHCenter&#xff1a;水平居中对齐。Qt::AlignTop&#xff1a;顶部对齐。Qt::AlignBottom&#xff1a;底部对齐。…...

【qt】标准项模型

标准项模型 一.使用标准型项模型1.应用场景2.界面拖放3.创建模型4.配套模型5.视图设置模型6.视图属性的设置 二.从文件中拿到数据1.文件对话框获取文件名2.创建文件对象并初始化3.打开文件对象4.创建文本流并初始化5.读取文本流6.关闭文件7.完整代码 三.为模型添加数据1.自定义…...

一文深度剖析 ColBERT

近年来&#xff0c;向量搜索领域经历了爆炸性增长&#xff0c;尤其是在大型语言模型&#xff08;LLMs&#xff09;问世后。学术界开始重点关注如何通过扩展训练数据、采用先进的训练方法和新的架构等方法来增强 embedding 向量模型。 在之前的文章中&#xff0c;我们已经深入探…...

css左右滚动互不影响

想实现左右都可以滚动&#xff0c;且互不影响。 只需要再左边的css里面 .threedlist {cursor: pointer;width: 280px;position: fixed;height: 100vh; /* 定义父容器高度 */overflow-y: auto; /* 只有在内容超过父容器高度时才出现滚动条 */} 如果想取消滚动条样式 .threedli…...

【linux-uboot移植-mmc及tftp启动-IMX6ULL】

目录 1. uboot简介2. 移植前的基本介绍&#xff1a;2.1 环境系统信息: 3. 初次编译4. 烧录编译的u-boot4.1 修改网络驱动 5. 通过命令启动linux内核5.1 通过命令手动启动mmc中的linux内核5.1.1 fatls mmc 1:15.1.2 fatload mmc 1:1 0x80800000 zImage5.1.3 fatload mmc 1:1 0x8…...

Python-温故知新

1快速打开.ipynb文件 安装好anaconda后&#xff0c;在需要打开notebook的文件夹中&#xff0c; shift键右键——打开powershell窗口——输入jupyter notebook 即可在该文件夹中打开notebook的页面&#xff1a; 2 快速查看函数用法 光标放在函数上——shift键tab 3......

绿联NAS DXP系列发布:内网穿透技术在私有云的应用分析

5月23日&#xff0c;绿联科技举行了“新一代存储方式未来已来”发布会&#xff0c;发布了绿联NAS私有云DXP系列&#xff08;包括两盘位到八盘位的九款新品&#xff09;以及由绿联科技自研的全新NAS系统UGOS Pro。此次绿联发布的DXP系列九款产品&#xff0c;共有两盘位、四盘位、…...

(LeetCode 每日一题) 3442. 奇偶频次间的最大差值 I (哈希、字符串)

题目&#xff1a;3442. 奇偶频次间的最大差值 I 思路 &#xff1a;哈希&#xff0c;时间复杂度0(n)。 用哈希表来记录每个字符串中字符的分布情况&#xff0c;哈希表这里用数组即可实现。 C版本&#xff1a; class Solution { public:int maxDifference(string s) {int a[26]…...

PHP和Node.js哪个更爽?

先说结论&#xff0c;rust完胜。 php&#xff1a;laravel&#xff0c;swoole&#xff0c;webman&#xff0c;最开始在苏宁的时候写了几年php&#xff0c;当时觉得php真的是世界上最好的语言&#xff0c;因为当初活在舒适圈里&#xff0c;不愿意跳出来&#xff0c;就好比当初活在…...

最新SpringBoot+SpringCloud+Nacos微服务框架分享

文章目录 前言一、服务规划二、架构核心1.cloud的pom2.gateway的异常handler3.gateway的filter4、admin的pom5、admin的登录核心 三、code-helper分享总结 前言 最近有个活蛮赶的&#xff0c;根据Excel列的需求预估的工时直接打骨折&#xff0c;不要问我为什么&#xff0c;主要…...

Java - Mysql数据类型对应

Mysql数据类型java数据类型备注整型INT/INTEGERint / java.lang.Integer–BIGINTlong/java.lang.Long–––浮点型FLOATfloat/java.lang.FloatDOUBLEdouble/java.lang.Double–DECIMAL/NUMERICjava.math.BigDecimal字符串型CHARjava.lang.String固定长度字符串VARCHARjava.lang…...

【单片机期末】单片机系统设计

主要内容&#xff1a;系统状态机&#xff0c;系统时基&#xff0c;系统需求分析&#xff0c;系统构建&#xff0c;系统状态流图 一、题目要求 二、绘制系统状态流图 题目&#xff1a;根据上述描述绘制系统状态流图&#xff0c;注明状态转移条件及方向。 三、利用定时器产生时…...

AI,如何重构理解、匹配与决策?

AI 时代&#xff0c;我们如何理解消费&#xff1f; 作者&#xff5c;王彬 封面&#xff5c;Unplash 人们通过信息理解世界。 曾几何时&#xff0c;PC 与移动互联网重塑了人们的购物路径&#xff1a;信息变得唾手可得&#xff0c;商品决策变得高度依赖内容。 但 AI 时代的来…...

Java求职者面试指南:Spring、Spring Boot、MyBatis框架与计算机基础问题解析

Java求职者面试指南&#xff1a;Spring、Spring Boot、MyBatis框架与计算机基础问题解析 一、第一轮提问&#xff08;基础概念问题&#xff09; 1. 请解释Spring框架的核心容器是什么&#xff1f;它在Spring中起到什么作用&#xff1f; Spring框架的核心容器是IoC容器&#…...

如何更改默认 Crontab 编辑器 ?

在 Linux 领域中&#xff0c;crontab 是您可能经常遇到的一个术语。这个实用程序在类 unix 操作系统上可用&#xff0c;用于调度在预定义时间和间隔自动执行的任务。这对管理员和高级用户非常有益&#xff0c;允许他们自动执行各种系统任务。 编辑 Crontab 文件通常使用文本编…...

毫米波雷达基础理论(3D+4D)

3D、4D毫米波雷达基础知识及厂商选型 PreView : https://mp.weixin.qq.com/s/bQkju4r6med7I3TBGJI_bQ 1. FMCW毫米波雷达基础知识 主要参考博文&#xff1a; 一文入门汽车毫米波雷达基本原理 &#xff1a;https://mp.weixin.qq.com/s/_EN7A5lKcz2Eh8dLnjE19w 毫米波雷达基础…...

Java求职者面试指南:Spring、Spring Boot、Spring MVC与MyBatis技术解析

Java求职者面试指南&#xff1a;Spring、Spring Boot、Spring MVC与MyBatis技术解析 一、第一轮基础概念问题 1. Spring框架的核心容器是什么&#xff1f;它的作用是什么&#xff1f; Spring框架的核心容器是IoC&#xff08;控制反转&#xff09;容器。它的主要作用是管理对…...