Java网络编程实战:TCP/UDP Socket通信详解与高并发服务器设计

article/2025/6/23 20:50:49

在这里插入图片描述

🔍 开发者资源导航 🔍
🏷️ 博客主页: 个人主页
📚 专栏订阅: JavaEE全栈专栏

内容:

    • socket(套接字)
    • TCP和UDP差别
    • UDP编程
      • 方法
      • 使用
      • 简单服务器实现
    • TCP编程
      • 方法
      • Socket和ServerSocket之间的关系
      • 使用
      • 简单服务器实现
      • 多线程优化

socket(套接字)

在网络协议中,应用层是和程序员直接相关的,涉及到的网络通信协议也大多是程序员自定制的。

而在操作系统中提供了一组API:socket api(传输层给应用层提供的),socket相当于网卡的遥控器,我们可以通过这组api来间接操作网卡。

socket主要提供了两套,一个是TCP的,另一个是UDP,这两个核心协议差别很大,编写的时候风格不同,因此socket api提供了两套。

TCP和UDP差别

TCP:有连接,可靠传输,面向字节流,全双工
UDP:无连接,不可靠传输,面向数据报,全双工

  • 有连接 & 无连接: 对于TCP来说,TCP协议保存了对端的信息,如果A和B进行通信,A保存B的信息,B也保存A的信息,彼此都知道谁是和它建立联系的。而UDP就不会存储彼此的信息。

  • 可靠传输 & 不可靠传输: 在网络中,数据非常容易出现丢包的情况,因为光信号和电信号可能会收到外界的干扰,本来传输的数据被修改了,这样乱的数据会被丢弃掉。而可靠传输并不是保证数据包100%到达,而是尽可能的提高传输成功率,如果丢包了,也可以感知到,而可靠传输的代价就是效率降低~不可靠传输,只是把数据发送出去了,其他的不管了。

  • 面向字节流 & 面向数据报: TCP读写数据时是以字节为单位的,长度是任意的,可能会出现“粘包”问题。UPD读写数据时以一个数据报为单位,一次必须是一整个不能是半个,严格收到长度的限制,因此不会出现“粘包”问题。

  • 双全工 & 半全工: 一个通信链路层如果支持双向通信则称为双全工,如果只支持单向通信(仅能读或者写)则称之为半全工

粘包(TCP Stickiness)是指 TCP 协议在传输数据时,多个数据包被接收方当作一个包接收,导致数据解析错误的现象。主要发生在 基于流的传输协议(如 TCP),而 UDP 不会粘包,因为 UDP 是面向消息的协议,每个数据包都有明确的边界。

UDP编程

方法

DatagramSocket是JAVA为UPD提供的实现类。

构造方法:

方法用途
DatagramSocket()构建一个数据报套接字绑定到本地主机的任何可用的端口。
DatagramSocket(int port)构建一个数据报套接字绑定到本地主机的指定端口。

核心方法:

方法用途
send(DatagramPacket p)从该套接字发送数据报包。
receive(DatagramPacket p)从该套接字接收数据报包。
close()关闭该数据报套接字。

上述我们讲到UDP是以数据报为一个单位进行读写的,而在JAVA中DatagramPacket类表示一个完整的UDP数据报。

构造方法:

方法用途
DatagramPacket(byte[] buf, int length) 接收数据包长度 length 构建。
public DatagramPacket(byte[] buf, int offset, int length, SocketAddress address)发送有偏置 offset指定端口号指定主机上的数据包长度 length数据报包结构。

核心方法:

方法用途
getData() 返回数据缓冲区。
getLength() 返回要发送的数据的长度或收到的数据的长度。
getAddress() 返回的IP地址的机器,这个数据包被发送或从收到的数据报。
getSocketAddress() 获取SocketAddress(通常是IP地址+端口号)的远程主机,数据包被发送到或来自。

使用

构造数据报

