【网络】socket和udp协议

news2024/11/14 16:38:53

socket

  • 一、六个背景知识
    • 1、Q1:在进行网络通信时,是不是两台机器在进行通信?
    • 2、端口号
    • 3、端口号vs进程PID
    • 4、目的端口怎么跟客户端绑定的呢?也就是怎么通过目的端口去找到对应的进程的呢?
    • 5、我们的客户端,怎么知道服务器的端口号的呢?
    • 6、一个进程可以绑定多个端口号吗?一个端口号可以被多个进程绑定吗?
  • 二、两个协议
    • 1、TCP协议(传输控制协议)
    • 2、UDP协议(用户数据报协议)
  • 三、网络字节序列
  • 四、socket编程接口
    • 1、socket 常见API
    • 2、sockaddr结构
  • 五、udp_socket_server代码编写
    • 1、socket套接字的介绍
    • 2、Log.hpp(为了打印看结果)
    • 3、文件描述符socket是3(套接字创建)
    • 4、bind socket
    • 5、recvfrom和sendto
    • 6、测试一下
    • 7、两个问题(port和ip)
    • 8、argc 和 argv的联合使用
    • 9、总体代码
  • 六、udp_socket_client代码编写
    • 1、客户端要绑定吗?
    • 2、代码部分
    • 3、C/S逻辑
    • 4、效果展示
  • 七、改进代码(解耦合)
    • 1、使用functional改进
    • 2、改进使用linux命令(popen)
    • 3、windows与linux进行数据收发
    • 4、udp简易聊天室
  • 八、地址转换函数
    • 1、介绍
    • 2、关于inet_ntoa


一、六个背景知识

1、Q1:在进行网络通信时,是不是两台机器在进行通信?

答:不是的,是应用层在通信。
解析:网络协议中的下三层,主要解决的是数据安全可靠的送到远端机器。用户使用应用层软件完成数据发送和接收的。
而在使用软件的时候,必须得先启动软件,例如我们想刷抖音就需要先把抖音这个软件启动,运行以后就是进程。所以我们网络在进行通信和收发消息的时候,就是进程间通信啊!只不过是进程之间遵守了网络协议栈,用的是网络协议的系统调用接口罢了,其本质还是进程间通信。手段是两台主机通信,而目的和本质是进程之间的通信,是凌驾于应用层上的进程之间的通信。通过网络协议栈读取网络资源(共享内存资源)来让两台主机读取/存放信息。可以用读者写者这个问题来进行理解,我们的网络资源就是缓冲区的概念,读者写者在缓冲区(网络资源)中读取/存放资源。

2、端口号

在这里插入图片描述

端口号是一个2字节16位的整数;
端口号用来标识一个进程, 告诉操作系统, 当前的这个数据要交给哪一个进程来处理;
IP地址 + 端口号能够标识网络上的某一台主机的某一个进程;
一个端口号只能被一个进程占用。

注意概念:
在公网上,ip地址能标识唯一的一台主机,端口号port能标识该主机上的唯一一个进程,故:ip:port能标识全网唯一的一个进程。

socket:客户端和服务器在进行通信的时候,客户端进程有唯一的ip地址+端口号,服务端进程也有唯一的ip地址+端口号,两者只需要进行源和目的的唯一标识即发生通信,这就是socket的概念基石。

3、端口号vs进程PID

pid已经是能够标识一台主机上的进程的唯一性的,为什么还要搞个端口号这么复杂的概念?
引入端口号是为了实现系统跟网络功能解耦合,不会引起牵一发而动全身,例如我们的学号和身份证件号的关系。

4、目的端口怎么跟客户端绑定的呢?也就是怎么通过目的端口去找到对应的进程的呢?

在这里插入图片描述

5、我们的客户端,怎么知道服务器的端口号的呢?

每一个服务器的端口号必须是众所周知的精心设计的,要被客户端熟知的,我抖音自己的开发商在开发的时候,客户端和服务端的端口号都是内置的,都是被自己熟知的,所以我们使用者感觉不到这个,但是作为开发人员是要熟知服务器的端口号的。

6、一个进程可以绑定多个端口号吗?一个端口号可以被多个进程绑定吗?

