Linux网络套接字之UDP网络程序

news2025/2/27 23:22:34

 (。・∀・)ノ゙嗨!你好这里是ky233的主页:这里是ky233的主页,欢迎光临~icon-default.png?t=N7T8https://blog.csdn.net/ky233?type=blog

点个关注不迷路⌯'▾'⌯

 实现一个简单的对话发消息的功能!

目录

一.新增的接口:

1.socket

2.bing

3.inet_addr

4.recvform

5.inet_ntoa

5.sendto

6.popen

二、初步代码以及结果

1.udp_server.hpp

2.udp_server.cc 

3.udp_client

4.log.hpp

5.结果 

三、爆改多线程

1.udp_client.cc

2.udp_server.cc

3.udp_server.hpp

 4.thread.hpp

5.结果


一.新增的接口:

1.socket

#include <sys/types.h>          /* See NOTES */
#include <sys/socket.h>

       int socket(int domain, int type, int protocol);
  • 参数一:叫做域,用来判断是那个类型的,AF_UNIX, AF_LOCAL(用于本地通信)AF_INET(用于ipv4的网络通信)
  • 参数二:类型,通信的种类是什么,SOCK_DGRAM(套接字的类别,数据报的方式)
  • 参数三:协议,比如用AF_INET,SOCK_DGRAM,一般直接写0
  •  创建成功会得到一个文件描述符,失败返回-1

2.bing

#include <sys/types.h>          /* See NOTES */
#include <sys/socket.h>

       int bind(int sockfd, const struct sockaddr *addr,
                socklen_t addrlen);
  • 参数一:创建的套接字
  • 参数二:填充sockaddr结构体
  • 参数三:所传入这个结构体对象的长度

3.inet_addr

#include <sys/socket.h>
       #include <netinet/in.h>
       #include <arpa/inet.h>
n_addr_t inet_addr(const char *cp);
  • 将点分十进制转换成四字节,并且将主机序列转换成网络序列
  • 参数一:传入的ip

4.recvform

#include <sys/types.h>
#include <sys/socket.h>
ssize_t recvfrom(int sockfd, void *buf, size_t len, int flags,
                        struct sockaddr *src_addr, socklen_t *addrlen);
  • 用于从套接字接收数据。该函数通常与无连接的数据报服务
  • 参数一:套接字
  • 参数二和参数三:读取数据的缓冲区,用于存放读取到的数据,len叫做最终的大小
  • 参数四:读取的方式,0为阻塞的方式读取
  • 参数五和参数六:是输出型参数,我们还要知道是谁给我们发的消息

5.inet_ntoa

#include <sys/socket.h>
       #include <netinet/in.h>
       #include <arpa/inet.h>
char *inet_ntoa(struct in_addr in);
  • 将点分四字节转换成点分十进制,并且将网络序列转换成主机序列
  • 参数一:从网络中获取到的ip

5.sendto

#include <sys/types.h>
#include <sys/socket.h>
ssize_t sendto(int sockfd, const void *buf, size_t len, int flags,
                      const struct sockaddr *dest_addr, socklen_t addrlen);
  • 参数一:套接字
  • 参数二和参数三:要发送的数据和长度
  • 参数四:默认为0,阻塞
  • 参数五:要把数据发给谁,
  • 参数六:这个缓冲区的长度是多少

6.popen

#include <stdio.h>

       FILE *popen(const char *command, const char *type);

二、初步代码以及结果

1.udp_server.hpp

#ifndef _UDP_SERVER_HPP
#define _UDP_SERVER_HPP

#include <iostream>
#include <string>
#include <sys/types.h>        
#include <sys/socket.h>
#include "log.hpp"
#include <cerrno>
#include <cstring>
#include <cstdlib>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <strings.h>
#include <cstdio>
#include <unistd.h>


#define SIZE 1024

class UDPServer
{
public:
    UDPServer(uint16_t port,std::string ip="")
    :port_(port),ip_(ip),sock_(-1)
    {}