DatagramPacket packet = new DatagramPacket(new byte[1024], 1024);
  1. 第一个参数:创建一个长度为1024字节的字节数组
    这个数组将作为数据包的缓冲区,用于存储接收到的网络数据
  2. 第二个参数:1024
    指定缓冲区的大小(这里是1024字节)
    表示这个数据包最多能接收1024字节的数据

看到这里不知道你是否和我一样好奇,这里为什么要这样设计呢???这里的数值反正都一样,直接传入第一个参数不就可以获得第二个参数吗。

  • 当接收长度 < 缓冲区长度时 即使数据包大于1024字节,也只会填充缓冲区的前1024字节,可以限制接收数据量,防止缓冲区溢出
  • 当接收长度 > 缓冲区长度时 会抛出IllegalArgumentException异常

推荐做法:使接收长度等于缓冲区长度
特殊场景:当只需要接收部分数据时可以使用不一致设置

接受数据报

DatagramSocket socket =  new DatagramSocket(9090);
//存储字节大小为1024字节数组。
DatagramPacket packet = new DatagramPacket(new byte[1024], 1024);
//接收数据
socket.receive(packet);
//将有效部分转化成字符串
String receive = new String(packet.getData(), 0, packet.getLength());
System.out.println("收到消息:" + receive);

getLength()方法是获取DatagramPacket 的length属性,也就是在初始时设置的1024,但是length在接受到数据报后会被改变,变成实际接收到的字节长度。

发送数据报

DatagramSocket socket =  new DatagramSocket(9090);
//存储字节大小为1024字节数组。
DatagramPacket packet = new DatagramPacket(new byte[1024], 1024);
//接收数据
socket.receive(packet);
String respose  = "你好呀";
DatagramPacket resposePacket = new DatagramPacket(respose.getBytes(), respose.getBytes().length, packet.getSocketAddress());
socket.send(resposePacket);

在这里我们将信息重新发回源地址,那么怎么知道对方的ip呢?我们接收到的数据报里面含有对方的源端口以及源ip地址,那么此时对方的源地址就是我们的目的地址,而我们的getSocketAddress()方法恰好可以返回一个带有端口以及ip地址的类。

除此之外,还要注意我们的respose.getBytes().length,这里不能使用respose.length,因为字符的长度不等于字节的长度!

简单服务器实现

如果你理解了上述的代码,那么你就可以完成一个简单的回显服务器啦~
所谓回显服务器就是你的询问是什么,回答的就是什么…

一个服务器程序通常的流程:

  1. 读取请求并解析
  2. 根据请求,计算响应。(服务器最关键的部分)
  3. 返回响应
客户端输入消息
创建DatagramPacket
发送到服务端
服务端接收packet
处理请求
创建响应packet
返回客户端
客户端显示响应

服务端代码:

import java.io.IOException;
import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.net.SocketException;
import java.util.Scanner;public class UdpechoServer {//服务端DatagramSocket socket = null;public UdpechoServer (int port) throws SocketException {socket = new DatagramSocket(port);}public void start() throws IOException {System.out.println("服务器启动了");while (true) {//存储字节大小为1024字节数组。DatagramPacket packet = new DatagramPacket(new byte[1024], 1024);//接收数据socket.receive(packet);//将有效部分转化成字符串String receive = new String(packet.getData(), 0, packet.getLength());System.out.println("收到消息:" + receive);//根据输入回复String respose = poss(receive);//构建响应的数据报。并且传入目的端口和地址DatagramPacket resposePacket = new DatagramPacket(respose.getBytes(), respose.getBytes().length, packet.getSocketAddress());socket.send(resposePacket);//打印日志System.out.printf("[%s:%d] req:%s resp:%s\n", packet.getAddress(), packet.getPort(), receive, respose);}}//处理请求的函数,根据需要进行调整;private String poss(String receive) {return receive;}public static void main(String[] args) throws IOException {UdpechoServer udpechoServer = new UdpechoServer(9090);udpechoServer.start();}
}

关键点解析:

  1. 使用while(ture)是否会出现忙等问题?

