第2章 WebServer进阶

2.1 使用多线程处理多用户请求

2.1.1 多线程Socket通信

        在上一章的案例中,服务端显然只能处理一次浏览器请求,请求一次浏览器端就结束程序。如何解决这个问题呢?可以采用多线程Socket通信技术,解决多用户并发请求。

        在多线程Socket通信中,服务端会启动一个主线程用于监听客户端的连接请求,并为每个客户端连接请求创建一个新的子线程进行处理。这样可以保证服务端能够同时处理多个客户端的请求,提高系统的并发性能和稳定性。

        具体流程如下:

        1. 服务端启动主线程监听客户端的连接请求;

        2. 当有新的客户端连接请求时,主线程创建一个新的子线程来处理该客户端请求;

        3. 子线程接收客户端的请求信息,并根据请求内容进行相应的业务处理;

        4. 子线程将处理结果封装成响应报文发送给客户端;

        5. 子线程关闭连接,结束线程。

        这样,服务端就可以同时处理多个客户端请求,实现了高并发处理能力。同时,使用多线程编程也能提高代码的可维护性和可扩展性,减少代码耦合度。

2.1.2 使用多线程处理HTTP通信

        首先定义ClientHandler,作为线程处理HTTP请求和发生HTTP响应:

public class ClientHandler implements Runnable {private Socket socket;public ClientHandler(Socket clientSocket){socket = clientSocket;}@Overridepublic void run() {try {//从客户端Socket对象中获取输入流,读取HTTP请求报文(请求消息)。InputStream in = socket.getInputStream();StringBuilder builder= new StringBuilder();//   前一个字符  当前字符char previous = 0, current = 0;int b;while ((b=in.read())!=-1){//将读取的字节存储到当前字符, 由于请求头采用了ISO8859-1编码,// 所以可以讲字节直接转化为字符类型current = (char) b;//如果发现了 前一个字符是 \r 当前字符是 \n 就读取到了行末尾if (previous == '\r' && current == '\n'){//如果这一行是空行就结束处理了if (builder.toString().isEmpty()){break;}//输出这一行数据当前一行数据并且清空builder,为下次缓存数据做准备System.out.println(builder);builder.delete(0, builder.length());}else if (current != '\r' && current != '\n'){//当前的不是 \r \n 就是一行中的字符builder.append(current);}//最后将当前的字符作为下次的前一个字符previous = current;}OutputStream out = socket.getOutputStream();//一个简单的网页内容String html = "<html>\n" +"<head>\n" +"<title>Example</title>\n" +"</head>\n" +"<body>\n" +"<p>Hello World!</p>\n" +"</body>\n" +"</html>";byte[] body = html.getBytes(StandardCharsets.UTF_8);out.write("HTTP/1.1 200 OK".getBytes(StandardCharsets.ISO_8859_1));out.write('\r');out.write('\n');out.write("Content-Type: text/html; charset=utf-8".getBytes(StandardCharsets.ISO_8859_1));out.write('\r');out.write('\n');out.write(("Content-Length: "+body.length).getBytes(StandardCharsets.ISO_8859_1));out.write('\r');out.write('\n');out.write('\r'); //空行out.write('\n');out.write(body);//关闭客户端连接out.close();in.close();}catch (IOException e){e.printStackTrace();}finally {try {socket.close();} catch (IOException e) {e.printStackTrace();}}}
}

        然后重构start()方法:

public class ServerBootApplication {private ServerSocket serverSocket;public void start(){try {//创建ServerSocket对象,并指定监听的端口号8088。serverSocket = new ServerSocket(8088);while (true) {//使用accept()方法等待客户端的连接请求,并获取客户端的Socket对象Socket clientSocket = serverSocket.accept();ClientHandler clientHandler = new ClientHandler(clientSocket);Thread thread = new Thread(clientHandler);thread.start();}}catch (IOException e){e.printStackTrace();}}public static void main(String[] args) {//创建ServerBoot对象ServerBootApplication application = new ServerBootApplication();//启动服务器application.start();}
}