显而易见:一个进程可以绑定多个端口号,但一个端口号不能被多个进程绑定。

二、两个协议

1、TCP协议(传输控制协议)

传输层协议
有连接
可靠传输(前提网络要联通,复杂,维护性要更强)
面向字节流

2、UDP协议(用户数据报协议)

传输层协议
无连接
不可靠传输(简单,但丢包问题解决不了)
面向数据报

三、网络字节序列

我们已经知道,内存中的多字节数据相对于内存地址有大端和小端之分, 磁盘文件中的多字节数据相对于文件中的偏移地址也有大端小端之分, 网络数据流同样有大端小端之分. 那么如何定义网络数据流的地址呢?

发送主机通常将发送缓冲区中的数据按内存地址从低到高的顺序发出;
接收主机把从网络上接到的字节依次保存在接收缓冲区中,也是按内存地址从低到高的顺序保存;
因此,网络数据流的地址应这样规定:先发出的数据是低地址,后发出的数据是高地址.
TCP/IP协议规定,网络数据流应采用大端字节序,即低地址高字节.
不管这台主机是大端机还是小端机, 都会按照这个TCP/IP规定的网络字节序来发送/接收数据;
如果当前发送主机是小端, 就需要先将数据转成大端; 否则就忽略, 直接发送即可;

为使网络程序具有可移植性,使同样的C代码在大端和小端计算机上编译后都能正常运行,可以调用以下库函数做网络字节序和主机字节序的转换。

在这里插入图片描述

这些函数名很好记,h表示host,n表示network,l表示32位长整数,s表示16位短整数。
例如htonl表示将32位的长整数从主机字节序转换为网络字节序,例如将IP地址转换后准备发送。
如果主机是小端字节序,这些函数将参数做相应的大小端转换然后返回;
如果主机是大端字节序,这些 函数不做转换,将参数原封不动地返回。

四、socket编程接口

1、socket 常见API

// 创建 socket 文件描述符 (TCP/UDP, 客户端 + 服务器)
int socket(int domain, int type, int protocol);
// 绑定端口号 (TCP/UDP, 服务器) 
int bind(int socket, const struct sockaddr *address,
 socklen_t address_len);
// 开始监听socket (TCP, 服务器)
int listen(int socket, int backlog);
// 接收请求 (TCP, 服务器)
int accept(int socket, struct sockaddr* address,
 socklen_t* address_len);
// 建立连接 (TCP, 客户端)
int connect(int sockfd, const struct sockaddr *addr,
 socklen_t addrlen);

2、sockaddr结构

在这里插入图片描述

问题:为什么struct sockaddr* 不能写成void*呢?
因为刚开始网络出现的时候c语言还没有void*这个用法。

五、udp_socket_server代码编写

1、socket套接字的介绍

在这里插入图片描述

udp用的是SOCK_DGRAM,那么就是无连接不可靠的协议。

2、Log.hpp(为了打印看结果)

#pragma once

#include <iostream>
#include <time.h>
#include <stdarg.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include <stdlib.h>

#define SIZE 1024

#define Info 0
#define Debug 1
#define Warning 2
#define Error 3
#define Fatal 4

#define Screen 1
#define Onefile 2
#define Classfile 3

#define LogFile "log.txt"

class Log
{
public:
    Log()
    {
        printMethod = Screen;
        path = "./log/";
    }
    void Enable(int method)
    {
        printMethod = method;
    }
    std::string levelToString(int level)
    {
        switch (level)
        {
        case Info:
            return "Info";
        case Debug:
            return "Debug";
        case Warning:
            return "Warning";
        case Error:
            return "Error";
        case Fatal:
            return "Fatal";
        default:
            return "None";
        }
    }

    // void logmessage(int level, const char *format, ...)
    // {
    //     time_t t = time(nullptr);
    //     struct tm *ctime = localtime(&t);
    //     char leftbuffer[SIZE];
    //     snprintf(leftbuffer, sizeof(leftbuffer), "[%s][%d-%d-%d %d:%d:%d]", levelToString(level).c_str(),
    //              ctime->tm_year + 1900, ctime->tm_mon + 1, ctime->tm_mday,
    //              ctime->tm_hour, ctime->tm_min, ctime->tm_sec);

