网络编程 -- socket 套接字

news/2024/4/29 2:02:26/

文章目录

  • socket 套接字
  • UDP 和 TCP 区别
    • 1. 有连接 VS 无连接
    • 2. 可靠传输 VS 不可靠传输
    • 3. 面向字节流 VS 面向数据报
    • 4. 全双工
  • UDP 数据报套接字编程
  • TCP 数据报套接字编程

socket 套接字


本文就来学习一下 网络编程, 既然谈到了网络编程,那么要如何进行呢 ?


这里的核心就是 Socket API

在这里插入图片描述


Socket 就是操作系统,给应用程序,提供的网络编程 API


在上篇文章说过,在网络分层内传输层及其以下都是属于操作系统内核实现的,应用层是在应用程序内的,这里 socket api 就相当于 站在传输层的角度 与应用层进行交互的 ( 可以认为 socket api 是和 传输层密切相关的 )。


回忆一下 : 说过的传输层,是不是说过两个最核心的协议 UDP , TCP .


因此针对 传输层 中的 两种 核心的协议 , socket 推出了两种风格 API , 也就是 UDP 版本的 和 TCP 版本的 。(其实这里还有第三种风格 unix 域套接字, 只不过没人使用了,知道了也没啥用 ,这里就过个眼熟) .


这里我们想要学习 UDP 和 TCP 两种风格的API ,那么就需要对 UDP 和 TCP 这两种协议 有个简单的认识 , 所以下面就先来简单了解一下这两种协议

UDP 和 TCP 区别


这里先来看看 UDP 和 TCP 两种协议的 区别


UDP :

  • 无连接
  • 不可靠传输
  • 面向数据报
  • 全双工


TCP :

  • 有连接
  • 可靠传输
  • 面向字节流
  • 全双工

1. 有连接 VS 无连接


有连接 : 使用TCP 协议 ,需要连接成功才能够建立通信 ,连接建立需要对方来 接收 ,如果连接没建立好 ,通信不了. (简单来说就是 双方 需要数据传输的时候需要先建立联系,没有建立好就无法进行传输).


好比 打电话 :假设现在我要给女神打电话 , 拨号口,等待女神接听 ,等听到女神的 喂 , 就说明此时连接建立成功,可以进行交流了, 如果女神挂掉了,也会有对方正在繁忙的语音提示此时就说明没有连接建立成功,不能进行通话。


无连接 : 双方无需建立建立,直接发送消息 。


好比 : 发微信 发 qq 等 , 假设我 给女神发送早安 , 女神可能看到了也可能没看到,或者看到了但是不想回我们 。此时我们并不能知道了 女神 看没看到消息 ,这种情况就是 无连接 ,


总的来说 :TCP 就是要求双发先建立连接,连接好了,才能进行传数据。 而 UDP,直接传输数据,不需要双方建立连接。

2. 可靠传输 VS 不可靠传输


可靠传输 : 发送方知道 接收方 有没有接收到数据


注意 : 网络环境天然是复杂的 , 不可能保证传输的数据 100% 就能到达 , 比如说 拔网线 ,即便我们厉害 也不顶用。


所以可靠传输 , 传输的数据并不是百分百就能够成功的, 关键是看这里是否能 **感知 **到 传输的数据是否成功到达。


这里打电话就是可靠传输 : 比如说我们打电话,对方接听了, 我们说了一大堆事情,对方是否听到了我们是可以知道的,比如对方一直没有回应,我们就可以问一句,你听到了吗, 对方回应,此时我们就能够知道对方听到了 。


不可靠传输 : 发送方不知道接收方有没有接收到数据


这里的不可靠传输,同样于发微信或发qq消息 一样


还是发消息给女神,想要邀请女神来吃麻辣烫 ,假设将消息发送出去,

对方可以能不在线,此时无法看到消息,

对方可能太忙 (毕竟是女神吗,咋可能就一条舔狗呢) 。

对方看见了 但是不回你 (你都是舔狗 ,那不就是可有可无的吗 ,人家不会挺正常)

对方看见了 , 并接收了你的邀请消息没有发送成功 等


此时不管是那种情况,我们都无法知道 ,此时就相当于不可靠传输.


另外 : 有些软件,比如抖音 ,聊天有已读功能,此时发送消息,是能够知道对方是否接收到了 (看到了显示已读 ) 这种已读功能的就相当于可靠传输, QQ 微信 就没有这种已读功能, 就可以认为是不可靠传输 。


注意 : 可不可靠,和有没有连接没有任何关系


总的来说 : 你发送的消息 , 对方是否看的到 自己是知道的 心里有底对方看到了 ,那么就是 可靠的, 心里没底 , 不知道对方是否收到 那么就是不可靠的

3. 面向字节流 VS 面向数据报


面向字节流 : 数据传输就和 文件读写二 类似 “流式” 的


还记得 文件 IO 那片 吗, 这里的流 其实就是一种比喻的 说法,我们的数据可以 分多次发送

