【网络编程】TCP

请添加图片描述

✨个人主页:bit me👇
✨当前专栏:Java EE初阶👇

目 录

  • 🔮一. TCP流套接字编程
  • 💿二. TCP中的长短连接
  • 📀三. 写一个 TCP 版本的 回显服务器-客户端

🔮一. TCP流套接字编程

  • ServerSocket API

ServerSocket 是创建TCP服务端Socket的API。

ServerSocket:是服务器端使用的 Socket

ServerSocket 构造方法:

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

ServerSocket 方法:

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

accept:没有参数,返回值是一个 Socket 对象,功能是等待有客户端和服务器建立上连接,accept 则会把这个连接获取到进程中,进一步的通过返回值的 Socket 对象来和客户端进行交互。

在此处可以举例为售楼,比如外场有男生拉客人,女生内场介绍售楼服务,其中 ServerSocket 就是外场连接,通过 accept 把连接交给了 Socket ,然后 Socket 对象和客户端进行沟通。

  • Socket API

Socket:服务器和客户端都会使用的 Socket 。

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

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

Socket 构造方法:

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

Socket 方法:

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

第二和第三个方法的传输数据,不是直接通过 Socket 对象,而是 Socket 内部包含了输入流对象(接收)输出流对象(发送)


💿二. TCP中的长短连接

TCP发送数据时,需要先建立连接,什么时候关闭连接就决定是短连接还是长连接:

  • 短连接:每次接收到数据并返回响应后,都关闭连接,即是短连接。也就是说,短连接只能一次收发数据。
  • 长连接:不关闭连接,一直保持连接状态,双方不停的收发数据,即是长连接。也就是说,长连接可以多次收发数据。

对比以上长短连接,两者区别如下:

  1. 建立连接、关闭连接的耗时:短连接每次请求、响应都需要建立连接,关闭连接;而长连接只需要第一次建立连接,之后的请求、响应都可以直接传输。相对来说建立连接,关闭连接也是要耗时的,长连接效率更高。
  2. 主动发送请求不同:短连接一般是客户端主动向服务端发送请求;而长连接可以是客户端主动发送请求,也可以是服务端主动发。
  3. 两者的使用场景有不同:短连接适用于客户端请求频率不高的场景,如浏览网页等。长连接适用于客户端与服务端通信频繁的场景,如聊天室,实时游戏等。

📀三. 写一个 TCP 版本的 回显服务器-客户端

服务器启动的时候,进行 accept ,accept 进行的工作是拉客,对于操作系统来说,建立 TCP 连接,是内核工作,accept 要干的就是等连接建立好了之后把这个连接拿到应用程序中,如果当前连接还没建立,accept 就会阻塞!accept 相当于,有人给你打电话了,你按下接听键!如果没人打电话,阻塞等待有人给你真的打电话了才行。

如果服务器返回多次响应,其实这多次响应,都是以字节为单位返回给客户端的,客户端操作系统内核就会收到这些字节,然后调用 read / scanner.next 这样的方法的时候,就是从内核中读取。

  • read :如果使用字节流的 read ,就是在读取固定长度的字节。即使返回 10 次,每次返回 10 个字节,这里 read (buf[100]),这种操作一次性就读出来了,也可以多次读取
  • scanner.next :隐含的东西,这里的 next 是读到空白符(空格,回车,换行,制表,翻页,垂直制表…)

测试网络程序的时候,务必要先启动服务器,再启动客户端。