    bool initServer()
    {
        //这里开始使用网络部分
        //1.创建套接字
        sock_=socket(AF_INET,SOCK_DGRAM,0);
        if(sock_<0)
        {
            logMessage(FATAL,"%d:%s",errno,strerror(errno));
            exit(2);
        }
        //2.bind绑定:将用户设置的ip和端口号在内核中和我们的进程强关联
        //"192.168.1.0"->点分十进制
        //以.来分隔,每个区域的取值范围为0——255,
        //正好为1字节,四个区域理论上需要四字节
        //所以我们的看的时候会由4字节显示,转换为点分十进制
        
        //初始化结构体完成
        struct sockaddr_in local;
        bzero(&local,sizeof(local));
        //填充结构体
        local.sin_family=AF_INET;
        //服务器的IP和端口号,未来也是要发送给对方主机才能互相通信的
        //所以要先把数据发送到网络,要用htons
        local.sin_port=htons(port_);
        //同上要先把点分十进制转换成四字节ip
        //还需要要把四字节主机序列,转换成网络序列
        //所以用inet_addr一次性转成网络四字节序列
        //local.sin_addr.s_addr=inet_addr(ip_.c_str());
        //一般情况下,只要是这个端口的,那么我们就直接全部都接受,而不是指定端口
        local.sin_addr.s_addr = ip_.empty() ? INADDR_ANY : inet_addr(ip_.c_str());
        //这里需要做强转,因为bind需要的是sockaddr类型而不是sockaddr_in类型
        if(bind(sock_,(struct sockaddr*)&local,sizeof(local))<0)
        {
            logMessage(FATAL,"%d:%s",errno,strerror(errno));
            exit(2);
        }
        //至此初始化服务器写完,首先创建套接字,接着用bind来绑定ip和端口号写进内核中
        logMessage(NORMAL, "init udp server done ... %s", strerror(errno));
        return true;
    }

    void strat()
    {
        //作为一款网络服务器,是永远不退出的!
        // 服务器启动-> 进程 -> 常驻进程 -> 永远在内存中存在,除非挂了!
        //echo服务器,主机给我们发送消息,我们原封不动的返回
        char buffer[SIZE];
        for( ; ;)
        {
            //1.读取数据
            //注意peer,纯输出型参数
            struct sockaddr_in peer;
            bzero(&peer,sizeof(peer));
            //输入:peer 缓冲区大小
            //输入:实际读到的peer大小
            //所以len的大小要为实际的peer大小
            socklen_t len = sizeof(peer);
            //这样就把数据读取到buffer里了
            ssize_t s= recvfrom(sock_,&buffer,sizeof(buffer)-1,0,(struct sockaddr*)&peer,&len);
            if(s>0)
            {
                buffer[s]=0;//我们目前数据当作字符串
                //1.输出发送的数据信息
                //2.是谁?提取!
                uint16_t cil_port=ntohs(peer.sin_port);//从网络中来的,所以要进行转成主机端口号
                std::string cli_p=inet_ntoa(peer.sin_addr);//将点分四字节转换成点分十进制,并且将网络序列转换成主机序列
                printf("[%s:%d]# %s\n",cli_p.c_str(),cil_port,buffer);
            }
            //2.分析和处理数据

            //3.写回数据
            sendto(sock_,buffer,sizeof(buffer),0,(struct sockaddr*)&peer,len);
        }
    }

    ~UDPServer()
    {
        if(sock_>=0)
        {
            close(sock_);
        }
    }

private:
uint16_t port_;
std::string ip_;
int sock_;
};

#endif

2.udp_server.cc 

#include "udp_server.hpp"
#include <memory>
#include <cstdlib>

static void usage(std::string proc)
{
    std::cout << "\nUsage: " << proc << " port\n" << std::endl;
}

int main(int argc,char *argv[])
{
    //检查传入ip和端口号是否错误
    if(argc!=2)
    {
        usage(argv[0]);
        exit(1);

    }
    //初始化服务器,利用unique_ptr
    uint16_t port=atoi(argv[1]);
    //std::string ip=argv[1];
    std::unique_ptr<UDPServer> svr(new UDPServer(port));
    svr->initServer();
    svr->strat();
    return 0;
}

3.udp_client