当调用 socket.receive() 时,如果内核的 接收缓冲区(Receive Buffer) 中没有数据,应用程序的线程会被操作系统挂起(进入阻塞状态),并添加到该 Socket 的等待队列中。此时线程不会占用 CPU。。

  1. 服务端的端口号如何设置?

服务端的端口号一般设置为一个固定值,这样服务端相当于一个固定的商户,而不是流动的小摊,客户可以根据这个地址一直找到你,如果你的地址一直在变化,客户怎么找到你?你总不能一直让客户去找你吧?

  1. DatagramSocket 既然有close方法,是否需要关闭?

是否需要关闭需要考虑它的生命周期是怎样的,服务器一般是7*24小时的运行的,只要你开着它你就需要保持DatagramSocket 的开启状态,而如果你的服务器关闭了,进程结束时自然会关闭所有的资源,无需手动结束。

客户端代码:

import java.io.IOException;
import java.net.*;
import java.util.Scanner;public class UdpEchoClient {//udp不会保存对方的ip和端口号,因此需要额外进行保存private DatagramSocket socket = null;private String ServerAdress = null;private  int ServerPort;public UdpEchoClient(String adress, int port) throws SocketException {this.ServerPort = port;this.ServerAdress = adress;//客户端的端口号随机生成一个空闲的就行。socket = new DatagramSocket();}public void start() throws IOException {while (true) {Scanner sc = new Scanner(System.in);System.out.print("请输入:");String input = sc.nextLine();//getByName是一个特殊的构造方法DatagramPacket packet = new DatagramPacket(input.getBytes(), input.getBytes().length,InetAddress.getByName(ServerAdress), ServerPort);socket.send(packet);//等待回复System.out.println("等待回复中...");DatagramPacket respose = new DatagramPacket(new byte[1024], 1024);socket.receive(respose);String s = new String(respose.getData(), 0, respose.getLength());System.out.println("回复:" + s);}}public static void main(String[] args) throws IOException {UdpEchoClient client = new UdpEchoClient("127.0.0.1", 9090);client.start();}
}

关键解析:

  1. 客户端如何设置端口号以及ip地址?

此处的ip地址127.0.0.1是指向的本机,毕竟咱是在本地上部署的嘛~,因此服务器的ip地址就是本机。
客户端端口号不建议使用固定值,因为咱无法控制客户端的电脑,万一这个端口号被占用了呢?因此只需要随机找一个空闲的端口号即可。

2.这里的DatagramSocket 是否需要关闭?

按理说客户端的是需要关闭的,但是咱这里用的while(ture),因此就没这个必要了。

在这里插入图片描述

运行结果
一定要先启动服务端再启动客户端!

在这里插入图片描述
在这里插入图片描述

TCP编程

方法

在JAVA中TCP协议使用ServerSocket创建一个服务端流套接字,并绑定到指定端口,作用:揽客,并把它交给Socket。

构造方法

方法定义说明
ServerSocket()创建一个绑定服务器套接字。
ServerSocket(int port)创建一个服务器套接字,绑定到指定的端口。

核心方法

方法定义说明
accept()监听要对这个套接字作出的连接并接受它。
close()关闭这个套接字。

因为TCP协议是通过字节流进行读写的,因此在JAVA中有一个专门的类Socket来实现这个读写功能,但是和UDP不同的是他一次可能会接收到多个请求。

构造方法

方法定义说明
Socket()创建一个连接的套接字,与socketimpl系统默认的类型。
Socket(InetAddress address, int port)创建一个流套接字,并将其与指定的IP地址中的指定端口号连接起来。

核心方法

方法定义说明
close()关闭这个套接字。
getPort()返回此套接字连接的远程端口号。
getOutputStream()返回此套接字的输出流。
getInputStream()返回此套接字的输入流。

Socket和ServerSocket之间的关系

他们之间和UDP的关系不同,UDP的客户端和服务端都需要一个DatagramPacket来进行发送和接受数据。

而在TCP中,ServerSocket仅用于服务端的建立连接,发送和接受数据是通过Socket的字节流来进行的。