    //     // va_list s;
    //     // va_start(s, format);
    //     char rightbuffer[SIZE];
    //     vsnprintf(rightbuffer, sizeof(rightbuffer), format, s);
    //     // va_end(s);

    //     // 格式:默认部分+自定义部分
    //     char logtxt[SIZE * 2];
    //     snprintf(logtxt, sizeof(logtxt), "%s %s\n", leftbuffer, rightbuffer);

    //     // printf("%s", logtxt); // 暂时打印
    //     printLog(level, logtxt);
    // }
    void printLog(int level, const std::string &logtxt)
    {
        switch (printMethod)
        {
        case Screen:
            std::cout << logtxt << std::endl;
            break;
        case Onefile:
            printOneFile(LogFile, logtxt);
            break;
        case Classfile:
            printClassFile(level, logtxt);
            break;
        default:
            break;
        }
    }
    void printOneFile(const std::string &logname, const std::string &logtxt)
    {
        std::string _logname = path + logname;
        int fd = open(_logname.c_str(), O_WRONLY | O_CREAT | O_APPEND, 0666); // "log.txt"
        if (fd < 0)
            return;
        write(fd, logtxt.c_str(), logtxt.size());
        close(fd);
    }
    void printClassFile(int level, const std::string &logtxt)
    {
        std::string filename = LogFile;
        filename += ".";
        filename += levelToString(level); // "log.txt.Debug/Warning/Fatal"
        printOneFile(filename, logtxt);
    }

    ~Log()
    {
    }
    void operator()(int level, const char *format, ...)
    {
        time_t t = time(nullptr);
        struct tm *ctime = localtime(&t);
        char leftbuffer[SIZE];
        snprintf(leftbuffer, sizeof(leftbuffer), "[%s][%d-%d-%d %d:%d:%d]", levelToString(level).c_str(),
                 ctime->tm_year + 1900, ctime->tm_mon + 1, ctime->tm_mday,
                 ctime->tm_hour, ctime->tm_min, ctime->tm_sec);

        va_list s;
        va_start(s, format);
        char rightbuffer[SIZE];
        vsnprintf(rightbuffer, sizeof(rightbuffer), format, s);
        va_end(s);

        // 格式:默认部分+自定义部分
        char logtxt[SIZE * 2];
        snprintf(logtxt, sizeof(logtxt), "%s %s", leftbuffer, rightbuffer);

        // printf("%s", logtxt); // 暂时打印
        printLog(level, logtxt);
    }

private:
    int printMethod;
    std::string path;
};

// int sum(int n, ...)
// {
//     va_list s; // char*
//     va_start(s, n);

//     int sum = 0;
//     while(n)
//     {
//         sum += va_arg(s, int); // printf("hello %d, hello %s, hello %c, hello %d,", 1, "hello", 'c', 123);
//         n--;
//     }

//     va_end(s); //s = NULL
//     return sum;
// }

3、文件描述符socket是3(套接字创建)

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

4、bind socket

在这里插入图片描述

// 2.绑定端口号bind socket
        struct sockaddr_in local; // 网络套接字结构体
        bzero(&local, sizeof(local)); // 将该套接字结构体对象全部清零
        local.sin_family = AF_INET; // 类型:ipv4
        local.sin_port = htons(_port); // 端口号:是在网络中来回发送的,我发过去要让对面知道我发的端口号是什么,所以必须是网络字节序列
        local.sin_addr.s_addr = inet_addr(_ip.c_str()); // 1.string->unit_32 2.来回通信对方要知道发送的ip,所以ip的unit_32必须是网络序列的
        int n = bind(_socketfd, (const struct sockaddr *)&local, sizeof(local));
        if (n < 0)
        {
            log(Fatal, "bind error, erron:%d, errno string:%s", errno, strerror(errno));
            exit(BIND_ERR);
        }
        log(Info, "bind sucess");

在这里插入图片描述

5、recvfrom和sendto

在这里插入图片描述