#include <iostream>
#include <string>
#include <cstring>
#include <unistd.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <netinet/in.h>
#include <memory>
#include "thread.hpp"

static void usage(std::string proc)
{
    std::cout << "\nUsage: " << proc << " serverIp serverPort\n"
              << std::endl;
}

int main(int argc, char *argv[])
{
    if (argc != 3)
    {
        usage(argv[0]);
        exit(1);
    }
    int sock = socket(AF_INET, SOCK_DGRAM, 0);
    if (sock < 0)
    {
        
        std::cerr << "socket error" << std::endl;
        exit(2);
    }
    //client要不要bind?要bind,但是client不会显示的bind,程序员不会自己bind
    //client是一个客户端,是普通人下载并使用的
    //如果需要显示的bind,也就要求了客户端也bind了一个固定的端口
    //万一其他的客户端提前真用了端口号了呢,这时这个客户端就无法启动了
    //所以不用显示的bind指定端口号,直接让OS自动随机选择
    std::string message;
    //给谁发的信息如下
    struct sockaddr_in server;
    memset(&server,0,sizeof(server));
    server.sin_family=AF_INET;
    server.sin_port=htons(atoi(argv[2]));
    server.sin_addr.s_addr=inet_addr(argv[1]);

    char buffer[1024];
    while(1)
    {
        std::cout<<"请输入你的信息# ";
        std::getline(std::cin,message);
        if(message=="quit")
        {
            break;
        }
        //当client首次发送消息给服务器的时候,OS会自动给client bind它的ip和port
        sendto(sock,message.c_str(),message.size(),0,(struct sockaddr*)&server,sizeof(server));
        //至此客户端和服务器已经建成

        //这里需要定义两个占位的
        struct sockaddr_in temp;
        socklen_t len = sizeof(temp);
        ssize_t s= recvfrom(sock,&buffer,sizeof(buffer)-1,0,(struct sockaddr*)&temp,&len);
        if(s>0)
        {
            buffer[s]=0;
            std::cout<<"server echo# "<<buffer<<std::endl;
        }

    }
    close(sock);
    return 0;
}

4.log.hpp

#pragma once

#include <iostream>
#include <cstdio>
#include <cstdarg>
#include <ctime>
#include <string>

// 日志是有日志级别的
#define DEBUG   0
#define NORMAL  1
#define WARNING 2
#define ERROR   3
#define FATAL   4

const char *gLevelMap[] = {
    "DEBUG",
    "NORMAL",
    "WARNING",
    "ERROR",
    "FATAL"
};

#define LOGFILE "./threadpool.log"

// 完整的日志功能,至少: 日志等级 时间 支持用户自定义(日志内容, 文件行,文件名)
void logMessage(int level, const char *format, ...)
{
#ifndef DEBUG_SHOW
    if(level== DEBUG) return;
#endif
    // va_list ap;
    // va_start(ap, format);
    // while()
    // int x = va_arg(ap, int);
    // va_end(ap); //ap=nullptr
    char stdBuffer[1024]; //标准部分
    time_t timestamp = time(nullptr);
    // struct tm *localtime = localtime(&timestamp);
    snprintf(stdBuffer, sizeof stdBuffer, "[%s] [%ld] ", gLevelMap[level], timestamp);

    char logBuffer[1024]; //自定义部分
    va_list args;
    va_start(args, format);
    // vprintf(format, args);
    vsnprintf(logBuffer, sizeof logBuffer, format, args);
    va_end(args);

    //FILE *fp = fopen(LOGFILE, "a");
    printf("%s%s\n", stdBuffer, logBuffer);
    //fprintf(fp, "%s%s\n", stdBuffer, logBuffer);
    //fclose(fp);
}

5.结果 

这个叫做本地环回:client和server发送数据只在本地协议栈中进行数据流动,不会把我们的数据发送到网络中,通常用于本地网络服务器的测试

 ——————————————————————————————————————————

其中客户端时OS随机绑定的,全0IP是因为只要是这个端口的我都要,不要具体IP的

三、爆改多线程

我们要把这个代码改成一个类似QQ群一样的模式,所有人发的消息都可以看到

1.udp_client.cc

