Linux网络——TCP的运用

news2025/4/6 15:03:29

系列文章目录


文章目录

  • 系列文章目录
    • 一、服务端实现
      • 1.1 创建套接字socket
      • 1.2 指定网络接口并bind
      • 2.3 设置监听状态listen
      • 2.4 获取新链接accept
      • 2.5 接收数据并处理(服务)
      • 2.6 整体代码
    • 二、客户端实现
      • 2.1 创建套接字socket
      • 2.2 指定网络接口
      • 2.3 发起链接connect
      • 2.4 发送数据并接收
      • 2.5 整体代码
      • 2.6 绑定问题
    • 三、问题与改进
      • 3.1 问题描述
      • 3.2 解决方法
        • 3.2.1 问题版本
        • 3.2.2 多进程版
        • 3.4.3 进程池(暂未实现)
        • 3.4.4 多线程版
        • 3.4.5 线程池版
    • 四、TCP与UDP的对比


一、服务端实现

1.1 创建套接字socket

和上篇文章UDP的使用一致,创建套接字

调用系统接口socket函数,帮助我们创建套接字,本质是把文件和网卡关联起来
在这里插入图片描述

参数介绍:

domain:一个域,标识了这个套接字的通信类型(网络或者本地)
在这里插入图片描述

只用关注上面三个类,第一个与第二个AF_UNIX/AF_LOCAL表示本地通信,而AF_INET表示网络通信
type:套接字提供服务的类型
在这里插入图片描述
我们用UDP实现,所以使用SOCK_DGRAM
protocol:想使用的协议,默认为0即可,因为前面的两个参数决定了,就已经决定了是TCP还是UDP协议了

返回值:

成功则返回打开的文件描述符(指向网卡文件,其实就是文件描述符),失败返回-1

创建套接字的本质其实就是创建了一个文件描述符,并返回该文件描述符的值
只是该文件描述符是用于对应服务的网路数据传输

        // 1. 创建套接字
        _listensock = socket(AF_INET, SOCK_STREAM, 0);

        if (_listensock < 0)
        {
            lg.LogMessage(Fatal, "socket error, sockfd : %d\n", _listensock);
            exit(1);
        }
        lg.LogMessage(Info, "socket success, sockfd : %d\n", _listensock);

1.2 指定网络接口并bind

和上篇文章UDP的使用一致,服务端需要手动bind

在这里插入图片描述

参数介绍:

socket:创建套接字的返回值
address:通用结构体(上一章Linux网络——网络套接字有详细介绍)
address_len:传入结构体的长度

我们要先定义一个sockaddr_in结构体,将结构体内对应的字段填充好,再将结构体作为参数传递
在这里插入图片描述

struct sockaddr_in {
    short int sin_family;           // 地址族,一般为AF_INET或PF_INET
    unsigned short int sin_port;    // 端口号,网络字节序
    struct in_addr sin_addr;        // IP地址
    unsigned char sin_zero[8];      // 用于填充,使sizeof(sockaddr_in)等于16
};

创建结构体后要先清空数据(初始化),我们可以用memset,也可以用系统接口:

#include <strings.h>

void bzero(void *s, size_t n);

填充端口号的时候要注意端口号是两个字节的数据,涉及到大小端问题
在计算机中的普遍规定:在网络中传输的数据都是大端的
所以为了统一,无论我们机器是大端还是小端,在调用接口的时候,都将IP与端口号从主机序列转化为网路序列

端口号的接口

#include <arpa/inet.h>
// 主机序列转网络序列
uint32_t htonl(uint32_t hostlong);
uint16_t htons(uint16_t hostshort);
// 网络序列转主机序列
uint32_t ntohl(uint32_t netlong);
uint16_t ntohs(uint16_t netshort);

IP的接口
对于IP,其实有两步:首先将字符串转换为整型,再解决大小端问题
系统给了直接能解决这两个问题的接口

#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>

int inet_aton(const char *cp, struct in_addr *inp);

in_addr_t inet_addr(const char *cp);// 点分十进制字符串

in_addr_t inet_network(const char *cp);

char *inet_ntoa(struct in_addr in);