struct sockaddr_in client;
            socklen_t len = sizeof(client);
            ssize_t n = recvfrom(_socketfd, inbuffer, sizeof(inbuffer) - 1, 0, (struct sockaddr*)&client, &len);
            if (n < 0)
            {
                log(Warning, "recvfrom error");
                continue;
            }
            // 简单的数据处理一下
            inbuffer[n] = 0;
            std::string info = inbuffer;
            std::string echo_string = "server_echo#" + info;

            sendto(_socketfd, echo_string.c_str(), echo_string.size(), 0, (const struct sockaddr*)&client, len);

6、测试一下

在这里插入图片描述

7、两个问题(port和ip)

Q1.公网ip问题:

ip地址全0,port随意?还是我们ip是云服务器,port随意呢?
在这里插入图片描述
其实就是我们云服务器可能有好几个ip地址能够进行访问,我们单独写一个ip地址肯定是不行的,我们需要一个动态的ip地址,也就是这一个ip地址就能是整个云服务器主机所有暴露出来的ip地址,那么就简单了,我们的ip只要是全0,port随意即可。
在这里插入图片描述
Q2:系统端口号0-1023:
在这里插入图片描述

注意:0到1023是系统内定的系统端口号,一般都要有固定的应用层协议使用,例如:http对应固定端口号是80,https对应固定的端口号是443等等,所以0到1023不让普通用户去绑定,因为与本身的应用层协议是强相关的!这是因为这个应用层协议用这个端口用多了,形成了一种固定的绑定了。所以我们进行端口绑定的时候用的是1024及往上的端口号使用,我们一般用8000到9000左右,好记好用。

8、argc 和 argv的联合使用

在这里插入图片描述

在这里插入图片描述

9、总体代码

udp.hpp:

#pragma once 

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

extern Log log;

uint16_t defaultport = 8080;
std::string defaultip = "0.0.0.0";

enum 
{
    SOCKET_ERR=1,
    BIND_ERR
};

class UdpServer
{
public:
    // 构造函数
    UdpServer(const uint16_t &port = defaultport, const std::string &ip = defaultip)
        : _socketfd(0)
        , _port(port)
        , _ip(ip)
        , _isrunning(false)
    {}
    void Init()
    {
        // 1.创建udp套接字socket
        _socketfd = socket(AF_INET, SOCK_DGRAM, 0);
        // 创建失败
        if (_socketfd < 0)
        {
            log(Fatal, "socket create error,socketfd:%d", _socketfd);
            exit(SOCKET_ERR);
        }
        // 创建成功
        log(Info, "socket create sucess,socketfd:%d", _socketfd);

        // 2.绑定端口号bind socket
        struct sockaddr_in local; // 网络套接字结构体
        bzero(&local, sizeof(local)); // 将该套接字结构体对象全部清零
        local.sin_family = AF_INET; // 类型:ipv4
        local.sin_port = htons(_port); // 端口号:是在网络中来回发送的,我发过去要让对面知道我发的端口号是什么,所以必须是网络字节序列
        local.sin_addr.s_addr = inet_addr(_ip.c_str()); // 1.string->unit_32 2.来回通信对方要知道发送的ip,所以ip的unit_32必须是网络序列的
        int n = bind(_socketfd, (const struct sockaddr *)&local, sizeof(local));
        if (n < 0)
        {
            log(Fatal, "bind error, erron:%d, errno string:%s", errno, strerror(errno));
            exit(BIND_ERR);
        }
        log(Info, "bind sucess");
    }
    void Run()
    {
        _isrunning = true;
        char inbuffer[1024];
        while (_isrunning)
        {
            struct sockaddr_in client;
            socklen_t len = sizeof(client);
            ssize_t n = recvfrom(_socketfd, inbuffer, sizeof(inbuffer) - 1, 0, (struct sockaddr*)&client, &len);
            if (n < 0)
            {
                log(Warning, "recvfrom error");
                continue;
            }
            // 简单的数据处理一下
            inbuffer[n] = 0;
            std::string info = inbuffer;
            std::string echo_string = "server_echo#" + info;

            sendto(_socketfd, echo_string.c_str(), echo_string.size(), 0, (const struct sockaddr*)&client, len);
        }
    }
    // 析构函数
    ~UdpServer()
    {
        if (_socketfd > 0) 
        {
            close(_socketfd);
        }
    }
private:
    int _socketfd; // 网络文件描述符,表示socket返回的文件描述符
    uint16_t _port; // 表明服务器进程的端口号
    std::string _ip; // ip地址,任意地址绑定为0
    bool _isrunning; // 判断是否运行
};