        该代码是一个典型的多线程Socket通信的服务器端代码结构。在主线程中,创建了一个ServerSocket对象,并指定要监听的端口号8088。接着,通过一个while循环不断地使用accept()方法等待客户端的连接请求,一旦接收到请求,就会创建一个新的ClientHandler对象,将客户端的Socket对象传递给它,然后将ClientHandler对象封装成一个新的线程并启动,用于处理客户端的请求。在这个过程中,主线程一直保持监听状态,等待下一个客户端连接。

        为了处理多个客户端的并发请求,每个ClientHandler对象都运行在一个单独的线程中,这使得服务器可以同时处理多个客户端的请求,提高了系统的并发处理能力。

        经过上述重构我们的WebServer就可以处理多用户的并发请求了。

2.1.3 关于favicon.ico

        实现了多线程Web请求处理功能以后,控制台上出现了如下请求信息:

        由图可以看出:显然请求了favicon.ico文件。

        favicon.ico是一个网站上常见的文件,它是网站的图标文件,通常会显示在网站的标签页和书签上。当用户访问一个网站时,浏览器会自动请求这个文件,以便在标签页和书签上显示网站的图标。因此,在服务器的请求日志中,我们会看到很多关于 favicon.ico 的请求记录。这些请求记录是非常正常的现象,不必过于关注。

        理解这些请求信息有助于我们对网站访问进行监控和分析。通常情况下,这些请求不会对服务器性能产生重大影响,因为 favicon.ico 文件通常是相对较小的图标文件,并且浏览器会进行缓存,减少了对服务器的重复请求。

        因此,不必担心看到这些 favicon.ico 请求记录,除非你注意到在短时间内出现异常的请求量,这可能是有人在恶意攻击或者其他异常情况,需要进一步分析和处理。否则,这些请求是正常的,不需要特别处理。

2.2 解析请求行

2.2.1 解析请求行

        HTTP请求行是HTTP请求报文的第一行,包括HTTP方法、请求URL和HTTP协议版本号。例如:

GET /index.html HTTP/1.1

        其中,GET表示HTTP请求方法,/index.html表示请求的资源URL,HTTP/1.1表示使用的HTTP协议版本号。

        解析HTTP请求行可以获取请求方法、请求URL和协议版本等信息,这些信息对于服务器来说非常重要,可以根据这些信息对请求进行处理和响应。例如,根据请求URL可以确定请求的资源类型和位置,从而进行处理和响应;根据请求方法可以确定请求的类型(如GET、POST、PUT、DELETE等),从而采取相应的处理方式。因此,解析HTTP请求行是Web服务器处理HTTP请求的重要步骤之一。

        前述多线程的WebServer案例虽然能过处理用户请求,但是用户发起任何请求都会得到相同的响应结果,比如发送:http://localhost:8088/ 、http://localhost:8088/index.html和 http://localhost:8088/demo.html,都得到如下结果:

        这个结果显然不理想:不可能任何请求都返回相同的响应结果。正确情况应该是:请求index.html就显示index.html文件的内容,请求demo.index文件就显示demo.html的内容。

2.2.2 显示正确的请求内容

        如何解决这个问题呢?解决的办法就是将请求行进行解析,找出客户端发起的请求资源路径,根据请求资源的路径找到响应的资源,发送响应到客户端浏览器。这样就可以在用户请求不同资源时候,响应不同的结果:

2.2.3 读取请求行

        请求行在请求消息的第一行,请求行以\r\n为结尾,可以使用算法在读取请求头之前读取第一行作为请求行,如下代码可以读取请求的第一行:

InputStream in = socket.getInputStream();
StringBuilder builder= new StringBuilder();
//   前一个字符  当前字符
char previous = 0, current = 0;
int b;
String requestLine = null;
//解析请求行
while ((b=in.read())!=-1){current = (char) b;if (previous == '\r' && current == '\n'){requestLine = builder.toString();builder.delete(0, builder.length());break;}else if (current != '\r' && current != '\n'){builder.append(current);}previous = current;
}
System.out.println(requestLine);