客户端代码:

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() throws IOException {
        //new 这个对象,是要和服务器建立连接的
        //建立连接,是要知道服务器在哪里
        socket = new Socket("127.0.0.1",8000);
    }

    public void start() throws IOException {
        //由于我们建立的是长连接,一个连接会处理 N 个请求和响应
        Scanner scanner = new Scanner(System.in);
        try (InputStream inputStream = socket.getInputStream(); OutputStream outputStream = socket.getOutputStream()){
            Scanner scannerNet = new Scanner(inputStream);
            PrintWriter printWriter = new PrintWriter(outputStream);
            while (true){
                //从控制台读取用户输入
                System.out.println("> ");
                String request = scanner.next();
                //2. 把请求发送给服务器
                printWriter.write(request);
                printWriter.flush();
                //3. 从服务器读取响应
                String response = scannerNet.next();
                //4. 把结果显示到界面上
                System.out.printf("req : %s; resp : %s\n",request,response);
            }
        }
    }

    public static void main(String[] args) throws IOException {
        TcpEchoClient client = new TcpEchoClient();
        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;

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){
            //如果当前没有客户端来进行连接,就会阻塞等待
            Socket clientSocket = serverSocket.accept();
            processConnect(clientSocket);
        }
    }

    //通过这个方法,给当前连上的这个客户端,提供服务
    //一个连接过来了,服务方式可能有两种
    //1. 一个连接只进行一次数据交互(一个请求 + 一个响应)  短连接
    //2. 一个连接进行多次数据交互(N 个请求 + N 个响应)   长连接
    //此处来写长连接版本
    public void processConnect(Socket clientSocket) throws IOException {
        System.out.printf("[%s:%d] 建立连接!\n",clientSocket.getInetAddress().toString(),clientSocket.getPort());
        try(InputStream inputStream = clientSocket.getInputStream();
            OutputStream outputStream = clientSocket.getOutputStream()){
            Scanner scanner = new Scanner(inputStream);
            PrintWriter printWriter = new PrintWriter(outputStream);

            //这里是长连接的写法,需要用循环来获取到多次交互的情况
            while (true){
                if(!scanner.hasNext()){
                    //断开连接,当客户端断开连接的时候,此时 hasNext 就会返回 false
                    System.out.printf("[%s:%d] 断开连接!\n",clientSocket.getInetAddress().toString(),clientSocket.getPort());
                    break;
                }
                //1. 读取请求并解析
                String request = scanner.next();
                //2. 根据请求计算响应
                String response = process(request);
                //3. 把响应返回给客户端
                printWriter.write(response);
                // 刷新一下缓冲区,避免数据没有发出去
                printWriter.flush();
                System.out.printf("[%s:%d] req: %s; resp: %s\n",clientSocket.getInetAddress().toString(),
                        clientSocket.getPort(),
                        request,response);
            }
        }
    }
    public String process(String req){
        return req;
    }

    public static void main(String[] args) throws IOException {
        TcpEchoServer server = new TcpEchoServer(8000);
        server.start();
    }
}

运行效果:

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

但是在此处我们发现客户端和服务器并没有想我们之前那样连接起来,输入内容之后,没发生反应,有两种可能性:

  1. 客户端没发出去(很可能没加 flush)
  2. 服务器收到了没有处理(很可能服务器还在阻塞的状态)

经过排查,猜测上面代码问题应该出现在服务器中,但是我们如何确认阻塞在哪里了呢?

我之前写过的,线程出现问题,可以查看 jconsole 查看当前线程状况,看到每个线程的调用栈

客户端中:

在这里插入图片描述

服务器中:

在这里插入图片描述

发现都是在 next 中阻塞了,下面两行就是代码阻塞的位置。

但是客户端阻塞在这里,是很合理的!等待服务器返回响应,服务器当前确实没有返回响应(如果服务器返回响应了,服务器会打印的,实际上服务器没打印这个日志)

next 啥时候会执行结束?就是当读到空白符空格,回车,换行,制表,翻页,垂直制表…)的时候才结束。

scanner.next 的行为是:尝试往后读,读到空白符就结束,会返回一个字符串,返回值里是不包含刚才最后的空白符的。由此可见,我们在客户端发送的信息中再加个空白符就行了。

此处会有人说可以用 nextline,nextline 是判定 /n,next 是判断空白符(包含 /n),都差不多。

所以此处最好的修改就是把 next 后面含有 write 的写法改成 println

客户端中第二步:把请求发送给服务器改为:printWriter.println(request);
服务器中第三步:把响应返回给客户端改为:printWriter.println(response);