#include <iostream>
#include <string>
#include <cstring>
#include <unistd.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <netinet/in.h>
#include <memory>
#include "thread.hpp"

uint16_t serverport=0;
std::string serverip;

//发现:无论是多线程读还是写,用的sock都是一个,sock代表就是文件,UDP是全双工的-> 可以同时进行收发而不受干扰


static void usage(std::string proc)
{
    std::cout << "\nUsage: " << proc << " serverIp serverPort\n"
              << std::endl;
}

//发送逻辑的回调
static void *udpSend(void *args)
{
    //根据线程的封装,先转换成data*的然后获取里面的args_,然后在转会int*
    //获得一个指针指向args里面的sock,然后解引用
    int sock=*(int*)((ThreadData*)args)->args_;
    std::string name = ((ThreadData*)args)->name_;
    //下面逻辑与之前一样,构建发送的字符串,套接字信息
    std::string message;
    //给谁发的信息如下
    struct sockaddr_in server;
    memset(&server,0,sizeof(server));
    server.sin_family=AF_INET;
    server.sin_port=htons(serverport);
    server.sin_addr.s_addr=inet_addr(serverip.c_str());

    //开始不间断的发送
    while(1)
    {
        std::cerr<<"请输入你的信息# ";
        std::getline(std::cin,message);
        if(message=="quit")
        {
            break;
        }
        //当client首次发送消息给服务器的时候,OS会自动给client bind它的ip和port
        sendto(sock,message.c_str(),message.size(),0,(struct sockaddr*)&server,sizeof(server));
        //至此客户端和服务器已经建成
    }
    return nullptr;
}

//接受逻辑的回调
static void *udpRecv(void *args)
{
    int sock=*(int*)((ThreadData*)args)->args_;
    std::string name = ((ThreadData*)args)->name_;

    //需要不断地去读,要去指定的套接字中读取
    while(1)
    {
        char buffer[1024];
        struct sockaddr_in temp;
        socklen_t len = sizeof(temp);
        ssize_t s= recvfrom(sock,buffer,sizeof(buffer)-1,0,(struct sockaddr*)&temp,&len);
        if(s>0)
        {
            buffer[s]=0;
            std::cout<<buffer<<std::endl;
        }

    }
}

int main(int argc, char *argv[])
{
    if (argc != 3)
    {
        usage(argv[0]);
        exit(1);
    }
    int sock = socket(AF_INET, SOCK_DGRAM, 0);
    if (sock < 0)
    {
        
        std::cerr << "socket error" << std::endl;
        exit(2);
    }

    serverport=atoi(argv[2]);
    serverip=argv[1];
    //client要不要bind?要bind,但是client不会显示的bind,程序员不会自己bind
    //client是一个客户端,是普通人下载并使用的
    //如果需要显示的bind,也就要求了客户端也bind了一个固定的端口
    //万一其他的客户端提前真用了端口号了呢,这时这个客户端就无法启动了
    //所以不用显示的bind指定端口号,直接让OS自动随机选择

    //爆改多线程,一个线程发送数据到各个主机,一个线程接受各个数据。
    std::unique_ptr<Thread> sender(new Thread(1,udpSend,(void*)&sock));
    std::unique_ptr<Thread> recver(new Thread(2,udpRecv,(void*)&sock));

    sender->start();
    recver->start();

    sender->join();
    recver->join();
    close(sock);



    // std::string message;
    // //给谁发的信息如下
    // struct sockaddr_in server;
    // memset(&server,0,sizeof(server));
    // server.sin_family=AF_INET;
    // server.sin_port=htons(atoi(argv[2]));
    // server.sin_addr.s_addr=inet_addr(argv[1]);

    // char buffer[1024];
    // while(1)
    // {
    //     std::cout<<"请输入你的信息# ";
    //     std::getline(std::cin,message);
    //     if(message=="quit")
    //     {
    //         break;
    //     }
    //     //当client首次发送消息给服务器的时候,OS会自动给client bind它的ip和port
    //     sendto(sock,message.c_str(),message.size(),0,(struct sockaddr*)&server,sizeof(server));
    //     //至此客户端和服务器已经建成

    //     //这里需要定义两个占位的
    //     struct sockaddr_in temp;
    //     socklen_t len = sizeof(temp);
    //     ssize_t s= recvfrom(sock,&buffer,sizeof(buffer)-1,0,(struct sockaddr*)&temp,&len);
    //     if(s>0)
    //     {
    //         buffer[s]=0;
    //         std::cout<<"server echo# "<<buffer<<std::endl;
    //     }

    // }
    // close(sock);
    return 0;
}