struct in_addr inet_makeaddr(int net, int host);

in_addr_t inet_lnaof(struct in_addr in);

in_addr_t inet_netof(struct in_addr in);

这里的inet_addr就是把一个点分十进制的字符串转化成整数再进行大小端处理

代码:

		// 2. 指定网络接口并bind
        struct sockaddr_in local;
        memset(&local, 0, sizeof(local));

        local.sin_family = AF_INET;
        local.sin_port = htons(_port);
        local.sin_addr.s_addr = INADDR_ANY;

        int n = bind(_listensock, (struct sockaddr *)&local, sizeof(local));
        if (n != 0)
        {
            lg.LogMessage(Fatal, "bind error\n");
            exit(2);
        }
        lg.LogMessage(Debug, "bind socket success, sockfd: %d\n", _listensock);

2.3 设置监听状态listen

这里TCP跟UDP有所不同

要把socket套接字的状态设置为listen状态,只有这样才能一直获取新链接,接收新的链接请求

举个例子:
我们买东西如果出现了问题会去找客服,如果客服不在那么就回复不了,所以规定了客服在工作的时候必须要时刻接收回复消息,这个客服所处的状态就叫做监听状态

在这里插入图片描述

关于第二个参数backlog后边讲TCP协议的时候介绍,目前先直接用

		const static int default_backlog = 1;
		
        // 3. 设置socket为监听状态
        int m = listen(_listensock, default_backlog);
        if (m != 0)
        {
            lg.LogMessage(Fatal, "listen error\n");
            exit(3);
        }
        lg.LogMessage(Debug, "listen socket success, sockfd: %d\n", _listensock);

做完这些,初始化工作就完成了,总代码

  void Init()
    {
        // 1. 创建套接字
        _listensock = socket(AF_INET, SOCK_STREAM, 0);

        if (_listensock < 0)
        {
            lg.LogMessage(Fatal, "socket error, sockfd : %d\n", _listensock);
            exit(1);
        }
        lg.LogMessage(Info, "socket success, sockfd : %d\n", _listensock);

        // 2. 指定网络接口并bind
        struct sockaddr_in local;
        memset(&local, 0, sizeof(local));

        local.sin_family = AF_INET;
        local.sin_port = htons(_port);
        local.sin_addr.s_addr = INADDR_ANY;

        int n = bind(_listensock, (struct sockaddr *)&local, sizeof(local));
        if (n != 0)
        {
            lg.LogMessage(Fatal, "bind error\n");
            exit(2);
        }
        lg.LogMessage(Debug, "bind socket success, sockfd: %d\n", _listensock);

        // 3. 设置socket为监听状态
        int m = listen(_listensock, default_backlog);
        if (m != 0)
        {
            lg.LogMessage(Fatal, "listen error\n");
            exit(3);
        }
        lg.LogMessage(Debug, "listen socket success, sockfd: %d\n", _listensock);
    }

2.4 获取新链接accept

上面初始化完毕,现在开始就是要运行服务端,而TCP不能直接发数据,因为它是面向链接的,必须要先建立链接。
在这里插入图片描述

参数介绍

sockfd文件描述符,找到套接字
addr输入输出型参数,是一个结构体,用来获取客户端的信息
addrlen输入输出型参数,客户端传过来的结构体大小

返回值

成功返回一个文件描述符
失败返回-1

而我们知道sockfd本来就是一个文件描述符,那么这个返回的文件描述符是什么呢?
举个例子:

我们去吃饭的时候会发现每个店铺门口都会有人来招揽顾客,这个人把我们领进去门店后,然后他就会继续站在门口继续招揽顾客,而我们会有里面的服务员来招待我们,给我们提供服务