改变后的代码执行就正确了

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

但是但是但是!!!bug 并没有全部解决,还有两个 bug。

  1. 上述代码中,使用 clientSocket !用完之后,是否要关闭呢?当然要关闭!和上面的 ServerSocket 不同!

生命周期不同,ServerSocket 的生命周期,是跟随整个程序的,clientSocket 的生命周期,只是当前连接!就应该在连接之后,把这里的 socket 进行关闭。如果不关闭,妥妥的资源泄露,ServerSocket 只有一个,clientSocket 会有无数个!每个客户端的连接,都是一个!!!

因此解决方案就是在 try 后面加个 finally ,finally 中写 clientSocket.close(); 即可

  1. 上述代码无法处理多个客户端

当第一个客户端连上服务器之后,服务器提示 “建立连接”
当第二个客户端也连接上的时候,服务器没提示
 
第一个客户端发的请求,可以正常处理
第二个客户端发的请求,不能处理
 
当把第一个客户端关闭之后,第二个客户端才能真正连接成功,之前发的请求,才有回应。

在服务器代码中:

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

当第一个客户端连接上之后,服务器就进入了 processConnect 代码,就在 while 循环 next 这里阻塞了,processConnect 方法就执行不完了,同时也就无法第二次执行到 accept ,也就无法处理第二个客户端。

next 和 循环 影响了服务器第二次调用 accept

因此解决方案就是既能够执行到 processConnect 里面的循环,又能够执行到 accept 即可,所以在这里,我们可以使用多线程。在主线程中执行 accept ,创建一个新的线程来调用 processConnect ,这样原来主线程就不会受影响。

在这里插入图片描述

执行效果:

  • 客户端1
    在这里插入图片描述

  • 客户端2
    在这里插入图片描述

  • 客户端3
    在这里插入图片描述

  • 服务器
    在这里插入图片描述

虽然比最开始单线程有提升,但是涉及到频繁创建销毁线程,在高并发的状态下,负担比较重,因此这里可以再优化使用线程池。

使用线程池,来解决频繁创建销毁线程的问题