        显然这个算法和读取请求头的代码是重复的,所以可以尝试将代码重构抽取一个从输入流中读取一行的方法:

public String readLine() throws IOException{InputStream in = socket.getInputStream();StringBuilder builder= new StringBuilder();//   前一个字符  当前字符char previous = 0, current = 0;int b;//解析请求行while ((b=in.read())!=-1){current = (char) b;if (previous == '\r' && current == '\n'){//遇到行结束就结束读取break;}else if (current != '\r' && current != '\n'){builder.append(current);}previous = current;}return builder.toString();
}

        这段代码的作用是从Socket的输入流中读取一行数据并返回。它通过InputStream获取Socket的输入流,然后使用一个StringBuilder对象来存储读取的数据,最终返回读取的数据。

        具体实现逻辑如下:

        1. 创建一个InputStream对象in,并将其设置为socket的输入流。

        2. 创建一个StringBuilder对象builder,用于存储读取的数据。

        3. 定义两个字符变量previous和current,用于记录前一个字符和当前字符。

        4. 定义一个int类型变量b,用于记录从输入流中读取的字节。

        5. 使用while循环从输入流中读取字节,直到读取完一行数据。

        6. 将读取到的字节转换成字符类型,并赋值给变量current。

        7. 判断当前字符是否为行结束符("\r\n"),如果是则退出循环,否则将当前字符添加到builder中。

        8. 将当前字符赋值给previous,以备下次循环使用。

        9. 循环结束后,将builder转换成字符串并返回。

        重构后的ClientHandler代码就清爽许多:

public class ClientHandler implements Runnable {private Socket socket;public ClientHandler(Socket clientSocket){socket = clientSocket;}@Overridepublic void run() {try {//从客户端Socket对象中获取输入流,读取HTTP请求报文(请求消息)。InputStream in = socket.getInputStream();//读取请求行String requestLine = readLine();System.out.println(requestLine);//读取请求头String requestHeader;//读取到空行就不在读取请求头了while (!(requestHeader = readLine()).isEmpty()){System.out.println(requestHeader);}OutputStream out = socket.getOutputStream();//一个简单的网页内容String html = "<html>\n" +"<head>\n" +"<title>Example</title>\n" +"</head>\n" +"<body>\n" +"<p>Hello World!</p>\n" +"</body>\n" +"</html>";byte[] body = html.getBytes(StandardCharsets.UTF_8);out.write("HTTP/1.1 200 OK".getBytes(StandardCharsets.ISO_8859_1));out.write('\r');out.write('\n');out.write("Content-Type: text/html; charset=utf-8".getBytes(StandardCharsets.ISO_8859_1));out.write('\r');out.write('\n');out.write(("Content-Length: "+body.length).getBytes(StandardCharsets.ISO_8859_1));out.write('\r');out.write('\n');out.write('\r'); //空行out.write('\n');out.write(body);//关闭客户端连接out.close();in.close();}catch (IOException e){e.printStackTrace();}finally {try {socket.close();} catch (IOException e) {e.printStackTrace();}}}/*** 这段代码的作用是从Socket的输入流中读取一行数据并返回。* @return 从Socket的输入流中读取一行数据并返回* @throws IOException 出现网络IO错误*/public String readLine() throws IOException{InputStream in = socket.getInputStream();StringBuilder builder= new StringBuilder();//   前一个字符  当前字符char previous = 0, current = 0;int b;//解析请求行while ((b=in.read())!=-1){current = (char) b;if (previous == '\r' && current == '\n'){//遇到行结束就结束读取break;}else if (current != '\r' && current != '\n'){builder.append(current);}previous = current;}return builder.toString();}
}

2.2.4 解析请求行