比如 : 发送 100 个字节的数据

可以采用 一次 传 100个字节, 一次 传 10 个字节 分 10次 ,一次传1个字节 分 100次 … 这里是不是就像水流一样。


面向数据报 : 数据传输 以 一个一个的 数据报 为基本单位 (一个数据报可能是诺干个字节,带有一定的格式 )

4. 全双工


全双工 : 一个通信通道,可以双向传输 (既可以发送,也可以接收)


图示 : 半双工 (了解即可)

在这里插入图片描述


简单了解完,UDP 和 TCP 的区别之后,下面 就来学习一下 ,两种风格的 Socket API 的使用 。


这里更详细的 UDP 和 TCP 协议 ,在后面的文章种会说到 ,不急 ,本主要还是对于 Socket 的使用.

UDP 数据报套接字编程


这里我们就基于 UDP 来编写一个简单的客户端服务器的网络通信程序


想要实现这个, 先来学习一下两个类 .


图示 :

在这里插入图片描述


1.DatagramSocket


DatagramSocketUDP Socket,用于发送和接收UDP数据报。


DatagramSocket 构造方法:

方法签名方法说明
DatagramSocket()创建一个UDP数据报套接字的Socket,绑定到本机任意一个随机端口
(一般用于客户端)
DatagramSocket(int port)创建一个UDP数据报套接字的Socket,绑定到本机指定的端口(一般用
于服务端)


DatagramSocket 方法:

方法签名方法说明
void receive(DatagramPacket p)从此套接字接收数据报(如果没有接收到数据报,该方法会阻塞等待)
void send(DatagramPacketp)从此套接字发送数据报包(不会阻塞等待,直接发送)
void close()关闭此数据报套接字


2.DatagramPacket


DatagramPacketUDP Socket 发送和接收的数据报。

DatagramPacket 构造方法:

方法签名方法说明
DatagramPacket(byte[] buf, int length)构造一个DatagramPacket以用来接收数据报,接收的数据保存在
字节数组(第一个参数buf)中,接收指定长度(第二个参数length)
DatagramPacket(byte[] buf, int offset, int length, SocketAddress address)构造一个DatagramPacket以用来发送数据报,发送的数据为字节
数组(第一个参数buf)中,从0到指定长度(第二个参数length)。address指定目的主机的IP和端口号


DatagramPacket 方法:

方法签名方法说明
InetAddressgetAddress()从接收的数据报中,获取发送端主机IP地址;
或从发送的数据报中,获取接收端主机IP地址
int getPort()从接收的数据报中,获取发送端主机的端口号;
或从发送的数据报中,获取接收端主机端口号
byte[] getData()获取数据报中的数据


有了对这些方法的认识,就可以 写一个最简单的 UDP 版本的 客户端服务器程序 回显服务器 echo server 。


一个普通的服务器 : 收到请求, 更具请求计算响应 , 返回响应。


这里 就好比 去小馆子里 吃个炒粉, 我们向店长说来份炒粉,店长听到了 ,就将这个请求告诉了厨师 ,厨师接收到了请求,就去炒粉(更具请求计算出响应) ,然后将炒出来的粉 交给我们 (返回响应) .


这里我们的回显服务器 (echo server) 会省略其中的 “根据计算计算响应” 这个步骤, 相当于 请求是啥,就返回啥 (当前这个代码没有实际的业务, 这个代码也没啥太大作用 和意义 ,只是展示了 socket api 基础用法).作为一个真正的服务器, 一定是"根据请求计算响应 " 这个环节是最最重要的 , 这里的 根据请求计算响应 其实就相当于业务逻辑。


下面就来编写代码 :


图一 :

在这里插入图片描述


图二 :

在这里插入图片描述


图三 :

在这里插入图片描述


图四 :补充一点小细节
在这里插入图片描述


到此 UDP 服务器 就写完了


附上 : UDP 服务器代码 ,注意这里最后加了一个启动服务器的 main 方法


