10.JAVAEE之网络编程

1.网络编程

  • 通过网络,让两个主机之间能够进行通信 =>基于这样的通信来完成一定的功能
  • 进行网络编程的时候,需要操作系统给咱们提供一组 AP1, 通过这些 API才能完成编程(API 可以认为是 应用层 和 传输层 之间交互的路径)(API:Socket API相当于一个插座:通过这一套 Socket AP| 可以完成不同主机之间,不同系统之间的网络通信)
  • 传输层,提供的网络协议,主要是两个:TCP UDP
  • 这俩协议的特性(工作原理) 差异很大.导致,使用这两种协议进行网络编程,也存在一定差别系统就分别提供了两套 API
  • TCP 和 UDP 的区别.(后面网络原理章节, 学习的重点)

       1.TCP 是有连接的, UDP 是无连接的

       2.TCP 是可靠传输的,UDP 是不可靠传输的

       3.TCP 是面向字节流的,UDP 是面向数据报

       4.TCP 和 UDP 都是全双工的

 1.TCP 是有连接的, UDP 是无连接的
(连接 是 抽象 的概念)
计算机中,这种 抽象 的连接是很常见的,此处的连接本质上就是建立连接的双方,各自保存对方的信息两台计算机建立连接,就是双方彼此保存了对方的关键信息~~
TCP 要想通信, 就需要先建立连接 (刚才说的, 保存对方信息),做完之后,才能后续通信(如果 A 想和 B 建立连接, 但是 B 拒绝了! 通信就无法完成!!!)

UDP 想要通信,就直接发送数据即可~~不需要征得对方的同意,UDP 自身也不会保存对方的信息(UDP 不知道,但是写程序的人得知道.UDP 自己不保存,但是你调用 UDP 的 socket api的时候要把对方的位置啥的给传过去)

2.TCP 是可靠传输的,UDP 是不可靠传输的
网络上进行通信, A ->B 发送一个消息,这个消息是不可能做到 100% 送达的!! 

可靠传输,退而求其次.
A ->B 发消息,消息是不是到达 B 这一方,A 自己能感知到.(A 心里有数)进一步的,就可以在发送失败的时候采取一定的措施(尝试重传之类的)

TCP 就内置了可靠传输机制;UDP 就没有内置可靠传输

【tips】可靠传输,听起来挺美好的呀, 为啥不让 UDP 也搞个可靠传输呢??

想要可靠传输,你就是要付出代价的(需要去交换)
可靠传输要付出什么代价?
1)机制更复杂
2)传输效率会降低

3.TCP 是面向字节流的,UDP 是面向数据报
此处说的 字节流 和 文件 操作这里的 字节流 是一个意思!!!
TCP 也是和文件操作一样,以字节为单位来进行传输.
UDP 则是按照数据报为单位,来进行传输的
UDP 数据报是有严格的格式的 

网络通信数据的基本单位,涉及到多种说法~~
1.数据报(Datagram)
2.数据包(Packet)

3.数据帧(Frame)
4.数据段 (Segment) 

4. TCP 和 UDP 都是全双工的
一个信道,允许双向通信, 就是全双工
一个信道,只能单向通信,就是半双工
代码中使用一个 Socket 对象, 就可以发送数据也能接受数据~~ 

2.UDP 的 socket api 如何使用

Datagramsocket 

Datagrampacket 

【回显服务器:(echo server)】

写一个简单的 UDP 的客户端/服务器 通信的程序.
这个程序没有啥业务逻辑,只是单纯的调用 socket api.
让客户端给服务器发送一个请求,请求就是一个从控制台输入的字符串.
服务器收到字符串之后,也就会把这个字符串原封不动的返回给客户端,客户端再显示出来. 