        上述代码实现了,读取请求行,读取后需要从请求行中解析其中的每个部分,然后可以根据请求行找到相应的本地文件资源,发送响应给浏览器,显示不同的资源内容。

        解析请求行:

String[] line = requestLine.split("\\s");
String method = line[0];
String uri = line[1];
String protocol = line[1];
System.out.println("method: "+method);
System.out.println("uri: " + uri);
System.out.println("protocol: " + protocol);

        这段代码用于解析HTTP请求报文中的请求行。首先,将请求行按照空白字符进行分割,得到一个包含请求方法、URI和协议版本三个字段的字符串数组。接着,将这三个字段分别存储到对应的变量中,并打印出来以供调试或其他用途。

        具体解释如下:

  • 使用String类的split()方法按照空白字符(包括空格、制表符和换行符)对请求行进行分割,得到一个包含请求方法、URI和协议版本三个字段的字符串数组line
  • 将line数组中的第一个元素存储到字符串变量method中,第二个元素存储到字符串变量uri中,第三个元素存储到字符串变量protocol中
  • 最后,使用System.out.println()方法打印出method、uri和protocol的值,方便调试和查看解析结果

        需要注意的是,该代码仅仅是对HTTP请求报文中的请求行进行了最基本的解析,仅适用于最简单的HTTP请求,对于复杂的HTTP请求报文,需要进行更加严谨和完整的解析。

        重新启动后服务端后,用浏览器发起 http://localhost:8088/index.html 请求,控制台信息包含如下信息:

        这个信息表示请求行解析成功。

2.3 响应静态资源

2.3.1 创建静态资源

        前述项目完成了解析请求行,从请求行中得到了请求资源的路径URI,为了能响应客户端需求,返回对应的资源,所以要建立静态文件夹存储静态资源:

        index.html 文件是一个html文件,格式类似于html,内容如下:

<!DOCTYPE html>
<html lang="cn">
<head><meta charset="UTF-8"><title>Title</title>
</head>
<body><h1>Hello World</h1>
</body>
</html>

        demo.html 文件内容如下:

<!DOCTYPE html>
<html lang="cn">
<head><meta charset="UTF-8"><title>Title</title>
</head>
<body><h1>Demo Page</h1>
</body>
</html>

        有了这了两个资源,就可以实现根据用户请求的uri,找到这两个资源,并且发送到响应到浏览器,实现根据用户请求响应不同文件的功能。

2.3.2 响应静态资源

        重构服务端代码,根据URI在resources/static文件夹中找到静态资源,并且将静态资源响应给客户端,原理为:

2.3.3 响应静态资源

        重构后的代码:

//发送响应
//根据找到静态资源
//类加载路径:target/classes
File root = new File(ClientHandler.class.getClassLoader().getResource(".").toURI()
);
//定位target/classes/static目录(SpringBoot中存放所有静态资源的目录)
File staticDir = new File(root,"static");
//定位target/classes/static目录中的文件
File file = new File(staticDir,uri);
//读取文件的全部内容
byte[] bytes = new byte[(int)file.length()];
FileInputStream fin = new FileInputStream(file);
fin.read(bytes);
fin.close();OutputStream out = socket.getOutputStream();out.write("HTTP/1.1 200 OK".getBytes(StandardCharsets.ISO_8859_1));
out.write('\r');
out.write('\n');
out.write("Content-Type: text/html; charset=utf-8".getBytes(StandardCharsets.ISO_8859_1));
out.write('\r');
out.write('\n');
out.write(("Content-Length: "+bytes.length).getBytes(StandardCharsets.ISO_8859_1));
out.write('\r');
out.write('\n');
out.write('\r'); //空行
out.write('\n');
out.write(bytes);
//关闭客户端连接
out.close();
in.close();