import java.io.IOException;
import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.net.SocketException;// UDP 版本的回显服务器
public class UdpEchoServer {// 网络编程 , 本质上是要操作网卡// 但是网卡不方便直接操作,在操作系统内核中, 使用了一种特殊的叫 "socket" 这样的文件来抽象表示网卡// 因此进行网络通信 , 势必需要现有一个 socket 对象private DatagramSocket socket = null;// 对于服务器来说 , 创建 socket 对象的同时 , 要让他绑定一个具体的端口号,// 服务器一定要关联上一个具体的端口号 !!!//服务器是网络传输中 ,被动的一方,如果是操作系统随机分配的端口号,此时客户端就不知道//  这个端口是啥了,也就无法进行通信了 !!!public UdpEchoServer(int port) throws SocketException {socket = new DatagramSocket(port);}public void start() throws IOException {System.out.println("启动服务器");// 服务器不是只给一个客户提供服务就完了 ,需要服务很多客户端// 这里就可以使用循环while (true) {// 只要有客户端过来,就可以提供服务 (只要有人来买烤肠,我们就可以卖给他们)// 1. 读取客户端发来的请求  (烤肠不止一种,等待客户进行挑选,我们根据客户的需求 来烤那种肠)//receive 方法的参数是一个输出型参数,需要先构造好一个空白的 DatagramPacket 对象 ,交给 receive 来进行填充。DatagramPacket requestPacket = new DatagramPacket(new byte[4096], 4096);socket.receive(requestPacket);// 此时这个 DatagramPacket 是一个特殊的对象,并不方便直接进行处理,可以把这里包含的数据拿出来, 构造成一个字符串String request = new String(requestPacket.getData(), 0, requestPacket.getLength());// 2. 根据请求计算响应 , 由于此时是一个回显服务器, 响应和请求相同String response = process(request);// 3.把响应写回到客户端  , send 的参数也是 DatagramPacket ,需要把这个 Packet 对象构造好// 此处构造的响应对象,不能是用空的字节数组构造了,而是要使用响应数据 (response字符)DatagramPacket responsePacket = new DatagramPacket(response.getBytes(), response.getBytes().length,requestPacket.getSocketAddress());// 将 计算出来的响应发送给客户端socket.send(responsePacket);// 4. 打印一下 ,当前这次请求响应的处理中间结果System.out.printf("[%s : %d] request : %s; response: %s\n" ,requestPacket.getAddress().toString(),requestPacket.getPort(),request,response);}}// 这个方法就表示 "根据请求计算响应"public String process(String request) {return request;}public static void main(String[] args) throws IOException {// 端口号的在指定,可以随便指定// 1024 -> 65535 这个范围内 随便挑一个数组即可  , 后面会说为啥 在这个范围内UdpEchoServer server = new UdpEchoServer(9090);server.start();}}


其实上面这个服务器的代码并不太复杂核心代码也就那么几行 , 这里我们的主要任务 就是 理解服务器的工作流程。

1.读取请求并解析

2.根据请求计算响应

3.构造响应并写回给客户端 .


下面就来写 客户端代码


图一 :

在这里插入图片描述


图二 :

在这里插入图片描述


图三 :

在这里插入图片描述


附上客户端代码 :


import java.io.IOException;
import java.net.*;
import java.util.Scanner;// UDP 版本的 回显客户端
public class UdpEchoClient {// 同样需要先创建一个 socket对象 , 来操作网卡private DatagramSocket socket = null;// 服务器 的 IP 地址private String serverIp;// 服务器 的 端口号private Integer serverPort;// 一次通信 ,需要由两个 ip , 两个端口// 客户端的 ip 是 127.0.0.1 已知.// 客户端的 port 是系统自动分配的// 服务器 ip 和 端口 也需要告诉客户端 , 才能顺利把消息发给服务器.public UdpEchoClient(String serverIp, Integer serverPort) throws SocketException {socket = new DatagramSocket();//这里需要 给 服务器的 ip 地址 和 端口号 ,相当于 买家了商品 ,需要提供 地址和电话 , 如果没有卖家咋发货呢?this.serverIp = serverIp;this.serverPort = serverPort;}// 启动 客户端public void start() throws IOException {System.out.println("客户端启动!");Scanner scanner = new Scanner(System.in);while (true) {
//        1. 从控制台读取发送的数据.System.out.println("-> ");String request = scanner.next();if (request.equals("exit")) {System.out.println("goodbye");break;}//        2. 构造成 UDP 请求,并发送.// 构造这个 Packet 的时候 ,需要把 serverIp 和 port 都传入过来, 但是此处 IP 地址需要填写一个 32位的整数形式// 上述的 IP 地址是一个字符串 , 需要使用 InetAddress.getByName 来进行一个转换 .DatagramPacket requestPacket = new DatagramPacket(request.getBytes(), request.getBytes().length,InetAddress.getByName(serverIp), serverPort);// 构造好了请求发送socket.send(requestPacket);//        3. 读取服务器的 UDP 响应,并解析.DatagramPacket responsePacket = new DatagramPacket(new byte[4096], 4096);socket.receive(responsePacket);String response = new String(responsePacket.getData(), 0, responsePacket.getLength());//        4. 把解析好的结果显示出来.System.out.println(response);}}// 最后添加启动方法public static void main(String[] args) throws IOException {UdpEchoClient client = new UdpEchoClient("127.0.0.1", 9090);client.start();}}


下面来执行一下 看看下效果 :

在这里插入图片描述


可以看到我们的客户端和服务器程序 ,是在我们自己的电脑上运行的, 而实际上,网络存在的意义,是跨主机通信.


那么我们的层序是否可以做到 跨主机通信的呢?


这里是可以的, 只不过需要外网 IP ,这里可以将服务器的代码 上传到 云服务器上,让后修改 客户端的 ServerIp 改为云服务器的, 就能够进行跨主机通信的。


这里就不演示了 ,比较麻烦 . 后面我们学习了 Linux , 购买了云服务器可以自己尝试一下.


下面继续 , 这里我们写的是回显服务器,缺少业务逻辑,这里就可以在上述代码的基础上稍作调正,实现一个 “查词典” 的服务器. (根据英文单词 , 翻译成中文解释)


附上代码 : 因为大部分代码是一样的 ,这里就可以通过继承来 ,重写 process 方法 , 来书写逻辑即可 .


// 对于 DictServer 来说 和 EchoServer 相比,大部分的东西都是一样的// 主要是 "根据请求计算响应" 这个步骤不太一样 .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 设置内容dict.put("cat", "小猫");dict.put("dog", "小狗");dict.put("error", "错误");// 当然,这里可以无限多的设置键值对. .}@Overridepublic String process(String request) {// 重写我们的 process 方法, 添加逻辑即可// 查词典的过程return dict.getOrDefault(request, "当前单词没有查询到结果!");}public static void main(String[] args) throws IOException {UdpDictServer server = new UdpDictServer(9090);server.start();}
}


这里关于上面这个程序还有一个小知识点 这里来补充一下 ,

针对上述的层序,来看看端口冲突 是啥效果 .

这里一个端口只能被一个进程使用 ,如果由多个使用,就不行。

这里具体来看一下咋样个不行法 。

在这里插入图片描述


这里 UDP socket api 使用就到这里 ,下满来 看看 TCP 的 socket api 的使用 .

TCP 数据报套接字编程


关于 TCP socket api 的这个 API , 难易程度 ,可以取决于你之前学习 IO 章节的掌握程度, 如果比较熟悉那么就容易, 如果不太熟悉或没学过,那么就可能有点困难.


这里就不多说了,下面就来愉快的学习一下 TCP 的 socket api 。


同样的这里 与 UDP 一样涉及到两个类


图示 :

在这里插入图片描述


ServerSocket API


ServerSocket 是创建TCP服务端Socket的API

ServerSocket 构造方法

方法签名方法说明
ServerSocket(int port)创建一个服务端流套接字Socket,并绑定到指定端口


ServerSocket 方法

方法签名方法说明
Socket accept()开始监听指定端口(创建时绑定的端口),有客户端连接后,
返回一个服务端Socket对象,并基于该Socket建立与客户端的连接,否则阻塞等待
void close ()关闭此套接字


Socket API

Socket 是客户端Socket,或服务端中接收到客户端建立连接(accept方法)的请求后,返回的服务端 Socket。

不管是客户端还是服务端Socket,都是双方建立连接以后,保存的对端信息,及用来与对方收发数据的。


Socket 构造方法

方法签名方法说明
Socket(String host , int port)创建一个客户端流套接字Socket,并与对应IP的主机上,对应端口的进程建立连接


Socket 方法

方法签名方法说明
InetAddress getInetAddress()返回套接字所连接的地址
InputStream getInputStream()返回此套接字的输入流
OutputStream getOutputStream()返回此套接字的输出流


看完这些 类和方法, 下面通过 写一个 TCP 版本的 回显服务器来 熟悉他们.


图一 :

在这里插入图片描述


图二 :

在这里插入图片描述


附上代码 :

package T_J4.T_1_23;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;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("启动服务器");while (true) {// TCP 是有连接的, 因此不能一上来就读取数据, 需要先建立连接// 使用这个 clientSocket 和 具体的客户端进行交流 (建立连接).Socket clientSocket = serverSocket.accept();processConnection(clientSocket);}}// 使用这个方法来处理一个连接.// 这一个连接对应到一个客户端 ,但是这里可能会涉及到多次交互.private void processConnection(Socket clientSocket) {System.out.printf("[%s : %d] 客户端上线!\n", clientSocket.getInetAddress().toString(), clientSocket.getPort());// 基于上述 socket 对象 和 客户端进行通信try (InputStream inputStream = clientSocket.getInputStream();OutputStream outputStream = clientSocket.getOutputStream()) {// 由于要处理多个请求和响应 , 这里也是使用循环来处理while (true) {
//                1. 读取请求Scanner scanner = new Scanner(inputStream);if (!scanner.hasNext()) {// 判断是否有下一个数据 , 没有下一个数据说明读完了 (客户端关闭连接) ;System.out.printf("[%s : %d] 客户端下线\n", clientSocket.getInetAddress().toString(), clientSocket.getPort());break;}// 注意 !! 此处使用 next 是一直读取到换行符 / 空格 / 其他空白符结束 , 但是最终返回结果里不包含上述 空白符 .String request = scanner.next();//                2. 根据请求构造响应String response = process(request);//                3. 返回响应结果
//                outputStream 没有 write String 这样的功能 , 可以把 String 里的字节数组拿出来,进行 写入 .// 也可以用字符流来转换一下PrintWriter printWriter = new PrintWriter(outputStream);printWriter.println(response); // 此处使用println 让结果中带有 \n 换行. 方便 对端来接受解析printWriter.flush(); // flush 用来刷新缓冲区 , 保证当前写入的数据, 确实是发送出去了System.out.printf("[%s : %d] request : %s , response %s \n", clientSocket.getInetAddress().toString(), clientSocket.getPort(),request, response);}} catch (IOException e) {e.printStackTrace();} finally {// finally 中的代码一定会被执行,所以 close 放在这里更加合适try {clientSocket.close();} catch (IOException e) {e.printStackTrace();}}}private String process(String request) {return request;}public static void main(String[] args) throws IOException {TcpEchoServer server = new TcpEchoServer(9090);server.start();}
}


这里 TCP版本的服务器写完了,下面来写 TCP 版本的客户端.


图一 :

在这里插入图片描述


图二 :

在这里插入图片描述


最后加上 main 方法 来启动 客户端 。


附上代码 :


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 构造方法 , 能够识别 点分十进制格式的 IP 地址 . 比 DatagramPacket 更方便 .// new 这个对象的同时 , 就会 进入 TCP 的连接操作.socket = new Socket(serverIp, serverPort);}public void start() {System.out.println("客户端启动!");Scanner scanner = new Scanner(System.in);try (InputStream inputStream = socket.getInputStream();OutputStream outputStream = socket.getOutputStream()) {while (true) {// 1. 先从键盘上读取用户输入的内容System.out.printf("> ");String request = scanner.next();if (request.equals("exit")) {System.out.println("goodbye");break;}// 2. 把读到的内容构造成请求,发送给服务器PrintWriter printWriter = new PrintWriter(outputStream);printWriter.println(request);// 此处加上一个 flush 保证数据确实发送出去了printWriter.flush();// 3. 读取服务器的响应Scanner respScanner = new Scanner(inputStream);String response = respScanner.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();}
}


