用TCP实现服务器与客户端的交互

目录

 一、TCP的特点

二、API介绍 

1.ServerSocket

 2.Socket

三、实现服务器

四、实现客户端

五、测试解决bug

1.客户端发送了数据之后,并没有响应

2.clientSocket没有执行close()操作

3.尝试使用多个客户端同时连接服务器

六、优化

1.短时间有大量客户端访问并断开连接

2.有大量的客户端长时间在线访问

七、源码


引言:

这篇文章主要是用TCP构造的回显服务器,也就是客户端发什么,就返回什么。用实现这个过程方式来学会TCP套接字的使用。

 一、TCP的特点

  • TCP是可靠的:这个需要去了解TCP的机制,这是一个大工程,博主后面写好了把连接附上
  • TCP是面向字节流
  • TCP是全双工
  • TCP是有连接

除了可靠性,在编程中无法体会到,其他特性我都会一 一讲解。

二、API介绍 

1.ServerSocket

ServerSocket 是创建TCP服务端Socket的API

ServerSocket 构造⽅法:

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

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

关闭此套接字

 2.Socket

Socket 是客⼾端Socket,或服务端中接收到客⼾端建⽴连接(accept⽅法)的请求后,返回的服务端Socket。
不管是客⼾端还是服务端Socket,都是双⽅建⽴连接以后,保存的对方信息,及⽤来与对⽅收发数据的

 Socket构造方法:

方法签名方法说明
Socket(String host, int port)

创建一个客户端流套接字Socket,并与对应IP的主机上,对应端口的进程建立连接。

host:IP地址

prot:端口号

 这里new出来就是和对方建立完成了。如果建立失败,就会在构造对象的时候抛出异常。

Socket方法:

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

三、实现服务器

服务器需要指定端口号:

public class TcpEchoServer {private ServerSocket serverSocket = null;//               需要指定服务器的端口    处理ServerSocket抛出的异常public TcpEchoServer(int port) throws IOException {//                     指定服务器的端口serverSocket = new ServerSocket(port);}
}

注意处理抛出的异常

和客户端建立连接:

public class TcpEchoServer {private ServerSocket serverSocket = null;//               需要指定服务器的端口    处理ServerSocket抛出的异常public TcpEchoServer(int port) throws IOException {//                     指定服务器的端口serverSocket = new ServerSocket(port);}public void start() throws IOException {//服务器需要不停的执行while (true) {//开始监听指定端口,当有客户端连接后,返回一个保存对方信息的SocketSocket clientSocket = serverSocket.accept();//处理逻辑processConnection(clientSocket);}}//针对一个连接,提供处理逻辑private void processConnection(Socket clientSocket) {}
}

这里的accept()就体现了TCP的有连接

当连接成功后,需要处理的逻辑:

    //针对一个连接,提供处理逻辑private void processConnection(Socket clientSocket) {//打印客户端的信息                               返回IP地址                      返回端口号System.out.printf("[%s : %d]客户端上线\n",clientSocket.getInetAddress(), clientSocket.getPort());//获取到socket中持有的流对象try(InputStream inputStream = clientSocket.getInputStream();OutputStream outputStream = clientSocket.getOutputStream()) {while (true) {//1.获取请求//2.处理请求//3.返回响应//4.打印日志}}catch (IOException e) {}}

全双工的意思:通信的双方(如客户端和服务器)可以在同一时间内同时进行数据的发送和接收,即两个方向的数据流可以同时传输,互不干扰。

这里的getInputStream、getOutputStream就体现了全双工和面向字节流。

不了解这两个接口的可以去看我这篇文章:

JAVA如何操作文件?(超级详细)_java操作文件-CSDN博客