        这段代码是一个简单的HTTP服务器响应客户端请求的部分,主要功能是根据请求的URI定位服务器上的静态资源,并将其发送给客户端。

        具体来说,代码首先根据当前类的类加载器获取类加载路径,然后根据该路径找到服务器上的静态资源所在目录。接着,根据URI定位静态资源文件,并读取该文件的全部内容到一个字节数组中。

        然后,代码使用Java的Socket API将HTTP响应发送给客户端。响应包括HTTP响应头和响应体两部分。响应头中包含HTTP协议版本、状态码和响应内容类型等信息,而响应体则包含实际的静态资源内容。发送响应的代码使用OutputStream将响应数据写入到客户端的Socket连接中,并在最后关闭客户端连接。

        完成的ClientHandler参考如下:

public class ClientHandler implements Runnable {private Socket socket;public ClientHandler(Socket clientSocket){socket = clientSocket;}@Overridepublic void run() {try {//从客户端Socket对象中获取输入流,读取HTTP请求报文(请求消息)。InputStream in = socket.getInputStream();//读取请求行String requestLine = readLine();System.out.println(requestLine);//解析请求行String[] line = requestLine.split("\\s");String method = line[0];String uri = line[1];String protocol = line[1];System.out.println("method: "+method);System.out.println("uri: " + uri);System.out.println("protocol: " + protocol);//读取请求头String requestHeader;//读取到空行就不在读取请求头了while (!(requestHeader = readLine()).isEmpty()){System.out.println(requestHeader);}//发送响应//根据找到静态资源//类加载路径:target/classesFile root = new File(ClientHandler.class.getClassLoader().getResource(".").toURI());//定位target/classes/static目录(SpringBoot中存放所有静态资源的目录)File staticDir = new File(root,"static");//定位target/classes/static目录中的文件File file = new File(staticDir,uri);//读取文件的全部内容byte[] bytes = new byte[(int)file.length()];FileInputStream fin = new FileInputStream(file);fin.read(bytes);fin.close();OutputStream out = socket.getOutputStream();out.write("HTTP/1.1 200 OK".getBytes(StandardCharsets.ISO_8859_1));out.write('\r');out.write('\n');out.write("Content-Type: text/html; charset=utf-8".getBytes(StandardCharsets.ISO_8859_1));out.write('\r');out.write('\n');out.write(("Content-Length: "+bytes.length).getBytes(StandardCharsets.ISO_8859_1));out.write('\r');out.write('\n');out.write('\r'); //空行out.write('\n');out.write(bytes);//关闭客户端连接out.close();in.close();}catch (IOException | URISyntaxException e){e.printStackTrace();}finally {try {socket.close();} catch (IOException e) {e.printStackTrace();}}}/*** 这段代码的作用是从Socket的输入流中读取一行数据并返回。它通过InputStream获取Socket的输入流,* 然后使用一个StringBuilder对象来存储读取的数据,最终返回读取的数据。* @return 从Socket的输入流中读取一行数据并返回* @throws IOException 出现网络IO错误*/public String readLine() throws IOException{InputStream in = socket.getInputStream();StringBuilder builder= new StringBuilder();//   前一个字符  当前字符char previous = 0, current = 0;int b;//解析请求行while ((b=in.read())!=-1){current = (char) b;if (previous == '\r' && current == '\n'){//遇到行结束就结束读取break;}else if (current != '\r' && current != '\n'){builder.append(current);}previous = current;}return builder.toString();}
}

2.3.4 HTTP协议补充

        HTTP(Hypertext Transfer Protocol)是一种用于在计算机网络上传输超文本数据的协议。它是Web应用程序中最基本的通信协议,用于在客户端(例如浏览器)和服务器之间传输数据。

        HTTP的主要特点包括:

  • 状态无关:HTTP是一种无状态协议,即服务器不会记录客户端之前的请求信息。每个HTTP请求都是独立的,服务器不会保留客户端的状态信息,这样可以降低服务器的负担,也使得HTTP协议具有良好的扩展性。
  • 请求-响应模型:HTTP是基于请求-响应模型的协议。客户端发送HTTP请求到服务器,然后服务器返回HTTP响应。请求包括请求方法(GET、POST等)、请求头、请求体等信息,响应包括状态码、响应头、响应体等信息。
  • 可靠性:HTTP在传输过程中使用TCP协议作为传输层协议,因此具有可靠性。TCP协议会确保数据的正确传输和接收,如果数据丢失或损坏,TCP会自动重传。
  • 简单灵活:HTTP协议采用文本形式传输数据,易于阅读和调试。同时,HTTP协议也非常灵活,可以传输不同类型的数据,支持多种编码和内容类型。
  • 支持缓存:HTTP协议支持缓存机制,通过在响应中添加缓存相关的头信息,可以让浏览器在下次请求相同资源时直接从缓存中获取,提高性能和加载速度。

        HTTP协议的工作方式是客户端向服务器发送请求,服务器根据请求进行处理并返回响应。客户端和服务器通过URL(统一资源定位符)来定位资源,URL由协议类型(例如http)、服务器地址和资源路径组成。

        HTTP协议是Web开发中非常重要的基础,它使得浏览器能够请求并获取Web页面、图片、视频、文件等资源,并实现了Web应用程序的交互性。同时,HTTP也不断发展,出现了新的版本,例如HTTP/1.1和HTTP/2,以满足不断增长的Web应用需求。

2.3.5 幂等性

        在计算机科学和网络编程中,幂等性(Idempotence)是指对同一个操作进行一次或多次的操作,产生的结果是相同的。换句话说,无论对一个操作进行多少次重复,其结果都是一致的。

        幂等性在计算机系统设计和网络通信中具有重要意义,特别是在处理故障、网络延迟或重试等情况下。幂等性操作保证了系统对相同请求的重复处理不会导致副作用或错误结果。

        举个简单的例子,假设有一个用于更新用户信息的API接口。如果这个API是幂等的,那么当多个请求同时更新同一个用户信息时,无论请求执行多少次,最终用户的信息都只会更新一次,而不会因为重复的请求导致用户信息被错误地更新多次。

        在实际应用中,一些常见的幂等性操作包括:

  • GET请求:GET请求是幂等的,因为对于同一个URL的GET请求,无论请求执行多少次,都只会返回相同的响应结果,不会对服务器产生任何副作用。
  • PUT请求:PUT请求通常用于更新资源,在幂等性的设计下,对于同一个URL的PUT请求,重复执行对资源的更新操作将得到相同的结果。
  • DELETE请求:DELETE请求通常用于删除资源,在幂等性的设计下,对于同一个URL的DELETE请求,重复执行对资源的删除操作将得到相同的结果。

        幂等性的接口设计:在设计API接口时,如果接口的操作具有幂等性,可以增强系统的稳定性和可靠性。通过一些设计措施,比如生成唯一的请求标识,对于相同的请求标识只处理一次,就能实现幂等性。