这里的揽客的人就是_listensock,而服务员就是返回值的文件描述符
意思就是_listensock的作用就是把链接从底层获取上来,返回值的作用就是跟客户端通信
从这里就知道了成员变量中的_listensock`并不是通信用的套接字,而是专门用来获取链接的套接字

    void Start()
    {
        _is_running = true;
        
        while (_is_running)
        {
            // 4. 获取连接
            struct sockaddr_in peer;
            socklen_t len = sizeof(peer);
            int sockfd = accept(_listensock, (struct sockaddr *)&peer, &len);
            if (sockfd < 0)
            {
                lg.LogMessage(Fatal, "socket accept error");
                continue;
            }
            lg.LogMessage(Debug, "accept socket success, get a new sockfd: %d\n", sockfd);

            Service(sockfd);
            close(sockfd);
        }
    }

2.5 接收数据并处理(服务)

当客户访问服务器的时候,必定是想要完成某件事,并且得到某件事完成的结果,我们称这个过程为服务
服务端收到客户端发来的信息或请求后,进行分析判断,完成客户端想要完成的任务,并返回给客户端

我们这里写一个简单的运用,此处的服务就是读取发来的信息并发回给客户端


    void Service(int sockfd)
    {
        char buffer[1024];

        while (true)
        {
            int n = read(sockfd, buffer, sizeof(buffer) - 1);

            if (n > 0)
            {
                buffer[n] = 0;
                std::cout << "client say# " << buffer << std::endl;
                std::string echo_string = "server echo# ";
                echo_string += buffer;
                write(sockfd, echo_string.c_str(), echo_string.size());
            }
            else if (n == 0) // read如果返回值是0,表示读到了文件结尾(对端关闭了连接!)
            {
                lg.LogMessage(Info, "client quit...\n");
                break;
            }
            else
            {
                lg.LogMessage(Error, "read socket error");
                break;
            }
        }
    }

2.6 整体代码

#pragma once
#include <iostream>
#include <string>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <string.h>
#include "Log.hpp"
#include "nocopy.hpp"

static const int default_fd = -1;
const static int default_backlog = 1;

class TcpServer : public nocopy
{
public:
    TcpServer(const uint16_t port)
        : _port(port), _listensock(default_fd), _is_running(false)
    {
    }

    void Init()
    {
        // 1. 创建套接字
        _listensock = socket(AF_INET, SOCK_STREAM, 0);

        if (_listensock < 0)
        {
            lg.LogMessage(Fatal, "socket error, sockfd : %d\n", _listensock);
            exit(1);
        }
        lg.LogMessage(Info, "socket success, sockfd : %d\n", _listensock);

        // 2. 指定网络接口并bind
        struct sockaddr_in local;
        memset(&local, 0, sizeof(local));

        local.sin_family = AF_INET;
        local.sin_port = htons(_port);
        local.sin_addr.s_addr = INADDR_ANY;

        int n = bind(_listensock, (struct sockaddr *)&local, sizeof(local));
        if (n != 0)
        {
            lg.LogMessage(Fatal, "bind error\n");
            exit(2);
        }
        lg.LogMessage(Debug, "bind socket success, sockfd: %d\n", _listensock);

        // 3. 设置socket为监听状态
        int m = listen(_listensock, default_backlog);
        if (m != 0)
        {
            lg.LogMessage(Fatal, "listen error\n");
            exit(3);
        }
        lg.LogMessage(Debug, "listen socket success, sockfd: %d\n", _listensock);
    }

    void Service(int sockfd)
    {
        char buffer[1024];

        while (true)
        {
            int n = read(sockfd, buffer, sizeof(buffer) - 1);

            if (n > 0)
            {
                buffer[n] = 0;
                std::cout << "client say# " << buffer << std::endl;
                std::string echo_string = "server echo# ";
                echo_string += buffer;
                write(sockfd, echo_string.c_str(), echo_string.size());
            }
            else if (n == 0) // read如果返回值是0,表示读到了文件结尾(对端关闭了连接!)
            {
                lg.LogMessage(Info, "client quit...\n");
                break;
            }
            else
            {
                lg.LogMessage(Error, "read socket error");
                break;
            }
        }
    }

    void Start()
    {
        _is_running = true;
        
        while (_is_running)
        {
            // 4. 获取连接
            struct sockaddr_in peer;
            socklen_t len = sizeof(peer);
            int sockfd = accept(_listensock, (struct sockaddr *)&peer, &len);
            if (sockfd < 0)
            {
                lg.LogMessage(Fatal, "socket accept error");
                continue;
            }
            lg.LogMessage(Debug, "accept socket success, get a new sockfd: %d\n", sockfd);

            Service(sockfd);
            close(sockfd);
        }
    }

    ~TcpServer()
    {
    }

private:
    uint16_t _port;
    int _listensock;
    bool _is_running;
};




#include "TcpServer.hpp"
#include <memory>

void Usage(const std::string s)
{
    std::cout << "Usagr: " << s << " local_port" << std::endl;
}

int main(int argc,char *argv[])
{

    if (argc != 2)
    {
        Usage(argv[0]);
        return 1;
    }

    std::unique_ptr<TcpServer> tcpser = std::make_unique<TcpServer>(std::stoi(argv[1]));

    tcpser->Init();
    tcpser->Start();

    return 0;
}

二、客户端实现

2.1 创建套接字socket

    // 1. 创建socket
    int _sockfd = socket(AF_INET, SOCK_STREAM, 0);
    if (_sockfd < 0)
    {
        lg.LogMessage(Fatal, "socket error, sockfd : %d\n", _sockfd);
        exit(1);
    }
    lg.LogMessage(Info, "socket success, sockfd : %d\n", _sockfd);

2.2 指定网络接口

    // 2. 指定网络接口
    struct sockaddr_in send;
    memset(&send, 0, sizeof(send));
    send.sin_family = AF_INET;
    send.sin_port = htons(stoi(argv[2]));
    send.sin_addr.s_addr = inet_addr(argv[1]);

2.3 发起链接connect

在这里插入图片描述

参数说明:

这里的addraddrlen填入的是服务端信息

在UDP通信中,客户端在sendto的时候会自动绑定IP和port,TCP这里就是在connect的时候绑定


    // 3. 进行连接
    int n = connect(_sockfd, (struct sockaddr *)&send, sizeof(send));

2.4 发送数据并接收

  while (true)
    {
        std::string inbuffer;
        std::cout << "Please Enter# ";
        getline(cin, inbuffer);

        int n = write(_sockfd, inbuffer.c_str(), inbuffer.size());

        if (n > 0)
        {
            char buffer[1024];
            int m = read(_sockfd, buffer, sizeof(buffer) - 1);
            if (m > 0)
            {
                buffer[m] = 0;
                cout << "get a echo messsge -> " << buffer << endl;
            }
            else if (m == 0 || m < 0)
            {
                break;
            }
        }
        else
        {
            break;
        }
    }

2.5 整体代码

#include <iostream>
#include <string>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <string.h>
#include "Log.hpp"

void usage(std::string s)
{
    std::cout << "Usagr: " << s << " server_ip server_port" << std::endl;
}

int main(int argc, char *argv[])
{
    if (argc != 3)
    {
        usage(argv[0]);
        return 0;
    }

    // 1. 创建socket
    int _sockfd = socket(AF_INET, SOCK_STREAM, 0);
    if (_sockfd < 0)
    {
        lg.LogMessage(Fatal, "socket error, sockfd : %d\n", _sockfd);
        exit(1);
    }
    lg.LogMessage(Info, "socket success, sockfd : %d\n", _sockfd);

    // 2. 指定网络接口
    struct sockaddr_in send;
    memset(&send, 0, sizeof(send));
    send.sin_family = AF_INET;
    send.sin_port = htons(stoi(argv[2]));
    send.sin_addr.s_addr = inet_addr(argv[1]);

    // 3. 进行连接
    int n = connect(_sockfd, (struct sockaddr *)&send, sizeof(send));

    while (true)
    {
        std::string inbuffer;
        std::cout << "Please Enter# ";
        getline(cin, inbuffer);

        int n = write(_sockfd, inbuffer.c_str(), inbuffer.size());

        if (n > 0)
        {
            char buffer[1024];
            int m = read(_sockfd, buffer, sizeof(buffer) - 1);
            if (m > 0)
            {
                buffer[m] = 0;
                cout << "get a echo messsge -> " << buffer << endl;
            }
            else if (m == 0 || m < 0)
            {
                break;
            }
        }
        else
        {
            break;
        }
    }

    return 0;
}

2.6 绑定问题

首先bind的作用是允许应用程序指定一个端口号用于监听传入的数据报或数据流

对于服务端:
需要绑定一个公开的端口号,允许大家访问,如果是随机的,其他人不知道,也就访问不了,所以服务端需要绑定

对于客户端:
客户端在给服务器发信息的同时,服务器也可能给客户端发信息,所以客户端为了监听服务器有没有给自己回信息也需要bind一个端口号用于监听,但客户端不需要显式的bind,因为客户端的端口号不会被所有人访问,别人不需要知道,所以OS自动帮我们bind一个随机的端口号,另外也是为了防止端口号重复,避免破坏唯一性

那么为什么前面服务端必须显示的绑定port呢?

因为服务器的端口号是众所周知的,不能改变,如果变了就找不到服务器了

而客户端只需要有就可以,只用标识唯一性即可
举个例子:

我们手机上有很多的app,而每个服务端是一家公司写的,但是客户端却是多个公司写的
如果我们绑定了特定的端口,万一两个公司都用了同一个端口号呢?这样就直接冲突了

OS会自动填充主机IP和随机生成端口号进行绑定(在发送数据的时候自动绑定)
所以创建客户端我们只用创建套接字即可

三、问题与改进

3.1 问题描述

在这里插入图片描述

上述图片是服务端的代码,由于整个服务端都是单进程的,所以这意味着在同一时间只能有一个客户端的链接能被accept,其他客户端的信息是接受不到的,因为一但某个客户端被成功accept了,那么单进程就会走到Service中,Service是一个死循环服务,所以只要客户端不退出,服务端是无法accept到其他链接的

3.2 解决方法

3.2.1 问题版本

在这里插入图片描述

3.2.2 多进程版

使用多进程,主进程进行accept,子进程进行Service服务

引入新问题
主进程需要阻塞等待子进程退出,还是accept不了新连接

两个解决办法

  1. 用孙子进程执行服务
            pid_t pid = fork();
            if (pid < 0)
            {
                lg.LogMessage(Fatal, "fork error");
                close(sockfd);
                continue;
            }
            else if (pid == 0)
            {
                close(_listensock);
                if (fork() == 0)
                {
                    Service(sockfd);
                    close(sockfd);
                    exit(0);
                }
                exit(0);
            }
            close(sockfd);
            waitpid(pid, nullptr, 0);
  1. 自定义信号,让父进程忽略子进程结束时发送给父进程的信号
        	signal(SIGCHLD,SIG_IGN);
            pid_t pid = fork();
            if (pid < 0)
            {
                lg.LogMessage(Fatal, "fork error");
                close(sockfd);
                continue;
            }
            else if(pid == 0)
            {
                close(_listensock);
                Service(sockfd);
                close(sockfd);
                exit(0);
            }

            close(sockfd);

3.4.3 进程池(暂未实现)

存在问题:
如果先创建子进程备用,子进程拿不到主进程accept的sockfd

3.4.4 多线程版

篇幅较长,Gitee连接:多线程版

3.4.5 线程池版

篇幅较长Gitee连接:线程池版

四、TCP与UDP的对比

对比UDP服务器,TCP服务器多了获取新链接和监听的操作
因为UDP是不可靠传输,而TCP是是可靠传输,所以TCP在传输数据之前会进行连接的建立

UDP和TCP的区别:

对于TCP协议有几个特点:

  1. 传输层协议
  2. 有连接(正式通信前要先建立连接)
  3. 可靠传输(在内部帮我们做可靠传输工作)
  4. 面向字节流

对于UDP协议有几个特点:

  1. 传输层协议
  2. 无连接
  3. 不可靠传输
  4. 面向数据报

注意:
这里的可靠与不可靠并不是贬义词,而是中性词,可靠或者不可靠形容的是特点,而不是优劣
并不是说可靠传输就好用,而是要区分应用场景和需求

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

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

相关文章

江苏捷科云:可视化平台助力制造企业智能化管理

公司简介 江苏捷科云信息科技有限公司&#xff08;以下简称“捷科”&#xff09;是一家专注于云平台、云储存、云管理等产品领域的创新型企业&#xff0c;集研发、生产和销售于一体&#xff0c;致力于在网络技术领域打造尖端品牌。在推动制造业企业数字化转型的进程中&#xf…

消息队列(一)消息队列的工作流程

什么是消息队列 首先&#xff0c;代入一个场景&#xff0c;我现在做一个多系统的集成&#xff0c;分别有系统A、B、C、D四个系统&#xff0c;A系统因为使用产生了业务数据&#xff0c;B、C、D需要使用这些数据做相关的业务处理和运算&#xff0c;最基本的做法就是通过接口通信…

施耐德变频器ATV320系列技术优势:创新与安全并重

在工业自动化领域&#xff0c;追求高效、安全与智能已成为不可阻挡的趋势。施耐德变频器ATV320系列凭借其强大的设计标准和全球认证&#xff0c;成为能够帮助企业降低安装成本&#xff0c;提高设备性能的创新解决方案。 【全球认证&#xff0c;品质保障】ATV320 系列秉持施耐德…

WEB入门——文件上传漏洞

文件上传漏洞 一、文件上传漏洞 1.1常见的WebShell有哪些&#xff1f;1.2 一句话木马演示1.2 文件上传漏洞可以利用需满足三个条件1.3 文件上传导致的危害 二、常用工具 2.1 搭建upload-labs环境2.2 工具准备 三、文件上传绕过 3.1 客户端绕过 3.1.1 实战练习 &#xff1a;upl…

Android 蓝牙开发-传输数据

概述 传统蓝牙是通过建立REFCCOM sockect来进行通信的&#xff0c;类似于socket通信&#xff0c;一台设备需要开放服务器套接字并处于listen状态&#xff0c;而另一台设备使用服务器的MAC地址发起连接。连接建立后&#xff0c;服务器和客户端就都通过对BluetoothSocket进行读写…

红米Note 9 Pro5G刷小米官方系统

前言 刷机有2种方式&#xff1a;线刷 和 卡刷。 线刷 线刷&#xff1a;需要用电脑刷机工具&#xff0c;例如&#xff1a;XiaoMiFlash.exe&#xff0c;通过电脑和数据线对设备进行刷机。 适用场景&#xff1a; 系统损坏无法开机。恢复官方出厂固件。刷机失败导致软砖、硬砖的…

html + css 淘宝网实战

之前有小伙伴说&#xff0c;淘宝那么牛逼你会写代码&#xff0c;能帮我做一个一样的淘宝网站吗&#xff0c;好呀&#xff0c;看我接下来如何给你做一个淘宝首页。hahh,开个玩笑。。。学习而已。 在进行html css编写之前 先了解下网页的组成和网页元素的尺寸吧 1.网页的组成 …

SOME/IP 协议详解——信息格式

文章目录 1. 头部格式1.1 消息 ID&#xff08;Message ID&#xff09;1.2 长度&#xff08;Length&#xff09;1.3 请求 ID&#xff08;Request ID&#xff09;1.4 协议版本&#xff08;Protocol Version&#xff09;&#xff1a;1.5 接口版本&#xff08;Interface Version&am…

使用QML实现播放器进度条效果

使用QML实现播放进度效果 QML Slider介绍 直接上DEMO如下&#xff1a; Slider {width: 300;height: 20;orientation: Qt.Vertical; //决定slider是横还是竖 默认是HorizontalstepSize: 0.1;value: 0.2;tickmarksEnabled: true; //显示刻度}效果图如下 那么我先改变滑块跟滚轮…

Android——自定义按钮button

项目中经常高频使用按钮&#xff0c;要求&#xff1a;可设置颜色&#xff0c;有圆角且有按下效果的Button 一、自定义按钮button button的代码为 package com.fslihua.clickeffectimport android.annotation.SuppressLint import android.content.Context import android.gra…

【双指针算法】--复写零(Java版)

文章目录 1. 题目2. 题目解析3. 代码 1. 题目 在线oj 给你一个长度固定的整数数组 arr &#xff0c;请你将该数组中出现的每个零都复写一遍&#xff0c;并将其余的元素向右平移。 注意&#xff1a;请不要在超过该数组长度的位置写入元素。请对输入的数组 就地 进行上述修改&a…

联合目标检测与图像分类提升数据不平衡场景下的准确率

联合目标检测与图像分类提升数据不平衡场景下的准确率 在一些数据不平衡的场景下&#xff0c;使用单一的目标检测模型很难达到99%的准确率。为了优化这一问题&#xff0c;适当将其拆解为目标检测模型和图像分类模型的组合&#xff0c;可以更有效地控制最终效果&#xff0c;尤其…

HDR视频技术之十:MPEG 及 VCEG 的 HDR 编码优化

与传统标准动态范围&#xff08; SDR&#xff09;视频相比&#xff0c;高动态范围&#xff08; HDR&#xff09;视频由于比特深度的增加提供了更加丰富的亮区细节和暗区细节。最新的显示技术通过清晰地再现 HDR 视频内容使得为用户提供身临其境的观看体验成为可能。面对目前日益…

LabVIEW声音信号处理系统

开发了一种基于LabVIEW的声音信号处理系统&#xff0c;通过集成的信号采集与分析一体化解决方案&#xff0c;提升电子信息领域教学与研究的质量。系统利用LabVIEW图形化编程环境和硬件如USB数据采集卡及声音传感器&#xff0c;实现了从声音信号的采集到频谱分析的全过程。 项目…

OpenCL(壹):了解OpenCL模型到编写第一个CL内核程序

目录 1.前言 2.简单了解OpenCL 3.为什么要使用OpenCL 4.OpenCL架构 5.OpenCL中的平台模型(Platform Model) 6.OpenCL中的内存模型(Execution Model) 7.OpenCL中的执行模型(Memory Model) 8.OpenCL中的编程模型(Programmin Model) 9.OpenCL中的同步机制 10.编写第一个OpenCL程序…

Flutter组件————Scaffold

Scaffold Scaffold 是一个基础的可视化界面结构组件&#xff0c;它实现了基本的Material Design布局结构。使用 Scaffold 可以快速地搭建起包含应用栏&#xff08;AppBar&#xff09;、内容区域&#xff08;body&#xff09;、抽屉菜单&#xff08;Drawer&#xff09;、底部导…

【数据结构】数据结构整体大纲

数据结构用来干什么的&#xff1f;很简单&#xff0c;存数据用的。 &#xff08;这篇文章仅介绍数据结构的大纲&#xff0c;详细讲解放在后面的每一个章节中&#xff0c;逐个击破&#xff09; 那为什么不直接使用数组、集合来存储呢 ——> 如果有成千上亿条数据呢&#xff…

搭建Elastic search群集

一、实验环境 二、实验步骤 Elasticsearch 是一个分布式、高扩展、高实时的搜索与数据分析引擎Elasticsearch目录文件&#xff1a; /etc/elasticsearch/elasticsearch.yml#配置文件 /etc/elasticsearch/jvm.options#java虚拟机 /etc/init.d/elasticsearch#服务启动脚本 /e…

链原生 Web3 AI 网络 Chainbase 推出 AVS 主网, 拓展 EigenLayer AVS 场景

在 12 月 4 日&#xff0c;链原生的 Web3 AI 数据网络 Chainbase 正式启动了 Chainbase AVS 主网&#xff0c;同时发布了首批 20 个 AVS 节点运营商名单。Chainbase AVS 是 EigenLayer AVS 中首个以数据智能为应用导向的主网 AVS&#xff0c;其采用四层网络架构&#xff0c;其中…

玩转OCR | 探索腾讯云智能结构化识别新境界

&#x1f4dd;个人主页&#x1f339;&#xff1a;Eternity._ &#x1f339;&#x1f339;期待您的关注 &#x1f339;&#x1f339; ❀ 玩转OCR 腾讯云智能结构化识别产品介绍服务应用产品特征行业案例总结 腾讯云智能结构化识别 腾讯云智能结构化OCR产品分为基础版与高级版&am…