import java.io.IOException;
import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.net.SocketException;public class UdpEchoServer {// 创建一个 DatagramSocket 对象. 后续操作网卡的基础.private DatagramSocket socket = null;public UdpEchoServer(int port) throws SocketException {// 这么写就是手动指定端口socket = new DatagramSocket(port);// 这么写就是让系统自动分配端口// socket = new DatagramSocket();}public void start() throws IOException {// 通过这个方法来启动服务器.System.out.println("服务器启动!");// 一个服务器程序中, 经常能看到 while true 这样的代码.while (true) {// 1. 读取请求并解析.DatagramPacket requestPacket = new DatagramPacket(new byte[4096], 4096);socket.receive(requestPacket);// 当前完成 receive 之后, 数据是以 二进制 的形式存储到 DatagramPacket 中了.// 要想能够把这里的数据给显示出来, 还需要把这个二进制数据给转成字符串.String request = new String(requestPacket.getData(), 0, requestPacket.getLength());// 2. 根据请求计算响应(一般的服务器都会经历的过程)//    由于此处是回显服务器, 请求是啥样, 响应就是啥样.String response = process(request);// 3. 把响应写回到客户端.//    搞一个响应对象, DatagramPacket//    往 DatagramPacket 里构造刚才的数据, 再通过 send 返回.DatagramPacket responsePacket = new DatagramPacket(response.getBytes(), response.getBytes().length,requestPacket.getSocketAddress());socket.send(responsePacket);// 4. 打印一个日志, 把这次数据交互的详情打印出来.System.out.printf("[%s:%d] req=%s, resp=%s\n", requestPacket.getAddress().toString(),requestPacket.getPort(), request, response);}}public String process(String request) {return request;}public static void main(String[] args) throws IOException {UdpEchoServer server = new UdpEchoServer(9090);server.start();}
}
import java.io.IOException;
import java.net.*;
import java.util.Scanner;public class UdpEchoClient {private DatagramSocket socket = null;private String serverIp = "";private int serverPort = 0;public UdpEchoClient(String ip, int port) throws SocketException {// 创建这个对象, 不能手动指定端口.socket = new DatagramSocket();// 由于 UDP 自身不会持有对端的信息. 就需要在应用程序里, 把对端的情况给记录下来.// 这里咱们主要记录对端的 ip 和 端口 .serverIp = ip;serverPort = port;}public void start() throws IOException {System.out.println("客户端启动!");Scanner scanner = new Scanner(System.in);while (true) {// 1. 从控制台读取数据, 作为请求System.out.print("-> ");String request = scanner.next();// 2. 把请求内容构造成 DatagramPacket 对象, 发给服务器.DatagramPacket requestPacket = new DatagramPacket(request.getBytes(), request.getBytes().length,InetAddress.getByName(serverIp), serverPort);socket.send(requestPacket);// 3. 尝试读取服务器返回的响应了.DatagramPacket responsePacket = new DatagramPacket(new byte[4096], 4096);socket.receive(responsePacket);// 4. 把响应, 转换成字符串, 并显示出来.String response = new String(responsePacket.getData(), 0, responsePacket.getLength());System.out.println(response);}}public static void main(String[] args) throws IOException {UdpEchoClient client = new UdpEchoClient("127.0.0.1", 9090);// UdpEchoClient client = new UdpEchoClient("42.192.83.143", 9090);client.start();}
}

【客户端】

  • 对于服务器来说,也就需要把端口号给明确下来了~~
  • 客户端的端口号是不需要确定的.交给系统进行分配即可,
  • 如果你手动指定确定的端口,就可能和别人的程序的端口号冲突
  • 【tips】服务器这边手动指定端口,就不会出现冲突嘛??
    为啥客户端在意这个冲突,而服务器不在意呢??

    服务器是在程序猿手里的,一个服务器上都有哪些程序, 都使用哪些端口,程序猿都是可控的!!程序猿写代码的时候,就可以指定一个空闲的端口,给当前的服务器使用即可
    但是客户端就不可控,客户端是在用户的电脑上;一方面,用户千千万~~ 每个用户电脑上装的程序都不一样,占用的端口也不一样;交给系统分配比较稳妥.系统能保证肯定分配一个空闲的端口

服务器一旦启动,就会立即执行到这里的 receive 方法此时,客户端的请求可能还没来呢~~
这种情况也没关系.receive 就会直接阻塞, 就会一直阻塞到真正客户端把请求发过来为止,(类似于阻塞队列)

【question】

//根据请求计算响应(核心步骤)

这个步骤是一个服务器程序,最核心的步骤!!!
咱们当前是 echo server 不涉及到这些流程,也不必考虑响应怎么计算,只要请求过来,就把请求当做响应

【question】

【question】上述写的代码中,为啥没写 close??
socket 也是文件,不关闭不就出问题了,不就文件资源泄露了么,为啥这里咱们可以不写 close?为啥不写 close 也不会出现文件资源泄露??

private DatagramSocket socket = null;
这个 socket 在整个程序运行过程中都是需要使用的(不能提前关闭)当 socket 不需要使用的时候, 意味着程序就要结束了

进程结束,此时随之文件描述符表就会销毁了(PCB 都销毁了).谈何泄露??
随着销毁的过程,被系统自动回收了~~

啥时候才会出现泄露?代码中频繁的打开文件,但是不关闭在一个进程的运行过程中,不断积累打开的文件,逐渐消耗掉文件描述符表里的内容最终就消耗殆尽了
但是如果进程的生命周期很短,打开一下没多久就关闭了.谈不上泄露
文件资源泄露这样的问题,在服务器这边是比较 严重的, 在客户端这边一般来说影响不大.

 【服务器】

【交互】

1.服务器先启动.服务器启动之后,就会进入循环,执行到 receive 这里并阻塞 (此时还没有客户端过来呢)
2.客户端开始启动,也会先进入 while 循环,执行 scanner.next.并且也在这里阻塞当用户在控制台输入字符串之后,next 就会返回,从而构造请求数据并发送出来~~

3.客户端发送出数据之后,
服务器: 就会从 receive 中返回,进一步的执行解析请求为字符串,执行 process 操作,执行 send 操作
客户端: 继续往下执行,执行到 receive,等待服务器的响应

4.客户端收到从服务器返回的数据之后,就会从 receive 中返回执行这里的打印操作,也就把响应给显示出来了
5.服务器这边完成一次循环之后, 又执行到 receive 这里,客户端这边完成一次循环之后,又执行到 scanner.next 这里双双进入阻塞

 【翻译服务器】

import java.io.IOException;
import java.net.SocketException;
import java.util.HashMap;
import java.util.Map;public class UdpDictServer extends UdpEchoServer {private Map<String, String> dict = new HashMap<>();public UdpDictServer(int port) throws SocketException {super(port);// 此处可以往这个表里插入几千几万个这样的英文单词.dict.put("dog", "小狗");dict.put("cat", "小猫");dict.put("pig", "小猪");}// 重写 process 方法, 在重写的方法中完成翻译的过程.// 翻译本质上就是 "查表"@Overridepublic String process(String request) {return dict.getOrDefault(request, "该词在词典中不存在!");}public static void main(String[] args) throws IOException {UdpDictServer server = new UdpDictServer(9090);server.start();}
}

上述重写 process 方法,就可以在子类中组织你想要的"业务逻辑",(你要写代码解决一些实际的问题)

3.TCP 的 socket api 如何使用 

TCP 的 socket api 和 UDP 的 socket api 差异又很大~,

但是和前面讲的 文件操作,有密切联系的

两个关键的类

1.ServerSocket(给服务器使用的类,使用这个类来绑定端口号)

2.Socket(既会给服务器用,又会给客户端用)

这俩类都是用来表示 socket 文件的,(抽象了网卡这样的硬件设备)

TCP 是字节流的.传输的基本单位,是 byte

import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.io.PrintWriter;
import java.net.Socket;
import java.util.Scanner;public class TcpEchoClient {private Socket socket = null;public TcpEchoClient(String serverIp, int serverPort) throws IOException {// 需要在创建 Socket 的同时, 和服务器 "建立连接", 此时就得告诉 Socket 服务器在哪里~~// 具体建立连接的细节, 不需要咱们代码手动干预. 是内核自动负责的.// 当我们 new 这个对象的时候, 操作系统内核, 就开始进行 三次握手 具体细节, 完成建立连接的过程了.socket = new Socket(serverIp, serverPort);}public void start() {// tcp 的客户端行为和 udp 的客户端差不多.// 都是:// 3. 从服务器读取响应.// 4. 把响应显示到界面上.Scanner scanner = new Scanner(System.in);try (InputStream inputStream = socket.getInputStream();OutputStream outputStream = socket.getOutputStream()) {PrintWriter writer = new PrintWriter(outputStream);Scanner scannerNetwork = new Scanner(inputStream);while (true) {// 1. 从控制台读取用户输入的内容System.out.print("-> ");String request = scanner.next();// 2. 把字符串作为请求, 发送给服务器//    这里使用 println, 是为了让请求后面带上换行.//    也就是和服务器读取请求, scanner.next 呼应writer.println(request);writer.flush();// 3. 读取服务器返回的响应.String response = scannerNetwork.next();// 4. 在界面上显示内容了.System.out.println(response);}} catch (IOException e) {e.printStackTrace();}}public static void main(String[] args) throws IOException {TcpEchoClient client = new TcpEchoClient("127.0.0.1", 9090);client.start();}
}
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.io.PrintWriter;
import java.net.ServerSocket;
import java.net.Socket;
import java.util.Scanner;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;public class TcpEchoServer {private ServerSocket serverSocket = null;public TcpEchoServer(int port) throws IOException {serverSocket = new ServerSocket(port);}public void start() throws IOException {System.out.println("服务器启动!");ExecutorService service = Executors.newCachedThreadPool();while (true) {// 通过 accept 方法, 把内核中已经建立好的连接拿到应用程序中.// 建立连接的细节流程都是内核自动完成的. 应用程序只需要 "捡现成" 的.Socket clientSocket = serverSocket.accept();// 此处不应该直接调用 processConnection, 会导致服务器不能处理多个客户端.// 创建新的线程来调用更合理的做法.// 这种做法可行, 不够好
//            Thread t = new Thread(() -> {
//                processConnection(clientSocket);
//            });
//            t.start();// 更好一点的办法, 是使用线程池.service.submit(new Runnable() {@Overridepublic void run() {processConnection(clientSocket);}});}}// 通过这个方法, 来处理当前的连接.public void processConnection(Socket clientSocket) {// 进入方法, 先打印一个日志, 表示当前有客户端连上了.System.out.printf("[%s:%d] 客户端上线!\n", clientSocket.getInetAddress(), clientSocket.getPort());// 接下来进行数据的交互.try (InputStream inputStream = clientSocket.getInputStream();OutputStream outputStream = clientSocket.getOutputStream()) {// 使用 try ( ) 方式, 避免后续用完了流对象, 忘记关闭.// 由于客户端发来的数据, 可能是 "多条数据", 针对多条数据, 就循环的处理.while (true) {Scanner scanner = new Scanner(inputStream);if (!scanner.hasNext()) {// 连接断开了. 此时循环就应该结束System.out.printf("[%s:%d] 客户端下线!\n", clientSocket.getInetAddress(), clientSocket.getPort());break;}// 1. 读取请求并解析. 此处就以 next 来作为读取请求的方式. next 的规则是, 读到 "空白符" 就返回.String request = scanner.next();// 2. 根据请求, 计算响应.String response = process(request);// 3. 把响应写回到客户端.//    可以把 String 转成字节数组, 写入到 OutputStream//    也可以使用 PrintWriter 把 OutputStream 包裹一下, 来写入字符串.PrintWriter printWriter = new PrintWriter(outputStream);//    此处的 println 不是打印到控制台了, 而是写入到 outputStream 对应的流对象中, 也就是写入到 clientSocket 里面.//    自然这个数据也就通过网络发送出去了. (发给当前这个连接的另外一端)//    此处使用 println 带有 \n 也是为了后续 客户端这边 可以使用 scanner.next 来读取数据.printWriter.println(response);//    此处还要记得有个操作, 刷新缓冲区. 如果没有刷新操作, 可能数据仍然是在内存中, 没有被写入网卡.printWriter.flush();// 4. 打印一下这次请求交互过程的内容System.out.printf("[%s:%d] req=%s, resp=%s\n", clientSocket.getInetAddress(), clientSocket.getPort(),request, response);}} catch (IOException e) {e.printStackTrace();} finally {try {// 在这个地方, 进行 clientSocket 的关闭.// processConnection 就是在处理一个连接. 这个方法执行完毕, 这个连接也就处理完了.clientSocket.close();} catch (IOException e) {e.printStackTrace();}}}public String process(String request) {// 此处也是写的回显服务器. 响应和请求是一样的.return request;}public static void main(String[] args) throws IOException {TcpEchoServer server = new TcpEchoServer(9090);server.start();}
}

【服务器】

内核中有一个“队列”(可以视为阻塞队列)

如果有客户端,和服务器建立连接,这个时候服务器的应用程序是不需要做出任何操作(也没有任何感知的),内核直接就完成了连接建立的流程(三次握手).

完成流程之后,就会在内核的队列中(这个队列是每个 serverSocket 都有一个这样的队列)。

排队应用程序要想和这个客户端进行通信,就需要通过一个 accept 方法把内核队列里已经建立好的连接对象,拿到应用程序中。

【question】

前面写过的 DatagramSocket, ServerSocket 都没写 close, 但是我们说这个东西都没关系但是 clientSocket 如果不关闭,就会真的泄露了!!!
DatagramSocket 和 ServerSocket,都是在程序中,只有这么一个对象.申明周期, 都是贯穿整个程序的.

而ClientSocket 则是在循环中,每次有一个新的客户端来建立连接,都会创建出新的clientSocket

每次执行这个,都会创建新的 clientSocket,并且这个 socket 最多使用到 该客户端退出(断开连接)
此时,如果有很多客户端都来建立连接~~此时,就意味着每个连接都会创建 clientSocket.当连接断开clientSocket 就失去作用了,但是如果没有手动 close此时这个 socket 对象就会占据着文件描述符表的位置

【客户端】

【question】出现一个bug

当前启动两个客户端,同时连接服务器.
其中一个客户端(先启动的客户端) 一切正常.
另一个客户端 (后启动的客户端)则没法和服务器进行任何交互,(服务器不会提示"建立连接”,也不会针对 请求 做出任何响应)

上述bug和代码结构密切相关

确实如刚才推理的现象一样,第一个客户端结束的时候,就从 processConnection 返回了就可以执行到第二次 accept 了,也就可以处理第二个客户端了~~
很明显,如果启动第三个客户端,第三个客户端也会僵硬住,又会需要第二个客户端结束才能活过来...

如何解决上述问题?让一个服务器可以同时接待多个客户端呢??

关键就是,在处理第一个客户端的请求的过程中,要让代码能够快速的第二次执行到 accept ~~~【多线程】

上述这里的关键,就是让这两个循环能够"并发"执行.
各自执行各自的,不会因为进入一个循环影响到另一个~~

【刚才出现这个问题的关键在于两重循环在一个线程里进入第二重循环的时候,无法继续执行第一个循环.
Udp 版本的服务器,当时是只有一个循环,不存在类似的问题~~(前面部署到云服务器的时候)】

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

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

相关文章

【树——数据结构】

文章目录 1.基本概念2.基本术语1.结点之间的关系描述2.结点&#xff0c;树的属性描述3.有序树&#xff0c;无序树4.森林 3.树的性质考点1考点2考点3考点4 4.树的存储结构5.树和森林的遍历 1.基本概念 结点&#xff0c;根节点&#xff0c;分支结点&#xff0c;叶子结点&#xf…

Redis系列-1 Redis介绍

背景&#xff1a; 本文介绍Redis相关知识&#xff0c;包括Redis的使用、单线程机制、事务、内存过期和淘汰机制。后续将在《三方件-3 Redis持久化机制》中介绍Redis基于RDB和AOF的持久化机制&#xff1b;在《三方件-4 Redis集群》介绍主从、哨兵和Cluster集群相关的内容&#…

python 11Pandas数据可视化实验

实验目的&#xff1a; 学会使用Pandas操作数据集&#xff0c;并进行可视化。 数据集描述&#xff1a; 该数据集是CNKI中与“中药毒理反应”相关的文献信息&#xff0c;包含文章题目、作者、来源&#xff08;出版社&#xff09;、摘要、发表时间等信息。 实验要求&#xff1…

QT-QTCreator环境配置

准备工作&#xff1a; 下载QT: 链接&#xff1a;https://pan.baidu.com/s/1prJcsC4DGqhKiXvLuPQFVA?pwd60b3 提取码&#xff1a;60b3下载WindowsKits&#xff1a; 链接&#xff1a;https://pan.baidu.com/s/1QNiS3HpbH5M5kXx5AhkqnQ?pwde2h8 提取码&#xff1a;e2h8安装的…

Python-快速搭建一个管理平台

目录 &#x1f4dc; 准备工作 一、项目介绍 ✨ 二、制作数据库表 添加信息 ⚒️ 三、运行client.exe &#x1f680; 1、连接数据库&#xff0c;选择对应表&#xff0c;生成代码 2、把后端代码依次复制到项目中 3、把前端代码依次复制到前端项目中 4、添加路由 四、运行后端项目…

[1678]旅游景点信息Myeclipse开发mysql数据库web结构java编程计算机网页项目

一、源码特点 JSP 旅游景点信息管理系统是一套完善的java web信息管理系统&#xff0c;对理解JSP java编程开发语言有帮助&#xff0c;系统具有完整的源代码和数据库&#xff0c;系统主要采用B/S模式开发。开发环境为TOMCAT7.0,Myeclipse8.5开发&#xff0c;数据库为Mysql…

数据结构:图

数据结构&#xff1a;图 前言 在自动化程序分析中&#xff0c;图和树的一些算法起到了至关重要的作用&#xff0c;所以在开始自动化程序分析的研究前&#xff0c;我用了两天复习了一遍数据结构中的图。本章主要内容有图的基本概念&#xff0c;图的存储和图相关的经典算法&…

OpenCV(二)—— 车牌定位

从本篇文章开始我们进入 OpenCV 的 Demo 实战。首先&#xff0c;我们会用接下来的三篇文章介绍车牌识别 Demo。 1、概述 识别图片中的车牌号码需要经过三步&#xff1a; 车牌定位&#xff1a;从整张图片中识别出牌照&#xff0c;主要操作包括对原图进行预处理、把车牌从整图…

数据结构--顺序表经典OJ题

例1&#xff1a;合并有序顺序表 给你两个按 非递减顺序 排列的整数数组 nums1 和 nums2&#xff0c;另有两个整数 m 和 n &#xff0c;分别表示 nums1 和 nums2 中的元素数目。请你 合并 nums2 到 nums1 中&#xff0c;使合并后的数组同样按 非递减顺序 排列。 注意&#xff…

测试开发工具开发 -JMeter 函数二次开发

在JMeter中开发自定义函数是一个常见的需求&#xff0c;允许我们扩展JMeter的功能以适应特定的测试需求。自定义函数可以用来处理数据&#xff0c;生成输出&#xff0c;或者执行特定的运算。通过JMeter函数二次开发可以帮我们解决实际测试过程中造数难的问题 用过JMeter的同学…

JAVASE->数据结构|顺序表底层逻辑

✅作者简介&#xff1a;大家好&#xff0c;我是橘橙黄又青&#xff0c;一个想要与大家共同进步的男人&#x1f609;&#x1f609; &#x1f34e;个人主页&#xff1a;再无B&#xff5e;U&#xff5e;G-CSDN博客 目标&#xff1a; 1. 什么是 List 2. List 常见接口介绍 3. …

java并发编程-AQS介绍及源码详解

介绍 AQS 的全称为 AbstractQueuedSynchronizer &#xff0c;就是抽象队列同步器。 从源码上可以看到AQS 就是一个抽象类&#xff0c;它继承了AbstractOwnableSynchronizer&#xff0c;实现了java.io.Serializable接口。 public abstract class AbstractQueuedSynchronizere…

信号与线性系统 1绪论

信号 信号是随时间变化的某种物理量&#xff08;狭义&#xff09; 本课程中&#xff0c;信号用 函数&#xff08;而且是一维函数&#xff09;表示 连续 离散 信号分类 确定与随机&#xff1a;是否能以确定的时间函数表示离散与连续&#xff1a;是用全体实数还是特定整数来描述…

普乐蛙景区vr体验馆VR游乐场设备身历其境体验

小编给大家推荐一款gao坪效产品【暗黑战车】&#xff0c;一次6人同乘&#xff0c;炫酷外观、强大性能和丰富内容适合各个年龄层客群&#xff0c;紧张刺激的VR体验让玩家沉浸在元宇宙的魅力中&#xff0c;无论是节假日还是平日&#xff0c;景区商场助力门店提高客流量和营收~ ◆…

苹果和OpenAI再续前缘,iOS 18会是颠覆级的吗?|TodayAI

据彭博社最新报道&#xff0c;苹果公司已经与人工智能领域的先锋企业OpenAI重启了对话&#xff0c;双方目前正在讨论一项可能的合作&#xff0c;以将OpenAI的生成式人工智能技术整合到苹果即将推出的iOS 18操作系统中。这一举措表明&#xff0c;苹果正加速其在人工智能技术上的…

Go Web 开发基础【用户登录、注册、验证】

前言 这篇文章主要是学习怎么用 Go 语言&#xff08;Gin&#xff09;开发Web程序&#xff0c;前端太弱了&#xff0c;得好好补补课&#xff0c;完了再来更新。 1、环境准备 新建项目&#xff0c;生成 go.mod 文件&#xff1a; 出现报错&#xff1a;go: modules disabled by G…

【JavaEE网络】网络编程及其应用概述

目录 面向字节流粘包问题 TCP异常情况TCP/UDP对比 网络层重点协议IP协议IP地址 面向字节流 粘包问题 在面向字节流的情况下&#xff0c;会产生一些其他的问题&#xff1a;粘包问题&#xff0c;这里“粘”的是“应用层数据报”&#xff0c;通过TCP read/write的数据&#xff0…

AI手机,走入小径分岔的花园

博尔赫斯在他的成名作《小径分岔的花园》里&#xff0c;描述了一种奇妙的世界观&#xff1a;一个可能性被选择之后&#xff0c;出现了许多不同的后世&#xff0c;许多不同的时间。 在现实世界中&#xff0c;选择不会如此神奇。但站在岔路口的抉择&#xff0c;也一定会带来结果的…

Mysql的关联查询以及语句

一、mysql的连接查询 1、等值连接 这里是三张表的等值连接 select rp.role_id,rp.permission_id from role_permission rp, role r, permission p where rp.role_idr.id and rp.permission_idp.id 2、内连接&#xff1a; 角色&#xff1a;系统管理员 是否拥有权限&#xf…

【Docker】docker部署lnmp和搭建wordpress网站

环境准备 docker&#xff1a;192.168.67.30 虚拟机&#xff1a;4核4G systemctl stop firewalld systemctl disable firewalld setenforce 0 安装docker #安装依赖包 yum -y install yum-utils device-mapper-persistent-data lvm2 #设置阿里云镜像 yum-config-manager --add…