main.cc:

#include "udp.hpp"
#include "Log.hpp"
#include <memory>

Log log;

void Usage(std::string proc)
{
    std::cout << "\n\rUsages: " << proc << "port[1024+]\n" << std::endl;
}

// 以后用的是./udpserver + port
int main(int argc, char *argv[])
{
    if (argc != 2)
    {
        Usage(argv[0]);
        exit(0);
    }
    uint16_t port = std::stoi(argv[1]);
    std::unique_ptr<UdpServer> svr(new UdpServer(port)); // new一个对象

    svr->Init(); // 初始化
    svr->Run();  // 跑起来
}

六、udp_socket_client代码编写

1、客户端要绑定吗?

客户端要有具体的ip地址和端口号port的,所以一定是需要绑定的,但是很多人说不需要绑定,这种说法是错误的,因为只是因为客户端不需要用户显示的绑定!一般是由操作系统随机选择的绑定!
解释:一个端口号只能被一个进程绑定,对于server端是如此,对于client端也是如此,而我们计算机上有几百个应用,我们假如说是这前十几个应用把常见的端口号都占了的话,我们后面的应用进程开启来的时候,就不能用这几个常见端口号,打开一次失败一次,试完了这几个常见端口号以后,再用其他冷门的端口号启动就变得很慢,用户自定义的端口号是很粗糙很不安全的,所以由OS操作系统随机的分配更加的安全和有效公平。其实客户端的端口号是多少并不重要,只要能够保证主机上的唯一性即可。

系统什么时候给我绑定端口号的呢?首次发送数据的时候,客户端就进行随机绑定端口号了(即客户端代码跑到sendto的时候)。

2、代码部分

#include <iostream>
#include <unistd.h>
#include <string>
#include <cstdlib>
#include <strings.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>

void Usage(std::string proc)
{
    std::cout << "\n\rUsages: " << proc << "serverip serverport\n" << std::endl;
}

// ./udpclient serverip serverport
int main(int argc, char *argv[])
{
    if (argc != 3)
    {
        Usage(argv[0]);
        exit(0);
    }
    std::string serverip = argv[1]; // serverip
    uint16_t serverport = std::stoi(argv[2]); // serverport

    int socketfd = socket(AF_INET, SOCK_DGRAM, 0); // 创建套接字
    if (socketfd < 0)
    {
        std::cout << "socket create error" << std::endl;
        return 1;
    }

    std::string message;
    char buffer[1024];
    while (true)
    {
        // 数据
        std::cout << "Please Enter# ";
        getline(std::cin, message);
        // 给谁发
        struct sockaddr_in server;
        bzero(&server, sizeof(server));
        server.sin_family = AF_INET;
        server.sin_port = htons(serverport);
        server.sin_addr.s_addr = inet_addr(serverip.c_str());
        
        // 发送数据 -- 把数据发送到socketfd文件中,并将server信息提炼出来发送给server,可以理解成唤醒server
        socklen_t len = sizeof(server);
        sendto(socketfd, message.c_str(), message.size(), 0, (struct sockaddr *)&server, len);

        // 收数据 -- 从socket文件中的数据拿出来到buffer中,并将收到的对方的个人信息进行保存到temp中
        struct sockaddr_in temp;
        socklen_t len2 = sizeof(temp);
        ssize_t n = recvfrom(socketfd, buffer, 1023, 0, (struct sockaddr*)&temp, &len2);
        if (n > 0)
        {
            buffer[n] = 0;
            // 打印数据
            std::cout << buffer << std::endl;
        }
    }

    close(socketfd);
    return 0;
}

3、C/S逻辑

在这里插入图片描述

4、效果展示

在这里插入图片描述