        总的来说,幂等性操作对于构建健壮和可靠的系统非常重要,可以避免重复操作导致的不一致性和错误结果。在网络通信中,幂等性的操作可以增加系统的容错性,确保请求的可靠传输和正确处理。

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

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

相关文章

从谚语:“一手交钱,一手交货来”谈谈什么是数据库事务

事务与交易 一手交钱&#xff0c;一手交货 一手交钱&#xff0c;一手交货&#xff0c;谚语&#xff0c;意思是指钱和货当场相交&#xff0c;互不拖欠。出自明朝施耐庵《水浒传》第二一回。 Transaction 意思 我们先来看一下来自于剑桥英-英词典的解释&#xff1a; transac…

Flask-HTTP请求、响应、上下文、进阶实验

本节主要目录如下&#xff1a; 一、请求响应循环 二、HTTP请求 2.1、请求报文 2.2、Request对象 2.3、在Flask中处理请求 2.4、请求钩子 三、HTTP响应 3.1、响应报文 3.2、在Flask中生成响应 3.3、响应格式 3.4、Cookie 3.5、session&#xff1a;安全的Cookie 四、…

企业车辆管理系统参考论文(论文 + 源码)

【免费】关于企业车辆管理系统.zip资源-CSDN文库https://download.csdn.net/download/JW_559/89282550 企业车辆管理系统 摘 要 随着经济的日益增长,车辆作为最重要的交通工具,在企事业单位中得以普及,单位的车辆数目已经远远不止简单的几辆,与此同时就产生了车辆资源的合理…

汽车灯罩一般都是用什么材质做的?汽车车灯的灯罩如果破损破裂破洞了要怎么修复?

汽车灯罩一般都是用什么材质做的&#xff1f; 汽车灯罩一般使用的主要材质是聚碳酸酯&#xff08;PC&#xff09;和丙烯酸酯&#xff08;PMMA&#xff09;这两种塑料。这两种材料具有良好的透明性、耐候性和耐冲击性&#xff0c;因此非常适合用于汽车灯罩的制造。 聚碳酸酯&am…

小程序支付的款项流转与到账时间

商家做小程序&#xff0c;最关心的是客户通过小程序下单支付的钱&#xff0c;是怎么样的流转状态以及最终到哪里。因此&#xff0c;本文将详细解析款项最终流向何处以及多久能够到账。 一、小程序支付的款项流向 当用户在小程序内完成支付后&#xff0c;款项并不会直接到达商…

5月8日学习记录

_[FBCTF2019]RCEService&#xff08;preg_match函数的绕过&#xff09; 涉及知识点&#xff1a;preg_match函数绕过&#xff0c;json的格式&#xff0c;正则回溯 打开环境&#xff0c;要求用json的格式输入 搜索学习一下json的语法规则 数组&#xff08;Array&#xff09;用方括…

制造版图大变革!逾10座晶圆厂蓄势待发 | 百能云芯

在全球半导体产业的激烈竞争和市场需求的复杂波动中&#xff0c;晶圆厂建设热潮正在美国兴起&#xff0c;这一波建设浪潮的核心动力之一&#xff0c;便是美国政府推出的《芯片与科学法案》所承诺的巨额补贴&#xff0c;旨在提升美国在全球半导体行业的竞争力。 当地时间4月25日…

JSP技术讲解

目录 1、JSP简介 2、JSP体验 3、JSP运行原理 4、JSP基本语法 5、JSP指令 6、JSP内置九大对象 7、JSP标签 8、JSP配置 9、JSP排错 10、总结 在前面的Servlet学习中发现Servlet本质是一个java程序&#xff0c;因此Servlet更加擅长编写程序的业务逻辑&#xff0c;而如果要…

shpfile转GeoJSON;控制shp转GeoJSON的精度;如何获取GeoJSON;GeoJSON是什么有什么用;GeoJSON结构详解(带数据示例)

目录 一、GeoJSON是什么 二、GeoJSON的结构组成 2.1、点&#xff08;Point&#xff09;数据示例 2.2、线&#xff08;LineString&#xff09;数据示例 2.3、面&#xff08;Polygon&#xff09;数据示例 2.4、特征&#xff08;Feature&#xff09;数据示例 2.5、特征集合&…

Hass哈斯数控数据采集网络IP配置设置

机床数据采集&#xff08;MDC&#xff09;允许你使用Q和E命令通过网络接口或选项无线网络从控制系统提取数据。设置143支持该功能&#xff0c;并且指定控制器使用这个数据端口。MDC是一个需要一台附加计算机发送请求&#xff0c;解释说明和存储机床数据的软件功能。这个远程计算…

【计算机毕业设计】基于SSM++jsp的电子竞技管理平台【源码+lw+部署文档+讲解】

目录 1 绪论 1.1 研究背景 1.2 目的和意义 1.3 论文结构安排 2 相关技术 2.1 SSM框架介绍 2.2 B/S结构介绍 2.3 Mysql数据库介绍 3 系统分析 3.1 系统可行性分析 3.1.1 技术可行性分析 3.1.2 经济可行性分析 3.1.3 运行可行性分析 3.2 系统性能分析 3.2.1 易用性指标 3.2.2 可…

JavaScript数字分隔符

● 如果现在我们用一个很大的数字&#xff0c;例如2300000000&#xff0c;这样真的不便于我们进行阅读&#xff0c;我们希望用千位分隔符来隔开它&#xff0c;例如230,000,000; ● 下面我们使用_当作分隔符来尝试一下 const diameter 287_266_000_000; console.log(diameter)…

C++学习————第十天(string的基本使用)

1、string 对象类的常见构造 (constructor)函数名称 功能说明&#xff1a; string() &#xff08;重点&#xff09; 构造空的string类对象&#xff0c;即空字符串 string(const char* s) &#xff08;重点&#xff09;…

PostgreSQL的学习心得和知识总结(一百四十一)|深入理解PostgreSQL数据库数据库角色的使用及预定义角色的原理

目录结构 注&#xff1a;提前言明 本文借鉴了以下博主、书籍或网站的内容&#xff0c;其列表如下&#xff1a; 1、参考书籍&#xff1a;《PostgreSQL数据库内核分析》 2、参考书籍&#xff1a;《数据库事务处理的艺术&#xff1a;事务管理与并发控制》 3、PostgreSQL数据库仓库…

Mysql 基础 - 常见 子句

算数运算符 > < > < !/<> 逻辑运算符 3i in is null is not null 2l limit like 2o or 、order by 1a and ib between and 1n not and、or 、not、 in、 orderby、 limit、 like、 between...and、 is null 、is not null

【C++】C++11--- 列表初始化|关键字

目录 前言 列表初始化 创建对象时的列表初始化 单参数隐式类型转换 多参数的隐式类型转换 new表达式中使用列表初始化 列表初始化适用于STL 容器 模板类initializer_list 关键字auto 关键字decltype 关键字nullptr 前言 C标准10年磨一剑&#xff0c;第二个真正意义上…

学习软考----数据库系统工程师20

数据库技术基础 主要内容如下&#xff1a; DBMS的功能和特点 课本上&#xff1a; 数据库系统的三级模式结构 数据模型 E-R图

AI讲师大模型培训老师叶梓:大模型应用的方向探讨

大模型应用的关键方向及其落地案例可以从多个角度进行探讨&#xff0c;结合最新的研究和实际应用案例&#xff0c;我们可以更全面地理解这些技术如何推动社会和经济的发展。 Agent&#xff08;数字代理&#xff09;: 方向说明:Agent方向的AI技术旨在创建能够独立执行任务、做出…

Git系列:Git Stash临时保存与恢复工作进度

&#x1f49d;&#x1f49d;&#x1f49d;欢迎莅临我的博客&#xff0c;很高兴能够在这里和您见面&#xff01;希望您在这里可以感受到一份轻松愉快的氛围&#xff0c;不仅可以获得有趣的内容和知识&#xff0c;也可以畅所欲言、分享您的想法和见解。 推荐:「stormsha的主页」…

掌控网络流量,优化网络性能 - AnaTraf网络流量分析仪登场

在当今日新月异的网络环境中,网络流量监控和性能诊断已成为企业IT部门不可或缺的重要工作。只有充分了解网络流量状况,才能有效优化网络性能,提高业务运营效率。针对这一需求,全新推出的AnaTraf网络流量分析仪应运而生,为企业提供全面的网络监控和性能诊断解决方案。 快速定位…