特性Socket (客户端)ServerSocket (服务端)
作用主动连接服务端监听端口,接受客户端连接
连接方式直接连接指定IP和端口被动等待客户端连接
数据流双向通信不直接处理数据,仅管理连接

在这里插入图片描述

使用

接受信息

//服务端接受信息
ServerSocket Serversocket = new ServerSocket(9090);
Socket socket = Serversocket.accept();try (InputStream inputStream = socket.getInputStream();OutputStream outputStream = socket.getOutputStream()) {//简化输入输出Scanner sc = new Scanner(inputStream);PrintWriter printWriter = new PrintWriter(outputStream);while (true) {if (!sc.hasNext()) {break;}//获取输入String receive = sc.next();//处理数据String respose = "hello";//读写缓冲区printWriter.println(respose);//刷新缓冲区printWriter.flush();}} catch (IOException e) {throw new RuntimeException(e);} finally {socket.close();}

相比于UDP的发送与接收,TCP的流程要复杂的多。

讲解点1:读写数据

我们知道他们是通过字节流来进行读写的,因此我们在建立连接以后,只需要通过他们的输入输出流来进行操作,输入流是接受对方发送的数据,输出流是主动给对方发送数据。

此外我们不知道对方什么时候结束连接,因此需要我们使用while(ture)来一直进行判断。

讲解点2:try-resource-close

字节流使用完是需要进行关闭的,使用try()包裹内部的资源它在结束后会自动关闭,避免忘记手动关闭。

讲解点3:Scanner & PrintWriter

使用这两个类对输入输出流进行封装可以简化我们的读取操作,并且PrintWriter 的println方法在输出的时候还会帮我们加上\n,可以有效分割多次输入或输出的差别。

讲解点4:刷新缓冲区flush

当我们使用PrintWriter 的时候会出现一个问题,println仅仅是将数据发送了缓冲区内,并没有真正写入网卡中,这是因为JAVA在设计它的时候为了保证它的高效性,想让用户尽可能的多写入几次,再一起发送出去。

但是在这里它会造成我们的数据并没有实际发送出去,因此我们需要增添一个flush方法冲刷缓冲区到网卡中去。

讲解点5:长连接 & 短连接

所谓短连接就是一次连接只发送一次请求,长连接则是可以有多个请求,而TCP的连接明显是长连接的,建立一次连接客户端可以一直发送请求,直到对方断开连接。

发送信息

//需要传入地址和端口号
Socket socket = new Socket("127.0.0.1", 9090);
Scanner sc = new Scanner(System.in);
try (InputStream inputStream = socket.getInputStream();OutputStream outputStream = socket.getOutputStream()) {//简化输入输出Scanner scnet = new Scanner(inputStream);PrintWriter printWriter = new PrintWriter(outputStream);while (true) {String request = sc.next();//发送读入的数据printWriter.println(request);printWriter.flush();//返回的响应数据String respose = scnet.next();System.out.println(respose);}
} catch (IOException e) {throw new RuntimeException(e);
}

如果上一个理解了的话,这段代码应该不难理解,在这里的printWriter也是使用的println方法,这样可以约定双方的每一个请求以换行结束。

简单服务器实现

这里我们依然是一个回显服务器。

客户端Socket 服务端ServerSocket 服务端SocketA 启动服务端 new ServerSocket(9090) accept() [阻塞] 客户端连接 连接请求(SYN) 创建专属SocketA 连接确认(ACK) 数据通信阶段 发送数据(OutputStream) 返回数据(InputStream) 客户端Socket 服务端ServerSocket 服务端SocketA

服务端代码:

import java.io.*;
import java.net.ServerSocket;
import java.net.Socket;
import java.util.Scanner;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.ThreadFactory;public class TcpEchoServer {ServerSocket Serversocket = null;TcpEchoServer (int port) throws IOException {Serversocket = new ServerSocket(port);}public void start() throws IOException {System.out.println("服务器启动了~");ExecutorService executorService = Executors.newCachedThreadPool();while (true) {Socket socket = Serversocket.accept();executorService.submit(()->{try {processConnection(socket);} catch (IOException e) {throw new RuntimeException(e);}});}}private void processConnection(Socket socket) throws IOException {System.out.printf("[%s:%d]客户端上线!\n", socket.getInetAddress(), socket.getPort());try (InputStream inputStream = socket.getInputStream();OutputStream outputStream = socket.getOutputStream()) {//简化输入输出Scanner sc = new Scanner(inputStream);PrintWriter printWriter = new PrintWriter(outputStream);while (true) {if (!sc.hasNext()) {System.out.printf("[%s:%d]客户端下线!\n", socket.getInetAddress(), socket.getPort());break;}//获取输入String receive = sc.next();
//                System.out.println("yes");//处理数据String respose = pross(receive);//读写缓冲区printWriter.println(respose);//刷新缓冲区printWriter.flush();//输出日志System.out.printf("[%s:%d] req:%s rep:%s\n",socket.getInetAddress(), socket.getPort(), receive, respose);}} catch (IOException e) {throw new RuntimeException(e);} finally {socket.close();}}private String pross(String receive) {return receive;}public static void main(String[] args) throws IOException {TcpEchoServer tcpEchoServer = new TcpEchoServer(9091);tcpEchoServer.start();}
}

客户端代码:

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 {Socket socket = null;public TcpEchoclient(String adress, int port) throws IOException {socket = new Socket(adress, port);}public void start() {//用户输入内容Scanner sc = new Scanner(System.in);try (InputStream inputStream = socket.getInputStream();OutputStream outputStream = socket.getOutputStream()) {//简化输入输出Scanner scnet = new Scanner(inputStream);PrintWriter printWriter = new PrintWriter(outputStream);while (true) {String request = sc.next();//发送读入的数据printWriter.println(request);printWriter.flush();//返回的响应数据String respose = scnet.next();System.out.println(respose);}} catch (IOException e) {throw new RuntimeException(e);}}public static void main(String[] args) throws IOException {TcpEchoclient echoclient = new TcpEchoclient("127.0.0.1", 9091);echoclient.start();}
}

关键解析

  1. 是否会出现忙等问题

这里的accept方法和UPD的类似,没有信息时也会进入阻塞等待。
而在另一个循环里面sc.hasNext()如果输入流中没有数据,但连接未关闭,hasNext() 也会阻塞(线程挂起),直到数据到达或流关闭。

  1. 粘包问题

上述我们反复提到过TCP的这个问题,这里我们是通过println的换行符来对多个请求进行了分割。

除此之外,一点要记得关闭socket对象哦~

运行结果
运行顺序:先服务端再客户端。
在这里插入图片描述
在这里插入图片描述

多线程优化

通过这个代码不知道你有没有发现他并不支持多线程的操作?因为在服务端的代码里面它一次只能进行一次连接,只有这个连接断开的时候其他连接才有机会。

因此我们可以使用多线程的方法来解决这个问题:

public void start() throws IOException {System.out.println("服务器启动了~");while (true) {Socket socket = Serversocket.accept();Thread thread = new Thread(()->{try {processConnection(socket);} catch (IOException e) {throw new RuntimeException(e);}});thread.start();}
}

使用多线程操作,每个客户端都独享一个线程。

但是每次都创建和销毁这个线程开销会比较大,因此我们可以在起基础上再使用线程池进行优化。

public void start() throws IOException {System.out.println("服务器启动了~");ExecutorService executorService = Executors.newCachedThreadPool();while (true) {Socket socket = Serversocket.accept();executorService.submit(()->{try {processConnection(socket);} catch (IOException e) {throw new RuntimeException(e);}});}
}

如果客户端进一步增加达到1w或者100w,此时多线程/线程池,就会产生大量的线程,因此之后我们可以使用IO多路复用的方法来处理,感兴趣的可以自行了解。


感谢各位的观看Thanks♪(・ω・)ノ,创作不易,如果觉得有收获的话留个关注再走吧。


http://www.hkcw.cn/article/cqFPsoDYuh.shtml

相关文章

算法:滑动窗口

1.长度最小的子数组 209. 长度最小的子数组 - 力扣&#xff08;LeetCode&#xff09; 运用滑动窗口&#xff08;同向双指针&#xff09;来解决&#xff0c;因为这些数字全是正整数&#xff0c;在left位置确定的下&#xff0c;right这个总sum会越大&#xff0c;所以我们先让num…

AI笔记 - 网络模型 - mobileNet

网络模型 mobileNet mobileNet V1网络结构深度可分离卷积空间可分![在这里插入图片描述](https://i-blog.csdnimg.cn/direct/aff06377feac40b787cfc882be7c6e5d.png) 参考 mobileNet V1 网络结构 MobileNetV1可以理解为VGG中的标准卷积层换成深度可分离卷积 可分离卷积主要有…

新中地三维GIS开发智慧城市效果和应用场景

近年来&#xff0c;随着科技的发展和城市化进程的加速&#xff0c;智慧城市成为了全球各大城市的一个重要发展方向。 在这一背景下&#xff0c;三维GIS技术以其独特的优势&#xff0c;成为构建智慧城市不可或缺的工具。新中地GIS开发特训营正是在这样的大环境下应运而生&#…

Linux笔记---线程

1. 线程的介绍 1.1 线程的概念 基本定义&#xff1a; 线程&#xff08;Thread&#xff09;是操作系统能够进行运算调度的最小单位。它被包含在进程&#xff08;Process&#xff09;之中&#xff08;或者说是进程的一部分、对进程的划分&#xff09;&#xff0c;是进程中的实际…

Java数据结构之ArrayList(如果想知道Java中有关ArrayList的知识点,那么只看这一篇就足够了!)

前言&#xff1a;ArrayList是Java中最常用的动态数组实现之一&#xff0c;它提供了便捷的操作接口和灵活的扩展能力&#xff0c;使得在处理动态数据集合时非常方便。本文将深入探讨Java中ArrayList的实现原理、常用操作以及一些使用场景。 一&#xff1a;体系结构 二&#xff…

antddesign使用iconfont的字体库和图标库

antddesign使用iconfont 使用iconfont自定义字体 1️⃣选择一种需要的字体&#xff0c;点击【字体包下载】&#xff1a; 2️⃣下载好的字体放到项目目录下&#xff1a;src/assets/fonts&#xff1a; 3️⃣新建styles/font.css文件&#xff1a; /* src/styles/fonts.css */ f…

LearnOpenGL-笔记-其十二

今天我们来将LearnOpenGL的高级光照部分彻底完结&#xff1a; Bloom 泛光是一个非常常见的用于改善图像质量的手段&#xff0c;其主要做法就是将某个高亮度区域的亮度向四周发善以实现该区域更亮的视觉效果&#xff08;因为显示器的亮度范围有限&#xff0c;需要通过泛光来体…

第十二节:第一部分:集合框架:概述、Collection集合的常用方法

集合体系结构 Collection集合体系 Collection的常用方法 代码&#xff1a; 代码一&#xff1a;认识Collection体系的特点 package com.itheima.day17_Collection;import java.util.ArrayList; import java.util.HashSet;/* * 目标:认识Collection体系的特点。 * */ public cl…

C++哈希表:unordered系列容器详解

本节目标 1.unordered系列关联式容器 2.底层结构 3.模拟实现 4.哈希的应用 5.海量数据处理面试题 unordered系列关联式容器 在c98中&#xff0c;STL提供了底层为红黑树结构的一系列关联式容器&#xff0c;在查询时效率可以达到logN&#xff0c;即最差的情况下需要比较红…

非常有趣的桌面萌宠互动软件

软件介绍 这里要介绍的软件是一款在主播直播领域十分实用的萌系插件&#xff0c;它能让主播的直播更具趣味性和吸引力。 软件开发者与特性 该软件由国外高中生kuroni开发&#xff0c;是一款开源软件。其最大的亮点在于&#xff0c;能让手鼓猫的手臂跟随鼠标和按键操作做出相…

InfluxQL 数据分析实战:聚合、过滤与关联查询全解析

InfluxQL 作为时序数据库的专用查询语言&#xff0c;在处理时间序列数据时展现出独特优势。本文深入探讨 聚合计算、数据过滤和跨测量关联 三大核心操作&#xff0c;通过真实代码示例展示如何从海量时序数据中提取关键洞察。文中涵盖从基础平均值计算到复杂多维度分析的完整流程…

记一次idea中lombok无法使用的解决方案

在注解处理器下&#xff0c;一般 Default 为“启用注解处理”和“从项目类路径获取处理器”&#xff0c;但是我的项目中的为选择“处理器路径”&#xff0c;导致了无法识别lombok&#xff0c;因此&#xff0c;需要改为使用“从项目类路径获取处理器”这个选项。如下图所示&…

【速通RAG实战:进阶】17、AI视频打点全攻略:从技术实现到媒体工作流提效的实战指南

一、AI视频打点的技术底层与数据处理流程 (一)视频内容结构化的核心技术栈 AI视频打点的本质是将非结构化视频数据转化为带时间戳的结构化信息,其技术流程涵盖音视频处理、语音识别、自然语言处理三大核心模块,形成“数据采集-内容解析-智能标记-协同应用”的完整闭环。 …

Java BigInteger类详解与应用

Java BigInteger类应用详解 BigInteger的构造方法&#xff1a; 对象一旦创建&#xff0c;内部的值不能发送改变 BigInteger常见的成员方法&#xff1a; 一、对象创建 BigInteger提供两种主要构造方式&#xff1a; // 通过字符串构造 BigInteger num1 new BigInteger("…

LLM优化技术——Paged Attention

在Transformer decoding的过程中&#xff0c;需要存储过去tokens的所有Keys和Values&#xff0c;以完成self attention的计算&#xff0c;称之为KV cache。 &#xff08;1&#xff09;KV cache的大小 可以计算存储KV cache所需的内存大小&#xff1a; batch * layers * kv-he…

Java并发编程实战 Day 1:Java并发编程基础与线程模型

【Java并发编程实战 Day 1】Java并发编程基础与线程模型 开篇&#xff1a;系列定位与学习目标 欢迎来到为期30天的《Java并发编程实战》系列教程。本系列将从Java并发编程的基础知识讲起&#xff0c;逐步深入到高级特性与实战应用&#xff0c;帮助开发者构建高性能、可扩展的…

如何配置国内docker镜像源?

如何配置国内docker镜像源&#xff1f; 安装docker chattr -i /etc/passwd chattr -i /etc/shadow chattr -i /etc/group chattr -i /etc/gshadow# 下载slirp4netns rpm包 wget https://1407433742.rsc.cdn77.org/c7-extras.x86_64/slirp4netns/20200428221211/0.4.3-4.el7_…

【Ubuntu】摸鱼技巧之虚拟机环境复制

前言 提示&#xff1a;所有的操作都需要关闭虚拟机 如何快速在其它电脑布置&#xff0c;linux环境&#xff0c;如果我们有一个环境直接拷贝就有时间摸鱼呀。 1.直接复制简单粗暴 不做赘述&#xff0c;如果不会复制&#xff0c;那么请右击鼠标压缩复制 2.克隆虚拟机 2.1 …

C51单片机

1.单片机的概述 (1)微处理器(CPU) 运算器主要负责数据的算术运算和逻辑运算。 控制器:是发布命令的“决策机构”&#xff0c;负责协调和指挥整个计算机系统操作。 (2)存储器 程序存储器:用于存储程序和一些固定不变的常数和表格数据&#xff0c;一般由只读存储器(ROM)组成。…

介绍一种LDPC码译码器

介绍比特翻转译码原理以及LDPC码译码器的设计。 1 译码理论 比特翻转&#xff08;BF&#xff09;译码算法是硬判决算法的一种。 主要译码思想是&#xff1a;当有一个校验矩阵出错时&#xff0c;BF 算法认为在这个校验矩阵中一定至少存在一个位置的码字信息是错误的&#xff1…