实现处理逻辑:

    //针对一个连接,提供处理逻辑private void processConnection(Socket clientSocket) {//打印客户端的信息                               返回IP地址                      返回端口号System.out.printf("[%s : %d]客户端上线\n",clientSocket.getInetAddress(), clientSocket.getPort());//获取到socket中持有的流对象try(InputStream inputStream = clientSocket.getInputStream();OutputStream outputStream = clientSocket.getOutputStream()) {//因为我们用字符串来做为数据传输,用Scanner就可以更方便的传输了Scanner scanner = new Scanner(inputStream);//包装输出流,主要是用println()会在数据之后加上\nPrintWriter printWriter = new PrintWriter(outputStream);while (true) {//1.获取请求if (!scanner.hasNext()) {//如果scanner无法读取出数据,说明客户端断开了连接,导致服务器这边读取到”末尾“break;}//2.处理请求//接收客户端的请求//如果遇到 空白字符 就会停止输入String request = scanner.next();//处理请求String response = process(request);//3.返回响应//此处可以按字节数组的形式,但是我们要输入的是字符串,这个就不太方便//outputStream.write(response.getBytes());//此方法在写入之后会自动加上\nprintWriter.println(response);//4.打印日志System.out.printf("[%s : %d] 请求 = %s 响应 = %s \n",clientSocket.getInetAddress(),clientSocket.getPort(),request,response);}}catch (IOException e) {throw new RuntimeException();}}private String process(String request) {//由于我们是回显服务器这里直接返回就可以了return request;}

注意里面使用了两个接口包装了一下输入输出流,最主要的是可以在用\n做为分割。

注意里面的:

发送字符串给客户端,最后会自动加上 \n 做为结尾

println(response);

接收客户端信息,以空白符做为结尾。

空白符:包括不限于 空格、回车、制表符……

scanner.next();

如果是nextLine()就比较严格,必须是\n做为结尾

这里的服务器处理逻辑就写完了,但其实这里还有三个错误,后面再单独讲解:

public class TcpEchoServer {private ServerSocket serverSocket = null;//               需要指定服务器的端口    处理ServerSocket抛出的异常public TcpEchoServer(int port) throws IOException {//                     指定服务器的端口serverSocket = new ServerSocket(port);}public void start() throws IOException {//服务器需要不停的执行while (true) {//开始监听指定端口,当有客户端连接后,返回一个保存对方信息的SocketSocket clientSocket = serverSocket.accept();//处理逻辑processConnection(clientSocket);}}//针对一个连接,提供处理逻辑private void processConnection(Socket clientSocket) {//打印客户端的信息                               返回IP地址                      返回端口号System.out.printf("[%s : %d]客户端上线\n",clientSocket.getInetAddress(), clientSocket.getPort());//获取到socket中持有的流对象try(InputStream inputStream = clientSocket.getInputStream();OutputStream outputStream = clientSocket.getOutputStream()) {//因为我们用字符串来做为数据传输,用Scanner就可以更方便的传输了Scanner scanner = new Scanner(inputStream);//包装输出流,主要是用println()会在数据之后加上\nPrintWriter printWriter = new PrintWriter(outputStream);while (true) {//1.获取请求if (!scanner.hasNext()) {//如果scanner无法读取出数据,说明客户端断开了连接,导致服务器这边读取到”末尾“break;}//2.处理请求//接收客户端的请求//如果遇到 空白字符 就会停止输入String request = scanner.next();//处理请求String response = process(request);//3.返回响应//此处可以按字节数组的形式,但是我们要输入的是字符串,这个就不太方便//outputStream.write(response.getBytes());//此方法在写入之后会自动加上\nprintWriter.println(response);//4.打印日志System.out.printf("[%s : %d] 请求 = %s 响应 = %s \n",clientSocket.getInetAddress(),clientSocket.getPort(),request,response);}}catch (IOException e) {throw new RuntimeException();}}private String process(String request) {//由于我们是回显服务器这里直接返回就可以了return request;}public static void main(String[] args) throws IOException {TcpEchoServer server = new TcpEchoServer(8080);server.start();}
}

四、实现客户端

指定服务器的IP和端口号:

public class TcpEchoClient {private Socket socket = null;public TcpEchoClient(String serverIP, int serverPort) throws IOException {//这里只要建立实例,就是和服务端的accept()建立了连接//socket也就保存了服务器的IP和端口号等//需要传入服务器的 IP地址 和 端口号socket = new Socket(serverIP, serverPort);}public void start() {System.out.println("客户端启动!");}}

整体逻辑:

public class TcpEchoClient {private Socket socket = null;public TcpEchoClient(String serverIP, int serverPort) throws IOException {//需要传入服务器的 IP地址 和 端口号//这里只要建立实例,就是和服务端的accept()建立了连接socket = new Socket(serverIP, serverPort);}public void start() {System.out.println("客户端启动!");try(OutputStream outputStream = socket.getOutputStream();InputStream inputStream = socket.getInputStream()) {while (true) {//1.从控制台获取数据//2.将数据发送给服务器//3.接收服务器响应//4.打印相关结果}}catch (IOException e) {throw new RuntimeException();}}
}

整体逻辑实现:

public class TcpEchoClient {private Socket socket = null;public TcpEchoClient(String serverIP, int serverPort) throws IOException {//这里只要建立实例,就是和服务端的accept()建立了连接//socket也就保存了服务器的IP和端口号等//需要传入服务器的 IP地址 和 端口号socket = new Socket(serverIP, serverPort);}public void start() {System.out.println("客户端启动!");try(OutputStream outputStream = socket.getOutputStream();InputStream inputStream = socket.getInputStream()) {//用来接收服务器的信息Scanner scanner = new Scanner(inputStream);//用于接收用户输入Scanner scannerIn = new Scanner(System.in);//用于输出数据给服务器PrintWriter printWriter = new PrintWriter(outputStream);while (true) {//1.从控制台获取数据System.out.print("->");String request = scannerIn.next();//2.将数据发送给服务器printWriter.println(request);//3.接收服务器响应//判断服务端是否还有信息if (!scanner.hasNext()) {break;}//接收服务端信息String response = scanner.next();//4.打印相关结果System.out.println(response);}}catch (IOException e) {throw new RuntimeException();}}public static void main(String[] args) throws IOException {
//                                              127.0.0.1是专门用来访问自己的TcpEchoClient client = new TcpEchoClient("127.0.0.1",8080);client.start();}
}

这里仍然纯在一个问题,一会和服务器的问题一起将

五、测试解决bug

最后我会把所有的问题解决了,再把源附上

1.客户端发送了数据之后,并没有响应

 先运行服务器,再运行客户端

可以看到目前还是成功的,那么我们来输入数据。

我们在客户端输入了消息,但是没有任何反应了!

此处的情况是,客户端并没有真正把请求发出去:

PrintWriter这样的类,以及很多IO流中的类,都是 “自带缓冲区” 的。

此方法就带有缓冲区:

printWriter.println(request);

引入缓冲区之后,进行写入数据的操作,并不会马上触发IO,而是先放到内存缓冲区中,等到缓冲区里攒了一波之后,再统一进行发送。

为什么引入缓冲区的机制?

因为IO操作其实是不小的开销,如果数据量较少,那么每一次都进行IO,就有很大一部分开销是IO操作。如果积累到一定数据量再进行IO操作,那么一次IO就传输了这么多数据。

我们可以使用flush方法,主动“刷新缓冲区”:

注意:

服务器 和 客户端 都需要在printWriter.println();后面加上flush()方法。

再来测试:

此时就可以接收到了

2.clientSocket没有执行close()操作

这个问题比较隐蔽,这些ServerSocket 和 Socket 每一次都会在“文件描述符”中创建一个新的表项。

文件描述符:描述了该进程都要操作哪些文件。数组的每个元素就是一个struct file对象,每个结构体就描述了对应的文件信息,数组的小标就称为“文件描述符”。

每次打开一个文件,就想当于在数组上占用了一个位置,而这个数组又是不能扩容的,如果数组满了就会打开文件失败。除非主动调用close才会关闭文件,或者这个进程直接结束了这个数组也被带走了。

那么我们就需要处理一下clientSocket:

 3.尝试使用多个客户端同时连接服务器

要对同一代码启动多个进程,需要设置一下步骤:

分别启动客户端1 和 客户端2 ,可以看到服务器上根本没有第二个客户端启动的信息:

原因:

我们可以用多线程去执行专门执行每一个客户端的请求:

    public void start() throws IOException {//服务器需要不停的执行while (true) {//开始监听指定端口,当有客户端连接后,返回一个保存对方信息的SocketSocket clientSocket = serverSocket.accept();//让一个线程去对应一个客户端Thread thread = new Thread(() -> {//处理逻辑processConnection(clientSocket);});thread.start();}}

结果:

bug问题解决了,但还有一些场景,可能会把服务器干崩溃

六、优化

1.短时间有大量客户端访问并断开连接

一旦短时间内有大量的客户端,并且每个客户端请求都是很快的连接之后并退出的,这个时候对于服务器来说,就会有比较大的压力。这个时候,就算是进程比线程更加的轻量,但是短时间内有大量的线程创建销毁,就无法忽略它的开销了。

我们可以引入线程池,这样就解决了这个问题:

    public void start() throws IOException {//服务器需要不停的执行while (true) {//开始监听指定端口,当有客户端连接后,返回一个保存对方信息的SocketSocket clientSocket = serverSocket.accept();ExecutorService service = Executors.newCachedThreadPool();//            //让一个线程去对应一个客户端
//            Thread thread = new Thread(() -> {
//                //处理逻辑
//                try {
//                    processConnection(clientSocket);
//                } catch (IOException e) {
//                    e.printStackTrace();
//                }
//            });
//            thread.start();service.submit(() -> {try {processConnection(clientSocket);} catch (IOException e) {e.printStackTrace();}});}}

这个线程可创建的线程数是很大的:

2.有大量的客户端长时间在线访问

例如直播这样的情况,每个客户端分配一个线程,对于一个系统来说,这里搞几百个线程压力就非常大了。所以这里 线程池/线程 都不太适用了。

可以使用 IO多路复用 ,也就是一个线程分配多个客户端进行服务,因为大部分时间线程都是在等待状态,就能够让线程分配多个客户端,这样的机制我们做为java程序员不需要过多了解,这样的机制以及被大佬们,装进各种框架中了。

七、源码

服务器源码:

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;//               需要指定服务器的端口    处理ServerSocket抛出的异常public TcpEchoServer(int port) throws IOException {//                     指定服务器的端口serverSocket = new ServerSocket(port);}public void start() throws IOException {//服务器需要不停的执行while (true) {//开始监听指定端口,当有客户端连接后,返回一个保存对方信息的SocketSocket clientSocket = serverSocket.accept();ExecutorService service = Executors.newCachedThreadPool();//            //让一个线程去对应一个客户端
//            Thread thread = new Thread(() -> {
//                //处理逻辑
//                try {
//                    processConnection(clientSocket);
//                } catch (IOException e) {
//                    e.printStackTrace();
//                }
//            });
//            thread.start();service.submit(() -> {try {processConnection(clientSocket);} catch (IOException e) {e.printStackTrace();}});}}//针对一个连接,提供处理逻辑private void processConnection(Socket clientSocket) throws IOException {//打印客户端的信息                               返回IP地址                      返回端口号System.out.printf("[%s : %d]客户端上线\n",clientSocket.getInetAddress(), clientSocket.getPort());//获取到socket中持有的流对象try(InputStream inputStream = clientSocket.getInputStream();OutputStream outputStream = clientSocket.getOutputStream()) {//因为我们用字符串来做为数据传输,用Scanner就可以更方便的传输了Scanner scanner = new Scanner(inputStream);//包装输出流,主要是用println()会在数据之后加上\nPrintWriter printWriter = new PrintWriter(outputStream);while (true) {//1.获取请求if (!scanner.hasNext()) {//如果scanner无法读取出数据,说明客户端断开了连接,导致服务器这边读取到”末尾“break;}//2.处理请求//接收客户端的请求//如果遇到 空白字符 就会停止输入String request = scanner.next();//处理请求String response = process(request);//3.返回响应//此处可以按字节数组的形式,但是我们要输入的是字符串,这个就不太方便//outputStream.write(response.getBytes());//此方法在写入之后会自动加上\nprintWriter.println(response);//刷新缓冲区printWriter.flush();//4.打印日志System.out.printf("[%s : %d] 请求 = %s 响应 = %s \n",clientSocket.getInetAddress(),clientSocket.getPort(),request,response);}}catch (IOException e) {throw new RuntimeException();} finally {System.out.printf("[%s : %d]客户端下线\n",clientSocket.getInetAddress(), clientSocket.getPort());clientSocket.close();}}private String process(String request) {//由于我们是回显服务器这里直接返回就可以了return request;}public static void main(String[] args) throws IOException {TcpEchoServer server = new TcpEchoServer(8080);server.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 {private Socket socket = null;public TcpEchoClient(String serverIP, int serverPort) throws IOException {//这里只要建立实例,就是和服务端的accept()建立了连接//socket也就保存了服务器的IP和端口号等//需要传入服务器的 IP地址 和 端口号socket = new Socket(serverIP, serverPort);}public void start() {System.out.println("客户端启动!");try(OutputStream outputStream = socket.getOutputStream();InputStream inputStream = socket.getInputStream()) {//用来接收服务器的信息Scanner scanner = new Scanner(inputStream);//用于接收用户输入Scanner scannerIn = new Scanner(System.in);//用于输出数据给服务器PrintWriter printWriter = new PrintWriter(outputStream);while (true) {//1.从控制台获取数据System.out.print("->");String request = scannerIn.next();//2.将数据发送给服务器printWriter.println(request);//刷新缓冲区printWriter.flush();//3.接收服务器响应//判断服务端是否还有信息if (!scanner.hasNext()) {break;}//接收服务端信息String response = scanner.next();//4.打印相关结果System.out.println(response);}}catch (IOException e) {throw new RuntimeException();}}public static void main(String[] args) throws IOException {TcpEchoClient client = new TcpEchoClient("127.0.0.1",8080);client.start();}
}

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

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

相关文章

鸟笼效应——AI与思维模型【84】

一、定义 鸟笼效应思维模型指的是人们在偶然获得一件原本不需要的物品后,会为了这件物品的配套或使用需求,进而继续添加更多与之相关但自己原本可能并不需要的东西,仿佛被这个“鸟笼”牵着走,最终陷入一种惯性消费或行为模式的现象。简单来说,就是人们在心理上会有一种自…

加密解密记录

一、RSA 加密解密 密钥对生成 1.前端加密解密 (1).vue页面引入 npm install jsencrypt(2)工具 jsencrypt.js import JSEncrypt from jsencrypt/bin/jsencrypt.min// 密钥对生成 http://web.chacuo.net/netrsakeypairconst p…

浅析 MegEngine 对 DTR 的实现与改进

分享笔者在学习 MegEngine 对 DTR 的实现时的笔记。关于 DTR 可以参考:【翻译】DTR_ICLR 2021 文章目录 MegEngine 架构设计MegEngine 的动态图部分Imperative RuntimeImperative 与 MegDNN / MegBrain 的关系静态图运行时管家 —— MegBrain动态图接口 —— Impera…

micro-app前端微服务原理解析

一、核心设计思想 基于 WebComponents 的组件化渲染 micro-app 借鉴 WebComponents 的 CustomElement 和 ShadowDom 特性&#xff0c;将子应用封装为类似 WebComponent 的自定义标签&#xff08;如 <micro-app>&#xff09;。通过 ShadowDom 的天然隔离机制&#xff0c;实…

CMake中强制启用option定义变量的方法

在CMake中&#xff0c;若要在另一个CMake文件中强制启用由option()定义的变量&#xff0c;可使用set(... FORCE)覆盖缓存变量。具体步骤如下&#xff1a; 使用set命令强制覆盖缓存&#xff1a; 在需要强制启用选项的CMake文件中&#xff0c;使用set命令并指定CACHE和FORCE参数。…

C++漫溯键值的长河:map set

文章目录 1.关联式容器2.set2.1 find2.2 lower_bound、upper_bound 3.multiset3.1 count3.2 equal_range 4.map4.1 insert4.2 operate->4.3 operate[ ]4.4 map的应用实践&#xff1a;随机链表的复制 5.multimap希望读者们多多三连支持小编会继续更新你们的鼓励就是我前进的动…

汽车用品商城小程序源码介绍

基于ThinkPHPFastAdminUniApp开发的汽车用品商城小程序源码&#xff0c;从技术架构来看&#xff0c;ThinkPHP作为后端框架&#xff0c;提供了稳定且高效的开发基础&#xff0c;能够处理复杂的业务逻辑和数据交互。FastAdmin则进一步简化了后台管理系统的开发流程&#xff0c;提…

力扣hot100——114.二叉树展开为链表

基于 Morris 遍历思想 将左子树插到右子树的位置&#xff0c;将原来的右子树插到左子树的最右结点&#xff0c;遍历右结点重复以上步骤&#xff0c;直至右结点为空。 class Solution { public:void flatten(TreeNode* root) {if(rootnullptr) return;while(root){if(!root-&g…

JConsole监控centos服务器中的springboot的服务

场景 在centos服务器中,有一个aa.jar的springboot服务,我想用JConsole监控它的JVM情况,具体怎么实现。 配置 Spring Boot 应用以启用 JMX 在java应用启动项进行配置 java -Djava.rmi.server.hostname=服务器IP -Dcom.sun.management.jmxremote=true \ -Dcom.sun.managem…

39.RocketMQ高性能核心原理与源码架构剖析

1. 源码环境搭建 1.1 主要功能模块 ​ RocketMQ的官方Git仓库地址&#xff1a;GitHub - apache/rocketmq: Apache RocketMQ is a cloud native messaging and streaming platform, making it simple to build event-driven applications. ​ RocketMQ的官方网站上下载指定版…

施磊老师rpc(一)

文章目录 mprpc项目**项目概述**&#xff1a;深入学习到什么**前置学习建议**&#xff1a;核心内容其他技术与工具**项目特点与要求**&#xff1a;**环境准备**&#xff1a; 技术栈集群和分布式理论单机聊天服务器案例分析集群聊天服务器分析分布式系统介绍多个模块的局限引入分…

基于LangChain构建最小智能体(Agent)实现指南

摘要 本文完整解析基于LangChain的极简Agent实现方案&#xff0c;通过26行代码构建具备网络搜索能力的对话系统&#xff0c;涵盖Agent初始化、工具集成、流式回调等核心技术要点。适用于LLM应用开发者快速入门Agent开发。(参考项目代码&#xff1a;Minimal Agent) 系统架构设计…

AWTK:一键切换皮肤,打造个性化UI

想让你的应用在不同场景下都能完美呈现吗&#xff1f;皮肤切换功能必不可少&#xff01;本文将介绍AWTK&#xff0c;一款强大的GUI框架&#xff0c;它通过内置资源管理和优化缓存&#xff0c;轻松实现皮肤切换功能。 前言 当今的UI应用中&#xff0c;为了满足不同使用场景和…

【Vagrant+VirtualBox创建自动化虚拟环境】Ansible测试Playbook

文章目录 Vagrant安装vagrant安装 VirtualBox如何使用 Ansible安装AnsiblePlaybook测试创建hosts文件创建setup.yml文件 Vagrant Vagrant是一个基于Ruby的工具&#xff0c;用于创建和部署虚拟化开发环境。它使用Oracle的开源VirtualBox虚拟化系统&#xff0c;使用 Chef创建自动…

AI在医疗领域的10大应用:从疾病预测到手术机器人

AI在医疗领域的10大应用&#xff1a;从疾病预测到手术机器人 系统化学习人工智能网站&#xff08;收藏&#xff09;&#xff1a;https://www.captainbed.cn/flu 文章目录 AI在医疗领域的10大应用&#xff1a;从疾病预测到手术机器人摘要引言1. 医学影像诊断&#xff1a;从静态…

Win11 配置 Git 绑定 Github 账号的方法与问题汇总

目录 一、创建 Github 项目库&#xff08;远程仓库&#xff09;二、配置安装好的 Git1. 设置用户信息2. 查看已配置的信息3. 建立本地仓库4. Git 的常用命令1&#xff09;git checkout&#xff08;切换&#xff09;2&#xff09;git push&#xff08;上传&#xff09;3&#xf…

6.应用层

6. 应用层 1. 概述 应用层是计算机网络体系结构的最顶层&#xff0c;是设计和建立计算机网络的最终目的&#xff0c;也是计算机网络中发展最快的部分 早期基于文本的应用&#xff08;电子邮件、远程登录、文件传输、新闻组&#xff09;20世纪90年代将因特网带入千家万户的万维…

FPGA 100G UDP纯逻辑协议栈

随着器件等级的升高&#xff0c;高速serdes的线速率也随之提高&#xff0c;RFSOC 4x最大可支持100G&#xff0c;主流方案为RDMA方案&#xff0c;该方案相对比较复杂&#xff0c;除了需要负责逻辑端的开发&#xff0c;还需操作系统中开发RDMA的驱动&#xff0c;对于对丢包不那么…

CSS实现DIV水平与垂直居中方法总结

大家好&#xff0c;欢迎来到程序视点&#xff01;我是你们的老朋友.小二&#xff01; CSS实现DIV水平与垂直居中方法总结 一、水平居中方案 标准方法 .center-div {margin-left: auto;margin-right: auto; }关键点&#xff1a;必须声明DOCTYPE&#xff08;推荐XHTML 1.0 Tran…

Qt快速上手:QSettings高效配置读写实战指南

文章目录 前言一、QSettings初识&#xff1a;配置管理利器二、基础操作三板斧2.1 文件读写基础2.2 数据类型处理指南2.3 分组管理技巧 三、高级技巧&#xff1a;精准控制配置项3.1 监听配置变更3.2 批量操作配置项 四、避坑指南&#xff1a;那些你可能会遇到的问题4.1 键顺序重…