这里执行以下, 来看一下效果如何 :

在这里插入图片描述


效果看完,这里提一个小问题 :

在这里插入图片描述


看完这个小问题 ,其实我们的代码还有一个大问题, 这里来演示一下 问题所在 :


图一 :

在这里插入图片描述


图二 :

在这里插入图片描述


优化 : 这里使用多线程,可以解决 一个服务器 只能处理一个客户端的问题, 但是客户端特别多,很多客户端频繁的来进行连接 , 此时就需要频繁的创建 / 销毁线程。


之前说过 创建 线程和 销毁线程 也是会消耗资源的 (虽然比 多进程 少) ,这里 频繁的创建和销毁 开销也是比较大的 。


那么 你能 回忆起之前学过的 知识 来优化它 吗 ?

想必你一定有了答案 , 没错就是线程池 , 下面再次调整我们的代码 :

在这里插入图片描述


补充一下 : TCP 的短链接 和 长连接

在这里插入图片描述


另外 : 这里虽然使用了线程池,但是还不够


如果 客户端非常多,而且客户端连接迟迟不肯断开,就会导致我们的机器上有很多线程 . (我们使用的线程池是自增的,觉得需要线程了就会自己创建) .


按照上面这种迟迟不肯断开的情况下 , 如果一个服务器 有几千个客户端,就的是几千个线程,如果是几万个客户端 , 就会有几万个线程 。