2.udp_server.cc

#include "udp_server.hpp"
#include <memory>
#include <cstdlib>

static void usage(std::string proc)
{
    std::cout << "\nUsage: " << proc << " port\n" << std::endl;
}

int main(int argc,char *argv[])
{
    //检查传入ip和端口号是否错误
    if(argc!=2)
    {
        usage(argv[0]);
        exit(1);

    }
    //初始化服务器,利用unique_ptr
    uint16_t port=atoi(argv[1]);
    //std::string ip=argv[1];
    std::unique_ptr<UDPServer> svr(new UDPServer(port));
    svr->initServer();
    svr->strat();
    return 0;
}

3.udp_server.hpp

#ifndef _UDP_SERVER_HPP
#define _UDP_SERVER_HPP

#include <iostream>
#include <string>
#include <sys/types.h>
#include <sys/socket.h>
#include "log.hpp"
#include <cerrno>
#include <cstring>
#include <cstdlib>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <strings.h>
#include <cstdio>
#include <unistd.h>
#include <unordered_map>

#define SIZE 1024

class UDPServer
{
public:
    UDPServer(uint16_t port, std::string ip = "")
        : port_(port), ip_(ip), sock_(-1)
    {
    }

    bool initServer()
    {
        // 这里开始使用网络部分
        // 1.创建套接字
        sock_ = socket(AF_INET, SOCK_DGRAM, 0);
        if (sock_ < 0)
        {
            logMessage(FATAL, "%d:%s", errno, strerror(errno));
            exit(2);
        }
        // 2.bind绑定:将用户设置的ip和端口号在内核中和我们的进程强关联
        //"192.168.1.0"->点分十进制
        // 以.来分隔,每个区域的取值范围为0——255,
        // 正好为1字节,四个区域理论上需要四字节
        // 所以我们的看的时候会由4字节显示,转换为点分十进制

        // 初始化结构体完成
        struct sockaddr_in local;
        bzero(&local, sizeof(local));
        // 填充结构体
        local.sin_family = AF_INET;
        // 服务器的IP和端口号,未来也是要发送给对方主机才能互相通信的
        // 所以要先把数据发送到网络,要用htons
        local.sin_port = htons(port_);
        // 同上要先把点分十进制转换成四字节ip
        // 还需要要把四字节主机序列,转换成网络序列
        // 所以用inet_addr一次性转成网络四字节序列
        // local.sin_addr.s_addr=inet_addr(ip_.c_str());
        // 一般情况下,只要是这个端口的,那么我们就直接全部都接受,而不是指定端口
        local.sin_addr.s_addr = ip_.empty() ? INADDR_ANY : inet_addr(ip_.c_str());
        // 这里需要做强转,因为bind需要的是sockaddr类型而不是sockaddr_in类型
        if (bind(sock_, (struct sockaddr *)&local, sizeof(local)) < 0)
        {
            logMessage(FATAL, "%d:%s", errno, strerror(errno));
            exit(2);
        }
        // 至此初始化服务器写完,首先创建套接字,接着用bind来绑定ip和端口号写进内核中
        logMessage(NORMAL, "init udp server done ... %s", strerror(errno));
        return true;
    }