七、改进代码(解耦合)

1、使用functional改进

在这里插入图片描述

2、改进使用linux命令(popen)

介绍127.0.0.1本地环回地址:
通常用来进行本地测试的。

bool SafeCheck(const std::string& cmd)
{
    std::vector<std::string> word_key = {
        "rm",
        "top",
        "cp",
        "yum",
        "while",
        "kill",
        "unlink"
        "uninstall",
        "top"
    };
    for (auto &word : word_key)
    {
        auto pos = cmd.find(word);
        if (pos != std::string::npos)
        {
            return false;
        }
    }
    return true;
}

std::string ExcuteCommand(const std::string& cmd)
{
    // 做一个保护
    if (!SafeCheck(cmd)) return "bad man";
    
    FILE* fp = popen(cmd.c_str(), "r"); // 管道创建好,子进程创建好,子进程通过管道放到父进程
    if (nullptr == fp)
    {
        perror("popen failed");
        return "error";
    }
    std::string result;
    char buffer[4096];
    while (true)
    {
        char* ok = fgets(buffer, sizeof(buffer), fp); // 写到buffer缓冲区中
        if (ok == nullptr)
        {
            break;
        }
        result += buffer;
    }
    pclose(fp);
    return result;
}

在这里插入图片描述

3、windows与linux进行数据收发

互通代码及成果展示

4、udp简易聊天室

udp简易聊天室

八、地址转换函数

1、介绍

字符串转in_addr的函数:
在这里插入图片描述

in_addr转字符串的函数:
在这里插入图片描述

其中inet_pton和inet_ntop不仅可以转换IPv4的in_addr,还可以转换IPv6的in6_addr,因此函数接口是void*addrptr

2、关于inet_ntoa

inet_ntoa这个函数返回了一个char*, 很显然是这个函数自己在内部为我们申请了一块内存来保存ip的结果. 那么是否需要调用者手动释放呢?
在这里插入图片描述
man手册上说, inet_ntoa函数, 是把这个返回结果放到了静态存储区. 这个时候不需要我们手动进行释放.
那么问题来了, 如果我们调用多次这个函数, 会有什么样的效果呢? 参见如下代码:
在这里插入图片描述

因为inet_ntoa把结果放到自己内部的一个静态存储区, 这样第二次调用时的结果会覆盖掉上一次的结果.

思考: 如果有多个线程调用 inet_ntoa, 是否会出现异常情况呢?
在APUE中, 明确提出inet_ntoa不是线程安全的函数;
但是在centos7上测试, 并没有出现问题, 可能内部的实现加了互斥锁;
同学们课后自己写程序验证一下在自己的机器上inet_ntoa是否会出现多线程的问题;
在多线程环境下, 推荐使用inet_ntop, 这个函数由调用者提供一个缓冲区保存结果, 可以规避线程安全问题;

#include <stdio.h>
#include <unistd.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <pthread.h>
void* Func1(void* p) {
 struct sockaddr_in* addr = (struct sockaddr_in*)p;
 while (1) {
 char* ptr = inet_ntoa(addr->sin_addr);
 printf("addr1: %s\n", ptr);
 }
 return NULL;
}
void* Func2(void* p) {
 struct sockaddr_in* addr = (struct sockaddr_in*)p;
 while (1) {
 char* ptr = inet_ntoa(addr->sin_addr);
  printf("addr2: %s\n", ptr);
 }
 return NULL;
}
int main() {
 pthread_t tid1 = 0;
 struct sockaddr_in addr1;
 struct sockaddr_in addr2;
 addr1.sin_addr.s_addr = 0;
 addr2.sin_addr.s_addr = 0xffffffff;
 pthread_create(&tid1, NULL, Func1, &addr1);
 pthread_t tid2 = 0;
 pthread_create(&tid2, NULL, Func2, &addr2);
 pthread_join(tid1, NULL);
 pthread_join(tid2, NULL);
 return 0;
}

在这里插入图片描述

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

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

相关文章

textblob文本处理、词性分析与情感分析