此时这种情况就会对我们的机器产生很大的负担 。


那么如何确解决这个问题呢 ?


方法一 : 采用多开服务器 ,但是多开服务器意味着成本的增加 , 得多加钱,明显是不好的选择 。


方法二 : 使用 IO 多路复用 (IO 多路转接)


关于这里的问题 ,其实就是 单机(单个服务器)如何支持更大量客户端的问题 , 这个问题 也可以称为 C 10M 问题.


C 10k 问题 : 单机处理 1w个客户端 , 这里 1w 个 ,我们通过 一些方法 ,还是能够解决的 。

C 10M 问题 : 但其处理 1Kw个 客户端 ,这里 不是说单机真的能处理 1kw 个 客户端,只是表达说 客户端比 C 10k 这里多很多 。


出现 C 10M 问题 的本质 就是 机器 承担不了 这么线程的开销 (每个线程处理一个客户端) ,


好比 你开了一家公司 , 你的公司 需要服务很多客户 ,然而你给每个客户都有一个专属的客服 , 因为你雇佣了很多客服 ,导致 月底了 你发不出工资 ,此时 你的公司自然就宣布 破产了。 。


这里解决办法很容易 ,不用雇佣那么多客服,而每名客服对接多名用户即可 ,这样就大大的减少了 客服人员 。


放在我们这里也是同理 ,我们想要一个线程处理多个客户端 连接,就可以采取 IO 多路复用 (IO 多路转接) , 别觉得这个东西很高大上,其实再生活中都接触过 。


比如说 : 买饭 , 假设 你 想吃 杂粮煎饼 , 你的弟弟想吃 牛肉面 , 你的 妹妹 想吃 肉夹馍 。