    void strat()
    {
        // 作为一款网络服务器,是永远不退出的!
        //  服务器启动-> 进程 -> 常驻进程 -> 永远在内存中存在,除非挂了!
        // echo服务器,主机给我们发送消息,我们原封不动的返回
        char buffer[SIZE];
        for (;;)
        {
            // 1.读取数据
            // 注意peer,纯输出型参数
            struct sockaddr_in peer;
            bzero(&peer, sizeof(peer));
            // 输入:peer 缓冲区大小
            // 输入:实际读到的peer大小
            // 所以len的大小要为实际的peer大小
            socklen_t len = sizeof(peer);
            // 这样就把数据读取到buffer里了
            ssize_t s = recvfrom(sock_, &buffer, sizeof(buffer) - 1, 0, (struct sockaddr *)&peer, &len);
            char result[256];
            char key[64];
            std::string cmd_echo;
            if (s > 0)
            {
                buffer[s] = 0; // 我们目前数据当作字符串
                // 1.输出发送的数据信息
                // 2.是谁?提取!
                // if(strcasestr(buffer,"rm")!=nullptr||strcasestr(buffer,"rmdir")!=nullptr)
                // {
                //     std::string err_message="坏人";
                //     std::cout<<err_message<<buffer<<std::endl;
                //     sendto(sock_, err_message.c_str(), err_message.size(), 0, (struct sockaddr *)&peer, len);

                //     continue;
                // }
                // FILE *fp = popen(buffer, "r");
                // if (nullptr == fp)
                // {
                //     logMessage(ERROR, "%d:%s", errno, strerror(errno));
                //     continue;
                // }

                // while (fgets(result, sizeof(result), fp) != nullptr)
                // {
                //     cmd_echo += result;
                // }
                // fclose(fp);
                uint16_t cil_port = ntohs(peer.sin_port);     // 从网络中来的,所以要进行转成主机端口号
                std::string cil_ip = inet_ntoa(peer.sin_addr); // 将点分四字节转换成点分十进制,并且将网络序列转换成主机序列
                // printf("[%s:%d]# %s\n",cli_p.c_str(),cil_port,buffer);
                snprintf(key, sizeof(key), "%s-%d", cil_ip.c_str(), cil_port);
                logMessage(NORMAL,"key: %s",key);
                auto it = users_.find(key);
                if(it==users_.end())
                {
                    logMessage(NORMAL,"add new user:%s",key);
                    users_.insert({key,peer});
                }

            }
            // 2.分析和处理数据

            // 3.写回数据
            // sendto(sock_,buffer,sizeof(buffer),0,(struct sockaddr*)&peer,len);
            //sendto(sock_, cmd_echo.c_str(), cmd_echo.size(), 0, (struct sockaddr *)&peer, len);
            for(auto &iter:users_)
            {
                std::string sendMessage=key;
                sendMessage+="# ";
                sendMessage+=buffer;
                logMessage(NORMAL,"push message to %s",iter.first.c_str());
                sendto(sock_, sendMessage.c_str(), sendMessage.size(), 0, (struct sockaddr *)&(iter.second), sizeof(iter.second));

            }
        }
    }

    ~UDPServer()
    {
        if (sock_ >= 0)
        {
            close(sock_);
        }
    }

private:
    uint16_t port_;
    std::string ip_;
    int sock_;
    std::unordered_map<std::string, struct sockaddr_in> users_;
};

#endif

 4.thread.hpp

#pragma once
#include <iostream>
#include <string>
#include <functional>
#include <cstdio>

using namespace std;

typedef void*(*fun_t)(void*);

class ThreadData
{
public:
    void* args_;
    string name_;
};

class Thread
{
public:
    Thread(int num,fun_t callback,void* args):func_(callback)
    {
        char namebuffer[64];
        snprintf(namebuffer,sizeof(namebuffer),"Thread-%d",num);
        name_=namebuffer;

        tdata_.args_=args;
        tdata_.name_=name_;
    }

    void start()
    {
        pthread_create(&tid_,nullptr,func_,&tdata_.args_);
    }

    void join()
    {
        pthread_join(tid_,nullptr);
    }
    
    string name()
    {
        return name_;
    }
    ~Thread()
    {

    }

private:
    string name_;
    fun_t func_;
    ThreadData tdata_;
    pthread_t tid_;
};

5.结果

利用管道可以更好的观看

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

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

相关文章

力扣---腐烂的橘子

题目&#xff1a; bfs思路&#xff1a; 感觉bfs还是很容易想到的&#xff0c;首先定义一个双端队列&#xff08;队列也是可以的~&#xff09;&#xff0c;如果值为2&#xff0c;则入队列&#xff0c;我这里将队列中的元素定义为pair<int,int>。第一个int记录在数组中的位…