1 前言 textBlob 是一個简单易用的 NLP库&#xff0c;基于 NLTK 和 pattern库&#xff0c; 提供了文本处理和情感分析等功能。 安装 textblob0.18.0 nltk3.8.1测试环境&#xff1a;Python3.10.9 使用前&#xff0c;先运行下面代码先下载些文件 import nltk nltk.download…

机器学习 | 阿里云安全恶意程序检测

目录 一、数据探索1.1 数据说明1.2 训练集数据探索1.2.1 数据特征类型1.2.2 数据分布1.2.3 缺失值1.2.4 异常值1.2.5 标签分布探索 1.3 测试集探索1.3.1 数据信息1.3.2 缺失值1.3.3 数据分布1.3.4 异常值 1.4 数据集联合分析1.4.1 file_id 分析1.4.2 API 分析 二、特征工程与基…

「YD-221WA无线多合一变送器」让高效监测触手可及!

前言 近年来&#xff0c;伴随着“中国制造2025”、“互联网”在我国的全面推进&#xff0c;智能无线仪表设备在工业控制领域大规模应用。“设备上云”成为众多企业实现数字化转型升级的重要策略&#xff0c;为提升仪表设备管理软件的网络化、智能化、易维护性等方面提供了强有…

问题记录-SpringBoot 2.7.2 整合 Swagger 报错

详细报错如下 报错背景&#xff0c;我将springboot从2.3.3升级到了2.7.2&#xff0c;报了下面的错误&#xff1a; org.springframework.context.ApplicationContextException: Failed to start bean documentationPluginsBootstrapper; nested exception is java.lang.NullPo…

1.3、校验码

校验码 简介奇偶校验编码方法分类注意练习题 CRC循环冗余校验模2除法异或运算模2除法计算过程 循环冗余校验CRC过程练习题选择题 海明校验步骤练习题 简介 计算机在接收相应信息的时候&#xff0c;能够识别的都是一些电信号或者转化后的0 1二进制。那包括我们在网络上传递信息…

昇思25天学习打卡营第16天|LLM-MindNLP ChatGLM-6B StreamChat

打卡 目录 打卡 任务说明 环境配置 部署方式 ChatGLM-6B 体验截图示例 ChatGLM-6B 模型结构解析如下 ChatGLM2-6B 模型结构解析如下 任务说明 加载智谱清言的chatglm模型权重文件&#xff08;目前有4个版本&#xff09;&#xff0c;本次主要尝试了chatglm-6b。 chatgl…

人工智能(AI)在办公场所的广泛应用

人工智能&#xff08;AI&#xff09;在办公场所的广泛应用正逐步改变着我们的工作方式和效率。随着技术的进步&#xff0c;越来越多的公司和组织开始采用各种AI技术来优化工作流程、提升生产力&#xff0c;并提供更好的用户体验。以下是人工智能在办公方面的一些主要作用和影响…

C++中,虚函数的作用详解

我个人认为虚函数的作用有两个&#xff1a; 增加安全性&#xff1b;提醒子类去做该做的事情。 提高效率&#xff1b;不是指程序执行效率&#xff0c;而是编码效率。 首先我这里要纠正一下&#xff1a; 一个函数被定义为虚函数&#xff0c;不代表这个函数未被实现&#xff1…

leetcode.nvim使用cookie无法登陆问题

错误描述&#xff1a; 使用力扣 (LeetCode) 全球极客挚爱的技术成长平台 的cookie在neovim上使用leetcode.nvim进行登录会出现curl xxx -D xxxx的报错。 解决方法&#xff1a; 使用LeetCode - The Worlds Leading Online Programming Learning Platform这个网站的cookie进行登…

这7款高效爬虫工具软件,非常实用!

在当今数据驱动的时代&#xff0c;自动化爬虫工具和软件成为了许多企业和个人获取数据的重要手段。这里会介绍6款功能强大、操作简便的自动化爬虫工具&#xff0c;用好了可以更高效地进行数据采集。 1. 八爪鱼采集器 八爪鱼是一款功能强大的桌面端爬虫软件&#xff0c;主打可…

轮船控制系统nmea2000电缆组件 7/8 T型连接器