此时就有两种做法 :

1.采用单线程的方式 , 你作为老大 ,一个人去买 ,先去买你吃的 杂粮煎饼 ,然后去帮妹妹买 肉夹馍,最后卖牛肉面 .

2.采用多线程的方式 , 你说 要吃自己去买 ,然后就各买各的 。

3.还是采用单线程的方式 , 还是你去买饭 ,但是这次不同了,你来到 卖杂粮煎饼的摊子前 ,说老板给我来份杂粮煎饼 ,等会做好了我来那,然后你就跑到肉夹馍的摊子 如法炮制, 最后来到了 牛肉面的摊子 ,点了份牛肉面,此时那个好了就去拿那个 。


这三种方式 , 明显是第三种更好 ,我们的IO 复用就是这种操作 , 虽然是单线程 ,但是充分的利用了等待时间,再等待的过程中做其他事情。


这里我们IO 过程中 ,并不会一直持续的 ,IO过程也会有等待, 所以 IO 多路复用就抓住了这一点, 将这个等待时间充分利用起来, 做别的事情 .


知道了 IO 复用是啥 ,那么来处理 C 10M问题 。


思路 : 这里就可以 给线程安排个集合 ,这个集合就放了一堆连接 ,线程就负责监听这个集合,那个连接有数据来了 , 线程就来处理那个连接 … 这个其实就应用了一个事实 , 虽然连接很多 ,但是这些连接的请求并非严格意义上的同时,总还是有先有后的 。


解决 :多路复用 在操作系统里 ,提供了一些原生 API select , poll , epoll , 在 java 提供了一组 NIO 类 就封装了上述多路复用的 API , 我们就可以使用这个 类来解决问题


这里 思路 大于 API 的使用 , 这里就不继续下去了 ,如果感兴趣 可以自己去看看这个 NIO 类 .


到此 本文结束 , 下文预告 网络原理 。


http://www.ppmy.cn/news/20481.html

相关文章

软件设计(二)

软件设计(二)

软件设计&#xff08;一&#xff09;https://blog.csdn.net/ke1ying/article/details/128779601 11、windows系统当双击.jpg文件的时候&#xff0c;系统会通过建立的 文件关联 来决定使用什么程序来打开该图像文件。 &#xff08;双击.jpg文件&#xff0c;系统会建立‘window…
阅读更多...
【FreeRTOS】详细讲解FreeRTOS的软件定时器及通过示例讲述其用法

【FreeRTOS】详细讲解FreeRTOS的软件定时器及通过示例讲述其用法

软件定时器 所谓定时器&#xff0c;也就可以类比生活中人们常用的闹钟&#xff0c;可以单次响铃提醒&#xff0c;也可以间隔固定时间响铃提醒&#xff1b;与FreeRTOS定时器不同的是周期不同&#xff0c;FreeRTOS的周期更加短&#xff0c;一般使用毫秒(ms)、秒(s)。   软件定时…
阅读更多...
【SpringCloud】Gateway服务网关的基本使用

【SpringCloud】Gateway服务网关的基本使用

一、初识Gateway服务网关为什么需要网关&#xff1f;在微服务中&#xff0c;各个模块之间的调用&#xff0c;也可以称其为远程调用&#xff01;但是&#xff0c;如果是外部&#xff08;用户&#xff09;对微服务进行访问时&#xff0c;发的请求能不加处理的直接访问微服务吗&am…
阅读更多...
【全套完结】通信原理----全套Matlab仿真实验报告

【全套完结】通信原理----全套Matlab仿真实验报告

&#x1f4a5;&#x1f4a5;&#x1f49e;&#x1f49e;欢迎来到本博客❤️❤️&#x1f4a5;&#x1f4a5; 本人持续分享更多关于电子通信专业内容以及嵌入式和单片机的知识&#xff0c;如果大家喜欢&#xff0c;别忘点个赞加个关注哦&#xff0c;让我们一起共同进步~ &#x…
阅读更多...
2Pai半导体-推出π122E61双通道数字隔离器 智能分压技术 兼容代替Si8622ET-IS

2Pai半导体-推出π122E61双通道数字隔离器 智能分压技术 兼容代替Si8622ET-IS

2Pai半导体-推出π122E61双通道数字隔离器 智能分压技术 兼容代替Si8622ET-IS 电路简单、稳定性更高 &#xff0c;具有出色的性能特征和可靠性&#xff0c;整体性能优于光耦和基于其他原理的数字隔离器产品。 产品传输通道间彼此独立&#xff0c;可实现多种传输方向的配置&…
阅读更多...
微信小程序页面跳转三种方式

微信小程序页面跳转三种方式

为了不让用户在使用小程序时造成困扰&#xff0c;微信小程序规定页面路径只能是五层&#xff0c;请尽量避免多层级的交互方式。 页面跳转的话就涉及到了多个页面层级 第一种&#xff1a;wx.navigateTo(OBJECT) 保留当前页面&#xff0c;跳转到应用内的某个页面&#xff0c;使用…
阅读更多...
c++实现堆排序