毅速3D打印随形透气钢:模具困气排气革新之选

在注塑生产过程中&#xff0c;模具内的气体若无法有效排出&#xff0c;往往会引发困气现象&#xff0c;导致产品表面出现气泡、烧焦等瑕疵。这些瑕疵不仅影响产品的美观度&#xff0c;更可能对其性能造成严重影响&#xff0c;甚至导致产品报废&#xff0c;从而增加生产成本。 传…

【C语言】linux内核tcp_write_xmit和tcp_write_queue_purge

tcp_write_xmit 一、讲解 这个函数 tcp_write_xmit 是Linux内核TCP协议栈中的一部分&#xff0c;其基本作用是发送数据包到网络。这个函数会根据不同情况推进发送队列的头部&#xff0c;确保只要远程窗口有空间&#xff0c;就可以发送数据。 下面是对该函数的一些主要逻辑的中…

C语言--函数指针变量和函数指针数组的区别(详解)

函数指针变量 函数指针变量的作用 函数指针变量是指向函数的指针&#xff0c;它可以用来存储函数的地址&#xff0c;并且可以通过该指针调用相应的函数。函数指针变量的作用主要有以下几个方面&#xff1a; 回调函数&#xff1a;函数指针变量可以作为参数传递给其他函数&…

基于pytorch的视觉变换器-Vision Transformer(ViT)的介绍与应用

近年来&#xff0c;计算机视觉领域因变换器模型的出现而发生了革命性变化。最初为自然语言处理任务设计的变换器&#xff0c;在捕捉视觉数据的空间依赖性方面也显示出了惊人的能力。视觉变换器&#xff08;Vision Transformer&#xff0c;简称ViT&#xff09;就是这种变革的一个…

链表基础知识详解

链表基础知识详解 一、链表是什么&#xff1f;1.链表的定义2.链表的组成3.链表的优缺点4.链表的特点 二、链表的基本操作1.链表的建立2.链表的删除3.链表的查找4.链表函数 一、链表是什么&#xff1f; 1.链表的定义 链表是一种物理存储单元上非连续、非顺序的存储结构&#xf…

人工智能|机器学习——K-means系列聚类算法k-means/ k-modes/ k-prototypes/ ......(划分聚类)

1.k-means聚类 1.1.算法简介 K-Means算法又称K均值算法&#xff0c;属于聚类&#xff08;clustering&#xff09;算法的一种&#xff0c;是应用最广泛的聚类算法之一。所谓聚类&#xff0c;即根据相似性原则&#xff0c;将具有较高相似度的数据对象划分至同一类簇&#xff0c;…

【Docker】golang使用DockerFile正确食用指南

【Docker】golang使用DockerFile正确食用指南 大家好 我是寸铁&#x1f44a; 总结了一篇golang使用DockerFile正确食用指南✨ 喜欢的小伙伴可以点点关注 &#x1f49d; 问题背景 今天寸铁想让编写好的go程序在docker上面跑&#xff0c;要想实现这样的效果&#xff0c;就需要用…

流放者柯南服务器端搭建!

这是一个开放世界生存游戏。在这个游戏里&#xff0c;你可以建造自己的城堡&#xff0c;探索神秘的遗迹&#xff0c;与野兽和敌人战斗&#xff0c;甚至成为一个神。 首先推荐服务器配置&#xff1a; 4核16G 月50 季度200 8核32G 月120 季度350 含安装搭建服务&#xff01…

《剑指 Offer》专项突破版 - 面试题 76 : 数组中第 k 大的数字(C++ 实现)

目录 详解快速排序 面试题 76 : 数组中第 k 大的数字 详解快速排序 快速排序是一种非常高效的算法&#xff0c;从其名字可以看出这种排序算法最大的特点是快。当表现良好时&#xff0c;快速排序的速度比其他主要对手&#xff08;如归并排序&#xff09;快 2 ~ 3 倍。 快速排…

WordPress高端后台美化WP Adminify Pro优化版