//注意线程池在循环外面创建
ExecutorService service = Executors.newCachedThreadPool();
//循环内部
service.submit(new Runnable() {
    @Override
    public void run() {
        try {
            processConnect(clientSocket);
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
});

当前是使用了线程池解决了问题,但是如果并发量太高了!就会导致池子里的线程特别特别多!就说明内存等资源调度开销特别大,,于是乎有了进一步的改进,是否能减少线程的数目?当前是一个线程对应一个客户端,能不能让一个线程对应多个客户端?当然可以,但是此处就不再拓展了,想知道的老铁可以去搜搜 “IO 多路复用”,本质上就是一个线程处理多个 socket ,操作系统提供的机制。

最后附上改进后的总代码:

服务器:

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("服务器启动!");
        ExecutorService service = Executors.newCachedThreadPool();
        while (true){
            //如果当前没有客户端来进行连接,就会阻塞等待
            Socket clientSocket = serverSocket.accept();
            //不再直接调用了,创建一个新的线程,让线程来调用他
            //版本1: 使用单线程,存在 bug ,无法处理多线程情况
            //processConnect(clientSocket);

			//版本2: 使用多线程,主线程负责拉客,新线程负责通信
			//虽然比版本 1 有提升,但是涉及到频繁创建销毁线程,在高并发的状态下,负担比较重
            //Thread t = new Thread(()->{
            //    try {
            //        processConnect(clientSocket);
            //    } catch (IOException e) {
            //        e.printStackTrace();
            //   }
            //});
            //t.start();

			//版本3: 使用线程池,来解决频繁创建销毁线程的问题
			//此处不太适合使用 "固定个数的"
			//在外面创建线程池
            service.submit(new Runnable() {
                @Override
                public void run() {
                    try {
                        processConnect(clientSocket);
                    } catch (IOException e) {
                        e.printStackTrace();
                    }
                }
            });
        }
    }

    //通过这个方法,给当前连上的这个客户端,提供服务
    //一个连接过来了,服务方式可能有两种
    //1. 一个连接只进行一次数据交互(一个请求 + 一个响应)  短连接
    //2. 一个连接进行多次数据交互(N 个请求 + N 个响应)   长连接
    //此处来写长连接版本
    public void processConnect(Socket clientSocket) throws IOException {
        System.out.printf("[%s:%d] 建立连接!\n",clientSocket.getInetAddress().toString(),clientSocket.getPort());
        try(InputStream inputStream = clientSocket.getInputStream();
            OutputStream outputStream = clientSocket.getOutputStream()){
            Scanner scanner = new Scanner(inputStream);
            PrintWriter printWriter = new PrintWriter(outputStream);

            //这里是长连接的写法,需要用循环来获取到多次交互的情况
            while (true){
                if(!scanner.hasNext()){
                    //断开连接,当客户端断开连接的时候,此时 hasNext 就会返回 false
                    System.out.printf("[%s:%d] 断开连接!\n",clientSocket.getInetAddress().toString(),clientSocket.getPort());
                    break;
                }
                //1. 读取请求并解析
                String request = scanner.next();
                //2. 根据请求计算响应
                String response = process(request);
                //3. 把响应返回给客户端
                printWriter.println(response);
                // 刷新一下缓冲区,避免数据没有发出去
                printWriter.flush();
                System.out.printf("[%s:%d] req: %s; resp: %s\n",clientSocket.getInetAddress().toString(),
                        clientSocket.getPort(),
                        request,response);
            }
        }finally {
            //加在这里是更稳妥的做法
            clientSocket.close();
        }
    }
    public String process(String req){
        return req;
    }

    public static void main(String[] args) throws IOException {
        TcpEchoServer server = new TcpEchoServer(8000);
        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() throws IOException {
        //new 这个对象,是要和服务器建立连接的
        //建立连接,是要知道服务器在哪里
        socket = new Socket("127.0.0.1",8000);
    }

    public void start() throws IOException {
        //由于我们建立的是长连接,一个连接会处理 N 个请求和响应
        Scanner scanner = new Scanner(System.in);
        try (InputStream inputStream = socket.getInputStream(); OutputStream outputStream = socket.getOutputStream()){
            Scanner scannerNet = new Scanner(inputStream);
            PrintWriter printWriter = new PrintWriter(outputStream);
            while (true){
                //从控制台读取用户输入
                System.out.println("> ");
                String request = scanner.next();
                //2. 把请求发送给服务器
                printWriter.println(request);
                printWriter.flush();
                //3. 从服务器读取响应
                String response = scannerNet.next();
                //4. 把结果显示到界面上
                System.out.printf("req : %s; resp : %s\n",request,response);
            }
        }
    }

    public static void main(String[] args) throws IOException {
        TcpEchoClient client = new TcpEchoClient();
        client.start();
    }
}

TCP 版本的字典查找

import java.io.IOException;
import java.util.HashMap;
import java.util.Map;

public class TcpDictServer extends TcpEchoServer{
    private Map<String,String> dict = new HashMap<>();

    public TcpDictServer(int port) throws IOException {
        super(port);

        dict.put("cat","小猫");
        dict.put("dog","小狗");
        dict.put("fuck","卧槽");
    }

    public String process(String req){
        return dict.getOrDefault(req,"俺也不知道是啥");
    }

    public static void main(String[] args) throws IOException {
        TcpDictServer server = new TcpDictServer(8000);
        server.start();
    }
}

运行结果展示:

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

在这里插入图片描述

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

网络视频监控如何入门?如何安装和配置、设备选择和实时监控?

网络视频监控是一种先进的安全技术&#xff0c;它可以通过互联网连接到远程视频服务器&#xff0c;使用户可以随时随地监控所关注的地点。本文将介绍网络视频监控的基础入门知识&#xff0c;包括安装和配置、设备选择和实时监控等方面。 一、安装和配置 在进行网络视频监控前&…

Opencv+Python笔记(四)图像的形态学处理

1.腐蚀与膨胀 膨胀用来处理缺陷问题&#xff0c;把缺陷填补掉&#xff0c;提高亮区面积&#xff1b; 腐蚀用来处理毛刺问题&#xff0c;把毛刺腐蚀掉&#xff0c;降低亮区面积。 腐蚀操作可以消除噪点&#xff0c;同时消除部分边界值&#xff0c;导致目标图像整体缩小。 膨胀…

C#+asp.net基于web的大学社团管理信息系统

本系统的模块是分为用户模块和管理员模块&#xff0c;管理员负责学生管理模块、社团管理模块、公告管理模块、留言管理模块、加入社团管理模块、活动管理模块、管理员管理模块。社团管理员则负责预约管理模块、活动报名的审核等。。 系统具有三类系统用户分别是&#xff1a;系统…

用于测试FDIA在现实约束下可行性的FDIA建模框架(Matlab代码实现)

目录 &#x1f4a5;1 概述 &#x1f4da;2 运行结果 &#x1f389;3 参考文献 &#x1f468;‍&#x1f4bb;4 Matlab代码 &#x1f4a5;1 概述 信息通信技术的发展和智能设备的引入使电力系统逐渐演变为电力信息物理系统&#xff0c;而信息层与物理层之间的深度耦合也加剧…

【测试面试】吐血整理,大厂测试开发岗面试题(1~4面),拿下年40w...

目录&#xff1a;导读 前言一、Python编程入门到精通二、接口自动化项目实战三、Web自动化项目实战四、App自动化项目实战五、一线大厂简历六、测试开发DevOps体系七、常用自动化测试工具八、JMeter性能测试九、总结&#xff08;尾部小惊喜&#xff09; 前言 自动化测试面试题&…

【文件系统和系统日志分析】

目录 一、inode和block概述block&#xff08;块&#xff09;inode&#xff08;索引节点&#xff09; 二、inode内容三、inode的号码3.1、查看inode号码的方法 四、inode的大小磁盘分区后的结构访问文件的简单流程 五、删除乱码文件六、inode节点耗尽故障处理6.1、模拟inode节点…

SSM整合的基本思路梳理

SSM整合的简单思路流程 基本思路 我在整合的时候一般习惯从MyBatis开始向上构建&#xff0c;也就是在开始一个项目的时候先将DAO层搭建起来&#xff0c;再向上整合Spring以及SpringMVC。按照这个流程&#xff0c;可以做出一个比较简单的大致流程作为参考&#xff0c;帮助我们…

[MySQL]基本数据类型及表的基本操作

一、常用的数据类型 1.1 数据库表的列类型 数值 1 2 3.14 tinyint 十分小的数据 1个字节smallint 较小的数据 2个字节mediumint 中等大小的数据 3个字节int 标准的整数 4个字节big 较大的数据 8个字节float 浮点数 4个字节double 浮点数 小数 8个字节&#xff08;精度问题&am…

JSON的用法和说明

JSON&#xff08;JavaScript Object Notation&#xff09;是一种轻量级的数据交换格式。 JSON建构于两种结构&#xff1a; "名称/值"对的集合。理解为对象 值的有序列表。理解为数组 JSON具有以下这些形式&#xff1a; 对象是一个无序的“ ’名称/值‘ 对”集合。一个…

【排序】快速排序(递归和非递归)

快速排序 前言图解大致思路对于hoare版本对于挖坑法对于前后指针法 实现方法递归非递归 快排的优化&#xff08;基于递归的优化&#xff09;三数取中法小区间优化 时间复杂度和空间复杂度 前言 快速排序&#xff0c;听名字就比较霸道&#xff0c;效率根名字一样&#xff0c;非…

永久免费内网穿透不限制速度

市面上的免费内网穿透大都有格式各样的限制&#xff0c;什么限制流量啊&#xff0c;每个月要签到打卡啊&#xff0c;还有更改域名地址等&#xff0c;只有神卓互联内网穿透是永久免费没有限制的&#xff0c;白嫖也可以。 这篇文章分享了3个方案&#xff0c;按照性能和综合指标排…

项目驱动的编写

驱动代码直接使用nfs传输,设备树直接在开发板中修改设备树文件 1、修改好设备树&#xff0c;在内核顶层make dtbs &#xff0c;然后替代tftp目录中的设备树文件 2、使用内核源码编译生成驱动程序&#xff0c;然后传送到开发板中&#xff0c;使用insmod动态加载 LCD驱动 1、初始…

从零学习SDK(7)如何打包SDK

打包SDK的目的是为了方便将SDK提供给其他开发者或用户使用&#xff0c;以及保证SDK的兼容性和安全性。打包SDK可以有以下几个好处&#xff1a; 减少依赖&#xff1a;打包SDK可以将SDK所需的库、资源、文档等打包成一个文件或者一个目录&#xff0c;这样就不需要用户再去安装或…

ArduPilot开源飞控系统之简单介绍

ArduPilot开源飞控系统之简单介绍 1. 源由2. 了解&阅读2.1 ArduPilot历史2.2 关于GPLv32.3 ArduPilot系统组成2.4 ArduPilot代码结构 3. 后续4. 参考资料 ArduPilot是一个可信赖的自动驾驶系统&#xff0c;为人们带来便利。为此&#xff0c;提供了一套全面的工具&#xff0…

读SQL进阶教程笔记12_地址与三值逻辑

1. SQL和数据库都在极力提升数据在表现层的抽象度&#xff0c;以及对用户隐藏物理层的概念 2. 关系模型是为摆脱地址而生的 2.1. “地址”不仅包括指针操作的地址&#xff0c;还包括数组下标等 3. 一个优雅的数据结构胜过一百行杂耍般的代码 3.1. 精巧的数据结构搭配笨拙的…

Spring MVC 的调用(12)

目录 SpringMVC流程 源码分析 第一步:用户发起请求到前端控制器&#xff08;DispatcherServlet&#xff09; 第二步&#xff1a;前端控制器请求处理器映射器&#xff08;HandlerMappering&#xff09;去查找处理器&#xff08;Handle&#xff09;&#xff1a;通过xml配置或者…

高效部署Redis Sentinel模式(哨兵模式),手把手教学

Redis Sentinel模式部署 前言一、服务器部署同版本的redis1、换软件源在yum拉取包的时候启用remi源 二、修改配置文件1.修改/etc/redis.conf2.配置/etc/redis/sentinel.conf 三、启动redis服务1、启动服务2、连接redis3、检查redis 前言 这里就不过多的解释高可用的好处了&…

CRM系统是什么?它有什么作用?

CRM系统是什么&#xff1f; CRM是Customer Relationship Management&#xff08;客户关系管理&#xff09;的缩写&#xff0c;是一种通过对客户进行跟踪、分析和管理的方法&#xff0c;以增加企业与客户之间的互动和联系&#xff0c;提高企业与客户之间的互信&#xff0c;从而…

基于 VITA57.4 标准的 8 路 500MSPS/1GSPS/1.25GSPS 采样率 14 位 AD 采集 FMC 子卡模块

板卡概述 FMC148 是一款基于 VITA57.4 标准的 JESD204B 接口 FMC 子卡模块&#xff0c;该模块可以实现 8 路 14-bit、500MSPS/1GSPS/1.25GSPS ADC 采集功能。该板卡 ADC 器件采用 ADI 公司的 AD9680 芯片,全 功率-3dB 模拟输入带宽可达 2GHz。该 ADC 与 FPGA 的主机接口通 …

Revit相关问题:符号线,转转问题,生成三维视图

一、Revit符号线如何画粗一些?如何自定义符号线子类别? 1、Revit在族里面符号线的粗细、显示颜色、显示线型为符号线的子类别控制! 你可以通过&#xff0c;管理选项卡新建子类别&#xff0c;然后在画符号线的时候应用该子类别! 新建符号线对象样式 应用子类别 二、Revit三维模…