c++实现堆排序

看了一下优先队列&#xff0c;查了一下堆排序。堆排序主要就是建最大堆&#xff08;最小堆&#xff09;和交换2个操作。如果建的是最大堆&#xff0c;那么交换的时候&#xff0c;父节点就和最大的子节点比较&#xff0c;如果它比最大的子节点还大&#xff0c;那就不用比了。因为…
阅读更多...
Android 实现隐私政策提示弹窗(完整版)

Android 实现隐私政策提示弹窗(完整版)

android studio版本&#xff1a;2021.2.1例程名称&#xff1a;pravicydialog功能&#xff1a;1、启动app后弹窗隐私协议2、屏蔽返回键3、再次启动不再显示隐私协议。本例程的绝大部分代码来自下面链接&#xff0c;因为本人改了一些&#xff0c;增加了一些功能&#xff0c;所以不…
阅读更多...
深度学习论文: Multi-modal Sensor Fusion for Auto Driving Perception: A Survey

深度学习论文: Multi-modal Sensor Fusion for Auto Driving Perception: A Survey

深度学习论文: Multi-modal Sensor Fusion for Auto Driving Perception: A Survey Multi-modal Sensor Fusion for Auto Driving Perception: A Survey PDF: https://arxiv.org/pdf/2202.02703.pdf PyTorch代码: https://github.com/shanglianlm0525/CvPytorch PyTorch代码: h…
阅读更多...
【Python】request库InsecureRequestWarning的原因

【Python】request库InsecureRequestWarning的原因

今天在测试我自己写的api的时候&#xff0c;发现了之前出现的一个warning的真正原因 1.起因 先来看请求api的代码 def ApiRq(account:str,passwd:str,background):url "https://example.com/shop-url"params { # 参数涉及到隐私&#xff0c;省略}res requests.…
阅读更多...
《JavaScript 核心原理解析》学习笔记 Day 13 作用域 对象

《JavaScript 核心原理解析》学习笔记 Day 13 作用域 对象

在 JavaScript 中&#xff0c;向没有声明的变量名赋值&#xff0c;会隐式地创建一个全局变量&#xff1b;全局变量会被绑定为全局对象&#xff08;global&#xff09;的属性。因此&#xff0c;JavaScript 的变量环境&#xff08;或者全局环境&#xff09;与对象系统就关联了起来…
阅读更多...
【算法】二分法 ② ( 排序数组中查找目标值 | 二分法的经典写法 | 在排序数组中查找元素的最后一个位置 | 二分法的通用模板 )

【算法】二分法 ② ( 排序数组中查找目标值 | 二分法的经典写法 | 在排序数组中查找元素的最后一个位置 | 二分法的通用模板 )

文章目录一、排序数组中查找目标值 ( 二分法的经典写法 )二、在排序数组中查找元素的最后一个位置 ( 二分法的通用模板 )一、排序数组中查找目标值 ( 二分法的经典写法 ) https://leetcode.cn/problems/binary-search/ 典型的二分查找题目 : 从一个 有序数组 中查找某个 目标值…
阅读更多...
Lua 垃圾回收

Lua 垃圾回收

Lua 垃圾回收 参考至菜鸟教程。 Lua 采用了自动内存管理。 这意味着你不用操心新创建的对象需要的内存如何分配出来&#xff0c; 也不用考虑在对象不再被使用后怎样释放它们所占用的内存。 Lua运行了一个垃圾收集器来收集所有死对象&#xff08;即在Lua中不可能再访问到的对象&…
阅读更多...
LeetCode-26. 删除有序数组中的重复项

LeetCode-26. 删除有序数组中的重复项

目录题目分析双指针理解代码实现题目来源 26. 删除有序数组中的重复项 题目分析 解法&#xff1a; 双指针 首先注意数组是有序的&#xff0c;那么重复的元素一定会相邻。 要求删除重复元素&#xff0c;实际上就是将不重复的元素移到数组的左侧。 考虑用 2 个指针&#xff0c;…
阅读更多...
STM32—超声波测距

STM32—超声波测距

超声波简介 超声波测距模块是用来测量距离的一种产品&#xff0c;通过发送和收超声波&#xff0c;利用时间差和声音传播速度&#xff0c; 计算出模块到前方障碍物的距离。 型号&#xff1a;HC-SR04 时序图 怎么让它发送波 Trig触发信号&#xff0c;给Trig端口至少10us的高电平…
阅读更多...
Golang如何优雅接入多个远程配置中心?

Golang如何优雅接入多个远程配置中心?

本文基于viper实现了apollo多实例快速接入&#xff0c;授人以渔&#xff0c;带着大家读源码&#xff0c;详解实现思路&#xff0c;封装成自己的工具类并且开源。 前言 viper是适用于go应用程序的配置解决方案&#xff0c;这款配置管理神器&#xff0c;支持多种类型、开箱即用、…
阅读更多...
介绍一款2023年新出的mysql管理工具: FlyBird Database Manager