NMEA 2000 7/8 T型连接器概述 NMEA 2000 7/8 T型连接器是专为船舶控制系统设计的电缆组件&#xff0c;主要用于连接船上的各种电子设备和系统&#xff0c;如GPS接收器、自动驾驶仪、风速和风向传感器、深度声纳等。这些设备通过NMEA 2000总线共享数据&#xff0c;包括导航信息…

1.Fabric框架

要了解Fabric&#xff0c;首先要知道Hyperledger开源项目。 2015年12月&#xff0c;由开源世界的旗舰组织Linux基金会牵头&#xff0c;30家初始企业成员共同宣布Hyperledger联合项目成立。Hyperledger 超级账本&#xff0c;是首个面向企业应用场景的分布式账本平台&#xff0c…

【算法】深入理解并优化算法:提升软件开发效率与质量

目录 一、算法的基本概念 输入 输出 确定性 有限性 有效性 二、常见算法类型 1. 排序算法 选择排序&#xff08;Selection Sort&#xff09; 插入排序&#xff08;Insertion Sort&#xff09; 快速排序&#xff08;Quick Sort&#xff09; 归并排序&#xff08;Mer…

搜维尔科技:Movella推出面向自主机器和边缘人工智能应用的Xsens MTi传感器组合

Movella近日宣布针对自主机器和边缘人工智能应用&#xff0c;已增强旗下的Xsens MTi™惯性传感器模块。Xsens MTi传感器可与NVIDIA Jetson™平台轻松集成&#xff0c;用于边缘人工智能和机器人技术&#xff0c;并与NVIDIA Jetson AGX Orin™、NVIDIA Jetson Orin™ NX和NVIDIA …

基本聚集函数和case的应用

文章目录 1.基本聚集函数(1)基本聚集函数的介绍(2)使用基本聚集函数的简单例子&#xff08;1&#xff09;查询最大年龄&#xff0c;最小年龄年龄和平均年龄<1>最大年龄<2>最小年龄<3>平均年龄 (2&#xff09;配合上where语句&#xff0c;查询女士的平均年龄(…

虚拟化环境中如何实现以业务为中心的网络隔离?Everoute 推出虚拟专有云网络(VPC)功能

目前&#xff0c;不少企业都利用云计算和虚拟化技术提升 IT 系统灵活性、敏捷性和成本效益。然而&#xff0c;云环境的“多租户”特性也为业务安全带来了新的挑战&#xff0c;如何保障不同业务主体或租户之间的数据安全和网络隔离&#xff0c;成为企业关注的焦点。 作为 Smart…

(C++) 智能指针指定删除器

文章目录 ⌚前言⏲️注意 ⌚unique_ptr⏲️说明⏲️实例 ⌚shared_ptr⏲️说明⏲️实例 ⌚拓展⏲️函数类型 & 函数指针类型 ⌚END&#x1f31f;关注我 ⌚前言 自C11后&#xff0c;推出了三个智能指针。其中 unique_ptr和shared_ptr可以指定删除器。 但两者的形式却不太一…

【Canvas与艺术】红底白色压边五角星

【成图】 【代码】 <!DOCTYPE html> <html lang"utf-8"> <meta http-equiv"Content-Type" content"text/html; charsetutf-8"/> <head><title>精确压边五角星版本2</title><style type"text/css&qu…

Java IO模型深入解析:BIO、NIO与AIO

Java IO模型深入解析&#xff1a;BIO、NIO与AIO 一. 前言 在Java编程中&#xff0c;IO&#xff08;Input/Output&#xff09;操作是不可或缺的一部分&#xff0c;它涉及到文件读写、网络通信等方面。Java提供了多种类和API来支持这些操作。本文将从IO的基础知识讲起&#xff…

虚拟现实和增强现实技术系列—Expressive Talking Avatars

文章目录 1. 概述2. 背景介绍3. 数据集3.1 设计标准3.2 数据采集 4. 方法4.1 概述4.2 架构4.3 目标函数 5. 实验评测5.1 用户研究5.2 我们方法的结果5.3 比较与消融研究 1. 概述 支持远程协作者之间的交互和沟通。然而&#xff0c;明确的表达是出了名的难以创建&#xff0c;主…