后台UI美化WP Adminify Pro修改自定义插件&#xff0c;适合建站公司和个人使用&#xff0c;非常高大上&#xff0c;下载地址&#xff1a;WP Adminify Pro优化版 修复记录&#xff1a; 1、修复已知BUG 2、修复手机版兼容问题 3、修复打开速度&#xff0c;原版打开速度太慢 4…

Git的基本操作(安装Git,创建本地仓库,配置Git,添加、修改、回退、撤销修改、删除文件)

文章目录 一、Git安装二、创建本地仓库三、配置Git四、认识工作区、暂存区、本地库五、添加文件六、修改文件七、版本回退八、撤销修改1.对于⼯作区的代码&#xff0c;还没有add2.已经add&#xff0c;但没有commit3.已经add&#xff0c;并且已经commit 九、删除⽂件 一、Git安装…

解释区块链技术的应用场景、优势及经典案例

目录 1.区块链应用场景 2.区块链优势 3.区块链经典案例 区块链技术是一种分布式账本技术&#xff0c;它通过加密和安全验证机制&#xff0c;允许网络中的多个参与者之间进行可信的、不可篡改的交易和数据的记录与传输。区块链技术的应用场景广泛&#xff0c;其优势也十分显著…

R语言复现:中国Charls数据库一篇现况调查论文的缺失数据填补方法

编者 在临床研究中&#xff0c;数据缺失是不可避免的&#xff0c;甚至没有缺失&#xff0c;数据的真实性都会受到质疑。 那我们该如何应对缺失的数据&#xff1f;放着不管&#xff1f;还是重新开始?不妨试着对缺失值进行填补&#xff0c;简单又高效。毕竟对于统计师来说&#…

【AcWing】蓝桥杯集训每日一题Day1|二分|差分|503.借教室(C++)

503. 借教室 503. 借教室 - AcWing题库难度&#xff1a;简单时/空限制&#xff1a;1s / 128MB总通过数&#xff1a;8052总尝试数&#xff1a;26311来源&#xff1a;NOIP2012提高组算法标签二分差分 题目内容 在大学期间&#xff0c;经常需要租借教室。 大到院系举办活动&…

Yolov8模型用torch_pruning剪枝

目录 &#x1f680;&#x1f680;&#x1f680;订阅专栏&#xff0c;更新及时查看不迷路&#x1f680;&#x1f680;&#x1f680; 原理 遍历所有分组 高级剪枝器 &#x1f680;&#x1f680;&#x1f680;订阅专栏&#xff0c;更新及时查看不迷路&#x1f680;&#x1f680…

TYPE C模拟耳机POP音产生缘由

关于耳机插拔的POP音问题&#xff0c;小白在之前的文章中讲述过关于3.5mm耳机的POP音产生原因。其实这类插拔问题的POP音不仅仅存在于3.5mm耳机&#xff0c;就连现在主流的Type C模拟耳机的插拔也存在此问题&#xff0c;今天小白就来讲一讲这类耳机产生POP音的缘由。 耳机左右…

计算机视觉——P2PNet基于点估计的人群计数原理与C++模型推理

简介 人群计数是计算机视觉领域的一个核心任务&#xff0c;旨在估算静止图像或视频帧中的行人数量。在过去几十年中&#xff0c;研究人员在这个领域投入了大量的精力&#xff0c;并在提高现有主流基准数据集性能方面取得了显著进展。然而&#xff0c;训练卷积神经网络需要大规…

书与我

和书深深结缘&#xff0c;始于需求&#xff0c;得益于通勤时间长。 读什么书 一直没有停止过编码&#xff0c;工作性质也要求我必须了解很多的新技术&#xff0c;从踏上工作岗位后&#xff0c;就需要不停的看书。从《JAVA编程思想》、《java与模式》、《TCP/IP详解》、《深入…

131.分割回文串

// 定义一个名为Solution的类 class Solution {// 声明一个成员变量&#xff0c;用于存储所有满足条件的字符串子序列划分结果List<List<String>> lists new ArrayList<>(); // 声明一个成员变量&#xff0c;使用LinkedList实现的双端队列&#xff0c;用于临…