介绍一款2023年新出的mysql管理工具: FlyBird Database Manager

FlyBird Database Manager 介绍 FlyBird Database Manager 是一款mysql 界面化管理工具&#xff0c; 使用go语言编写&#xff0c;天然支持Windows, MacOS, Linux等主流平台。 提供无需安装的命令行版本&#xff0c; 命令行中启动服务&#xff0c;以html支持UI界面,在浏览器中…
阅读更多...
UVM实战笔记(七)

UVM实战笔记(七)

第七章. UVM中的寄存器模型 7.1 寄存器模型简介 7.1.1 带寄存器配置总线的DUT 本章节使用的DUT带寄存器配置&#xff0c;代码如下&#xff1a; module dut(clk,rst_n,bus_cmd_valid,bus_op,bus_addr,bus_wr_data,bus_rd_data,rxd,rx_dv,txd,tx_en)input clk; …
阅读更多...
三层交换机报文转发过程

三层交换机报文转发过程

如图所示&#xff0c;假如主机A想访问主机B&#xff0c;首先主机A会将自己的IP地址和子网掩码做与操作,得出网路地址(如:Host-A的IP地址100.1.1.2与自身掩码255.255.255.0做与操作后,得到的网络号是100.1.1.0).然后判断目的IP地址(即Host-B的IP地址)与自己的网络地址是不是在同…
阅读更多...
CMMI之度量与分析(MA)

CMMI之度量与分析(MA)

目的度量与分析&#xff08;Measurement and Analysis&#xff0c; MA&#xff09;的目的在于开发并保持用于支持管理信息需要的度量能力概述“度量与分析”过程域涉及以下活动&#xff1a;• 明确说明度量与分析的目标&#xff0c;使其与所识别的信息需要及项目、组织级或业务…
阅读更多...
最新文章

聚圣源绿色椅子在线经商起名大全郑州起名馆哪家好总裁在上我在下小说李依后面起个女孩名字属猪宝宝起名宜用字大全男免费测算起名吉凶氵字偏旁的字有哪些字起名字好八佰演员表捏捏大爆炸酒店起什么名字付字男人起名科技公司起名规范邬字起名有寓意家居用品公司起名艺字起名搭配女孩湖南卫视我爱不一样母的小法斗起什么名字www.javlibrary.com属兔和什么属相最配一起发企业名录网起名子女孩起名八字占卜雁归西窗月电视剧免费观看完整版夏洛特·海瑟林克回到明朝当王爷小说汕头企业起名沉思的意思瑞字起名男孩人名钢材注册公司起名淀粉肠小王子日销售额涨超10倍罗斯否认插足凯特王妃婚姻让美丽中国“从细节出发”清明节放假3天调休1天男孩疑遭霸凌 家长讨说法被踢出群国产伟哥去年销售近13亿网友建议重庆地铁不准乘客携带菜筐雅江山火三名扑火人员牺牲系谣言代拍被何赛飞拿着魔杖追着打月嫂回应掌掴婴儿是在赶虫子山西高速一大巴发生事故 已致13死高中生被打伤下体休学 邯郸通报李梦为奥运任务婉拒WNBA邀请19岁小伙救下5人后溺亡 多方发声王树国3次鞠躬告别西交大师生单亲妈妈陷入热恋 14岁儿子报警315晚会后胖东来又人满为患了倪萍分享减重40斤方法王楚钦登顶三项第一今日春分两大学生合买彩票中奖一人不认账张家界的山上“长”满了韩国人?周杰伦一审败诉网易房客欠租失踪 房东直发愁男子持台球杆殴打2名女店员被抓男子被猫抓伤后确诊“猫抓病”“重生之我在北大当嫡校长”槽头肉企业被曝光前生意红火男孩8年未见母亲被告知被遗忘恒大被罚41.75亿到底怎么缴网友洛杉矶偶遇贾玲杨倩无缘巴黎奥运张立群任西安交通大学校长黑马情侣提车了西双版纳热带植物园回应蜉蝣大爆发妈妈回应孩子在校撞护栏坠楼考生莫言也上北大硕士复试名单了韩国首次吊销离岗医生执照奥巴马现身唐宁街 黑色着装引猜测沈阳一轿车冲入人行道致3死2伤阿根廷将发行1万与2万面值的纸币外国人感慨凌晨的中国很安全男子被流浪猫绊倒 投喂者赔24万手机成瘾是影响睡眠质量重要因素春分“立蛋”成功率更高?胖东来员工每周单休无小长假“开封王婆”爆火:促成四五十对专家建议不必谈骨泥色变浙江一高校内汽车冲撞行人 多人受伤许家印被限制高消费

聚圣源 XML地图 TXT地图 虚拟主机 SEO 网站制作 网站优化