Linux系统应用编程(五)Linux网络编程(上篇)

news2024/11/22 19:15:37

本篇主要内容:

    • Linux系统应用编程(五)Linux网络编程(上篇)
    • 一、网络基础
      • 1.两个网络模型和常见协议
        • (1)OSI七层模型(物数网传会表应)
        • (2)TCP/IP四层模型(网网传应)
        • (3)常见网络协议所属层
      • 2.字节序
        • (1)两种字节序
        • (2)字节序转换函数
      • 3.TCP通信时序(三次握手、四次挥手)
        • (1)什么是"三次握手"和"四次挥手"
        • (2)"三次握手"和"四次挥手"的过程
        • (3)为什么断开连接需要"四次挥手"
    • 二、Socket网络编程
      • 1.网络地址结构体
      • 2.Socket编程API
        • (1)创建套接字socket( )
        • (2)绑定地址bind( )
        • (3)设置监听listen( )
        • (4)等待连接accept( )
        • (5)发起连接connect( )
        • (6)设置地址复用setsockopt( )
    • 三、案例程序
      • 1.简易"模拟Linux终端"v1.0
      • 2.TCP粘包问题
        • (1)粘包问题引入
        • (2)TCP粘包产生原因
        • (3)解决粘包问题
        • (4)自定义协议
      • 3.简易"模拟Linux终端"v2.0

Linux系统应用编程(五)Linux网络编程(上篇)

一、网络基础

1.两个网络模型和常见协议

(1)OSI七层模型(物数网传会表应)

  • 物理层、数据链路层、网络层、传输层、会话层、表示层、应用层(自下到上)

(2)TCP/IP四层模型(网网传应)

  • 网络接口层(链路层)、网络层、传输层、应用层

(3)常见网络协议所属层

在这里插入图片描述

2.字节序

(1)两种字节序

在这里插入图片描述

(2)字节序转换函数

在这里插入图片描述

3.TCP通信时序(三次握手、四次挥手)

以下均为简述,仅针对面试时能够有东西掰扯

(1)什么是"三次握手"和"四次挥手"

  • "三次握手"意思是TCP客户端和服务器建立连接需要3次通信的过程;
  • "四次挥手"意思是TCP客户端和服务器断开连接需要4次通信的过程。

(2)"三次握手"和"四次挥手"的过程

  • “三次握手”:客户端主动向服务器发起连接请求,也就是发送建立连接的标志位SYN,服务器收到该请求同意后回复一个SYN和ACK(应答标志位),表示服务器收到客户端的连接请求,客户端收到服务器SYN+ACK后,再向服务器发送ACK应答标志位,等到服务器收到后就完成了三次握手建立连接。

  • “四次挥手”:一般由客户端主动断开,发送FIN标志位给服务器后,客户端处于半关闭状态(也就是只能接收服务器数据,而不能发送数据);服务器接收到FIN后回复客户端ACK应答;接着服务器也会发送FIN给客户端,同时服务器也进入半关闭状态,直到客户端回复ACK给到服务器,连接断开。

    实际上,套接字在内核中实现了读、写两个缓冲区,半关闭就是关闭了写缓冲区

  • 【补充】上面说到客户端处于半关闭,为什么可以在第四挥手时给服务器回复ACK?

    半关闭只是关闭socket中的写缓冲区,此时客户端和服务器的socket连接并没有关闭,因此,在半关闭状态下,客户端仍然可以通过已经建立好的TCP连接给服务器回复ACK确认包来完成四次挥手的过程。

(3)为什么断开连接需要"四次挥手"

  • 导致TCP连接关闭需要四次挥手的直接原因:半关闭
  • 为什么:为了确保双方在关闭连接之前都能够完成必要的操作,并尽可能地减少因网络不稳定性造成的影响,以保证数据的可靠性。

二、Socket网络编程

1.网络地址结构体

在这里插入图片描述

2.Socket编程API

(1)创建套接字socket( )

在这里插入图片描述

(2)绑定地址bind( )

在这里插入图片描述

(3)设置监听listen( )

在这里插入图片描述

(4)等待连接accept( )

在这里插入图片描述

(5)发起连接connect( )

在这里插入图片描述

(6)设置地址复用setsockopt( )

在这里插入图片描述

三、案例程序

本案例参考于抖音up@小飞有点东西《python全栈高级篇》,up的python视频很nb;以下为笔者学习后用C语言描述的版本

1.简易"模拟Linux终端"v1.0

【开发环境】 ubuntu22.04、CLion

【核心技术】 TCP网络编程、服务器多进程/多线程并发、解决粘包问题

【案例描述】 client接入server后,通过命令行输入Linux命令,由server执行后的结果发送给client。

【v1.0代码】 多进程实现服务器并发,父进程回收子进程避免僵尸进程,子进程和客户端通信。

至此,程序还有BUG未解决——粘包问题

#include "temp.h"   //many head files in it

/* 服务器socket结构体 */
struct ServerSocket{
    int sockfd;       //服务器socket文件描述符
    void (* socketBind)(int ,char *,int);   //给sockfd绑定地址函数
    void (* serverListen)(int , int);       //监听sockfd函数
    struct ClientSocket (* serverAccept)(int);  //建立连接函数
};

/* 客户端socket结构体 */
struct ClientSocket{
    int cfd;    //建立连接的socket文件描述符
    char ip[32];    //客户端IP
    int port;   //客户端Port
};

/* 服务器socket绑定地址信息函数实现 */
void socketBind(int sockfd,char *ip,int port){
    int retn;
    /* 初始化地址结构体sockaddr_in */
    struct sockaddr_in serAddr = {
            .sin_port = htons(port),
            .sin_family = AF_INET
    };
    inet_pton(AF_INET,ip,&serAddr.sin_addr.s_addr);
    /* 调用bind()绑定地址 */
    retn = bind(sockfd,(struct sockaddr *)&serAddr,sizeof(serAddr));
    if(retn == -1){
        perror("bind");
        exit(-1);
    }
    printf("<Server> bind address: %s:%d\n",ip,port);
}

/* 服务器socket监听函数实现 */
void serverListen(int sockfd,int n){
    int retn;
    retn = listen(sockfd,n);
    if(retn == -1){
        perror("listen");
        exit(-1);
    }
    printf("<Server> listening...\n");
}

/* 服务器建立连接函数实现,返回值为struct ClientSocket结构体 *
 * (包括建立连接的socket文件描述符、客户端信息) */
struct ClientSocket serverAccept(int sockfd){
    struct sockaddr_in clientAddr;
    socklen_t addrLen = sizeof(clientAddr);
    struct ClientSocket c_socket;
    c_socket.cfd = accept(sockfd,(struct sockaddr *)&clientAddr,&addrLen);
    if(c_socket.cfd == -1){
        perror("accept");
        exit(-1);
    }else{
        c_socket.port = ntohs(clientAddr.sin_port);
        inet_ntop(AF_INET,&clientAddr.sin_addr.s_addr,c_socket.ip,sizeof(clientAddr));
        return c_socket;
    }
}

/* 信号处理函数:回收子进程 */
void waitChild(int signum){
    wait(NULL);
}

int main(){
    /* 初始化服务器socket */
    struct ServerSocket ss = {
            .serverAccept = serverAccept,
            .socketBind = socketBind,
            .serverListen = serverListen
    };
    /* 设置端口复用 */
    int optval = 1;
    setsockopt(ss.sockfd,SOL_SOCKET,SO_REUSEPORT,&optval,sizeof(optval));

    ss.sockfd = socket(AF_INET,SOCK_STREAM,0);
    ss.socketBind(ss.sockfd,"192.168.35.128",8880);
    ss.serverListen(ss.sockfd,128);
    
    /* 多进程实现服务器并发 */
    struct ClientSocket cs; //客户端socket
    pid_t pid = 1;
    int nread;
    while(1){   //循环等待客户端接入
        cs = ss.serverAccept(ss.sockfd);
        printf("<Server> client connected.(%s:%d)\n",cs.ip,cs.port);
        pid = fork();   //创建父子进程
        if(pid > 0){    //父进程
            close(cs.cfd);  //关闭通信的套接字
            signal(SIGCHLD,waitChild);  //注册信号
            continue;
        }else if(pid == 0){     //子进程
            close(ss.sockfd);  //关闭建立连接的socket
            while(1){
                char *writeBuff = (char *) malloc(2048);    //写buff
                char *readBuff = (char *) malloc(128);      //读buff
                FILE *buffFile = NULL;          //文件流
                while(1) {
                    nread = read(cs.cfd, readBuff, 128);   //读取客户端发过来的命令
                    /* 对read判空,防止客户端退出后一直收空数据的死循环 */
                    if (nread == 0) {
                        printf("<server> client disconnected (%s:%d)\n",cs.ip,cs.port);
                        break;
                    }
                    /* 执行客户端发过来的命令 */
                    buffFile = popen(readBuff, "r");
                    fread(writeBuff, 2048, 1, buffFile);    //命令执行成功结果读取到writeBuff
                    if (strlen(writeBuff) == 0) {
                        write(cs.cfd, "\n", 1);
                    }else{
                        write(cs.cfd, writeBuff, strlen(writeBuff));   //结果写回给客户端
                    }
                    /* 清空缓存数据,关闭流 */
                    memset(writeBuff, '\0', strlen(writeBuff));
                    memset(readBuff, '\0', strlen(readBuff));
                    pclose(buffFile);
                }
                return 0;
            }
        }else{
            perror("fork");
            exit(-1);
        }
    }
}

在这里插入图片描述

在这里插入图片描述

2.TCP粘包问题

(1)粘包问题引入

  • v1.0的服务器代码,只执行了ls、dir执行结果较短的命令,看似没有BUG,但是如果执行的是像ps -aux命令结果较长的,就可以发现,由于返回的结果较长,客户端一次读取并没有读取完(或者读取太快、缓存太小),当下一条命令执行后,结果就会和上一条命令没有读取完的内容连在一起。如图:

在这里插入图片描述

  • 针对客户端读取数据太快,或客户端设置的缓存太小,虽然我们在代码中,用延时避免读取数据太快、设置较大的缓存区可以一定程度避免粘包问题,但是这种解决方法并不好,延时难免影响用户体验,过大的缓存区也不切实际。所以,需要从其他角度解决TCP的粘包问题。

(2)TCP粘包产生原因

  • TCP协议基于字节流传输数据,并不是基于消息,数据类似水流传输着,数据之间难以区分,所以不可避免出现将多个独立的数据包粘成一个数据包的情况;
  • TCP为了避免网络拥塞,减少网络负载而设计的底层优化算法Nagle算法,通过将多个小数据包合并成一个大数据包进行发送,以减少网络流量和传输延迟。当有大量小数据包需要发送时,Nagle算法会将这些数据包先缓存起来,并在缓存区中尝试组装成一个更大的数据包再进行发送。所以如果接收方不能及时地处理接收到的数据包,或者发送方的缓存区未被填满,那么就会导致TCP粘包问题的产生。

(3)解决粘包问题

  • 固定数据包的长度:每次发送读取都固定大小
  • 在数据头部加入数据的总长度:接收方先读取消息头中的长度信息,再根据长度信息读取对应长度的数据(实际上也就是<自定义协议>)
  • 特殊分割符:使用特殊的分割符(如\n或者\r\n)来分割每条数据

(4)自定义协议

  • 自定义协议通常包含两部分内容:

    1. 消息头:用于描述数据包的基本信息,如数据包类型、数据包长度等。

      例如:<文件传输>头部可以包括文件类型、文件的md5值、文件的大小等

    2. 消息体:用于存储具体的数据,如文本、图片、音频等。

  • 设计自定义协议时,需要遵循以下几个原则:

    1. 协议必须是可扩展的,能够容易地添加新的消息类型或字段。
    2. 消息的格式必须明确并符合规范,可以使用固定长度、分隔符、标记等方式来辨别消息的开始和结束。
    3. 在消息头中要包含足够的元信息,能够让接收方对消息进行正确的处理。
    4. 协议设计必须考虑网络上的安全问题,避免数据泄露和信息篡改等风险。
  • 自定义协议通常用于特定领域的应用,如游戏开发、嵌入式系统、金融交易等场景。自定义协议的设计和实现需要结合具体场景进行考虑,需要对网络协议有一定的了解,并且需要注意协议的可靠性、可扩展性和安全性等问题。

3.简易"模拟Linux终端"v2.0

【Server v2.0】 通过在数据头部加入数据的总长度,客户端先读取数据的总长度,决定本次读取的大小,解决粘包问题

#include "temp.h"   //many head files in it

/* 服务器socket结构体 */
struct ServerSocket{
    int sockfd;       //服务器socket文件描述符
    void (* socketBind)(int ,char *,int);   //给sockfd绑定地址函数
    void (* serverListen)(int , int);       //监听sockfd函数
    struct ClientSocket (* serverAccept)(int);  //建立连接函数
};

/* 客户端socket结构体 */
struct ClientSocket{
    int cfd;    //建立连接的socket文件描述符
    char ip[32];    //客户端IP
    int port;   //客户端Port
};

/* 数据结构体 */
struct Data{
    int headerLenth;	//数据头部长度
    long dataLenth;	//数据长度(命令执行成功的结果长度)
    char *dataBody;	//数据正文(命令执行成功的结果)
};

/* 服务器socket绑定地址信息函数实现 */
void socketBind(int sockfd,char *ip,int port){
    int retn;
    /* 初始化地址结构体sockaddr_in */
    struct sockaddr_in serAddr = {
            .sin_port = htons(port),
            .sin_family = AF_INET
    };
    inet_pton(AF_INET,ip,&serAddr.sin_addr.s_addr);
    /* 调用bind()绑定地址 */
    retn = bind(sockfd,(struct sockaddr *)&serAddr,sizeof(serAddr));
    if(retn == -1){
        perror("bind");
        exit(-1);
    }
    printf("<Server> bind address: %s:%d\n",ip,port);
}

/* 服务器socket监听函数实现 */
void serverListen(int sockfd,int n){
    int retn;
    retn = listen(sockfd,n);
    if(retn == -1){
        perror("listen");
        exit(-1);
    }
    printf("<Server> listening...\n");
}

/* 服务器建立连接函数实现,返回值为struct ClientSocket结构体 *
 * (包括建立连接的socket文件描述符、客户端信息) */
struct ClientSocket serverAccept(int sockfd){
    struct sockaddr_in clientAddr;
    socklen_t addrLen = sizeof(clientAddr);
    struct ClientSocket c_socket;
    c_socket.cfd = accept(sockfd,(struct sockaddr *)&clientAddr,&addrLen);
    if(c_socket.cfd == -1){
        perror("accept");
        exit(-1);
    }else{
        c_socket.port = ntohs(clientAddr.sin_port);
        inet_ntop(AF_INET,&clientAddr.sin_addr.s_addr,c_socket.ip,sizeof(clientAddr));
        return c_socket;
    }
}

/* 信号处理函数:回收子进程 */
void waitChild(int signum){
    wait(NULL);
}

/* 处理数据的函数,返回值为struct Data */
struct Data dataDealWith(FILE *file){
    char *tempBuff = (char *)malloc(8192);		//临时buff
    long readBytes = 0;			//读取的字节数
    struct Data data = {
            .dataLenth = 0,
            .dataBody = NULL
    };
    /* 处理数据:计算数据正文大小,并保留管道中的数据到data.dataBody(需要动态调整大小) */
    while(fread(tempBuff,sizeof(char),8192,file) > 0){
        readBytes = strlen(tempBuff)+1;   //读到临时buff的字节数
        data.dataLenth += readBytes;      //数据长度累加readBytes
        if(data.dataLenth <= readBytes){	//如果数据长度小于设置的tempBuff大小,直接拷贝
            data.dataBody = (char *)malloc(readBytes);	
            strcpy(data.dataBody,tempBuff);
        }else if(data.dataLenth > readBytes){	//如果数据长度大于设置的tempBuff大小,扩容后拼接到后面
            data.dataBody = realloc(data.dataBody,data.dataLenth);
            strcat(data.dataBody,tempBuff);
        }
        data.dataBody[strlen(data.dataBody)+1] = '\0';
        memset(tempBuff,'\0',8192);
    }
    free(tempBuff); //释放临时buff
    return data;
}

int main(){

    /* 初始化服务器socket */
    struct ServerSocket ss = {
            .serverAccept = serverAccept,
            .socketBind = socketBind,
            .serverListen = serverListen
    };

    /* 设置端口复用 */
    int optval = 1;
    setsockopt(ss.sockfd,SOL_SOCKET,SO_REUSEPORT,&optval,sizeof(optval));

    ss.sockfd = socket(AF_INET,SOCK_STREAM,0);
    ss.socketBind(ss.sockfd,"192.168.35.128",8880);
    ss.serverListen(ss.sockfd,128);

    /* 多进程实现服务器并发 */
    struct ClientSocket cs; //客户端socket
    pid_t pid = 1;
    int nread;
    while(1){   //循环等待客户端接入
        cs = ss.serverAccept(ss.sockfd);
        printf("<Server> client connected.(%s:%d)\n",cs.ip,cs.port);
        pid = fork();   //创建父子进程
        if(pid > 0){    //父进程
            close(cs.cfd);  //关闭通信的套接字
            signal(SIGCHLD,waitChild);  //注册信号
            continue;
        }else if(pid == 0){     //子进程
            close(ss.sockfd);  //关闭建立连接的socket
            while(1){
                char *readBuff = (char *) malloc(128);      //读buff
                FILE *buffFile = NULL;          //文件流
                struct Data data;
                char head[8];
                while(1) {
                    nread = read(cs.cfd, readBuff, 128);   //读取客户端发过来的命令
                    /* 对read判空,防止客户端退出后一直收空数据的死循环 */
                    if (nread == 0) {
                        printf("<server> client disconnected (%s:%d)\n",cs.ip,cs.port);
                        break;
                    }
                    /* 执行客户端发过来的命令 */
                    buffFile = popen(readBuff, "r");    //命令执行成功结果读取到writeBuff
                    data = dataDealWith(buffFile);
                    sprintf(head,"%ld",data.dataLenth);
                    write(cs.cfd,head, 8);
                    write(cs.cfd,data.dataBody,data.dataLenth);
                    memset(readBuff, '\0', strlen(readBuff));
                    memset(&data,0,sizeof(data));
                    pclose(buffFile);
                }
                exit(1);
            }
        }else{
            perror("fork");
            exit(-1);
        }
    }
}

【Client v2.0】

#include "temp.h"

int main(){
    int fd = socket(AF_INET,SOCK_STREAM,0);
    if(fd == -1){
        perror("socket");
        exit(-1);
    }

    struct sockaddr_in serAddr = {
            .sin_family = AF_INET,
            .sin_port = htons(8880)
    };
    inet_pton(AF_INET,"192.168.35.128",&serAddr.sin_addr.s_addr);

    int retn = connect(fd,(struct sockaddr *)&serAddr,sizeof(serAddr) );
    if(retn == -1){
        perror("connect");
        exit(-1);
    }

    char *writeBuff = (char *)malloc(128);
    char *readBuff = (char *)malloc(1024);
    char *header = (char *)malloc(8);
    int nread = 0;
    int dataLength = 0;
    while(1){
        printf("user@ubuntu-22.04:");
        fgets(writeBuff,128,stdin);
        if(*writeBuff == ' ' || *writeBuff == '\n'){
            continue;
        }
        write(fd,writeBuff, strlen(writeBuff));
        read(fd,header,8);
        if(atol(header) == 0)continue;
        printf("header:%ld\n", atol(header));
        while(dataLength <= atol(header)){
            read(fd,readBuff,1024);
            dataLength += strlen(readBuff)+1;
            printf("%s",readBuff);
            memset(readBuff,'\0', 1024);
            if(dataLength >= atol(header)){
                dataLength = 0;
                break;
            }
        }
        memset(header,'\0', strlen(header));
        memset(writeBuff,'\0', strlen(writeBuff));
        printf("done\n");
    }
}

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

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

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

相关文章

详解C语言string.h中常用的14个库函数(一)

我计划讲解C语言string.h这个头文件中&#xff0c;最常用的14个库函数。为了让大家更加深入的理解这些函数&#xff0c;部分函数我会模拟实现。篇幅所限&#xff0c;如果文章太长了&#xff0c;可能会较难坚持读完&#xff0c;所以我会分几篇博客来讲述。本篇博客主要讲解的函数…

FPGA时序约束(三)时序约束基本路径的深入分析

系列文章目录 FPGA时序约束&#xff08;一&#xff09;基本概念入门及简单语法 FPGA时序约束&#xff08;二&#xff09;利用Quartus18对Altera进行时序约束 文章目录 系列文章目录前言基本时序路径时钟偏差寄存器到寄存器&#xff08;reg2reg&#xff09;建立时间余量保持时…

PHP实现以数组var_dump,array_combine等函数的方法功能举例

目录 前言 一、什么是数组 二、把两个数组合并成一个数组 1.1运行流程&#xff08;思想&#xff09; 1.2代码段 1.3运行截图 三、自动创建数组的一个案例 1.1运行流程&#xff08;思想&#xff09; 1.2代码段 1.3运行截图 前言 1.若有选择&#xff0c;可实现在目录里…

小朋友排队

[蓝桥杯 2014 省 B] 小朋友排队 题目描述 n n n 个小朋友站成一排。现在要把他们按身高从低到高的顺序排列&#xff0c;但是每次只能交换位置相邻的两个小朋友。 每个小朋友都有一个不高兴的程度。开始的时候&#xff0c;所有小朋友的不高兴程度都是 0 0 0。 如果某个小朋友…

Python 学习曲线 从 Python 新手到 Pro

Python 学习曲线 从 Python新手到 Pro 使用代码片段介绍&#xff1a; Python 是世界上最通用和使用最广泛的编程语言之一&#xff0c;以其简单性、可读性和多功能性而闻名。 在本文中&#xff0c;我们将探讨一系列示例场景&#xff0c;其中代码由具有三个不同专业知识水平的程序…

文件系统和动静态库

目录 再识文件属性 查看文件属性的原理 初识inode 了解磁盘 什么是磁盘 磁盘的结构 磁盘的存储结构 CHS寻址 磁盘的逻辑结构 使用LBA地址的意义 理解文件系统 页框和页帧 分治思想管理 Linux ext2文件系统 软硬链接 软链接 硬链接 文件的三个时间 动静态库 …

java运行python脚本,待完善版

参考资料: windows下调用CMD运行方式 兼容linux/windows,同步异步方式 指定特殊运行环境的运行(如:anaconda运行环境) 整合以上三种方式终极版源码 相关内容: 调用python脚本传参说明 如果不传参数,python脚本可以随意写,比如:

【课程介绍篇】C/C++后台开发岗位技能知识树

1 C/C后台开发岗位技能知识树 2 Linux C/C后台架构开发 成长体系课程 3 C技术历史更新 https://www.0voice.com/uiwebsite/html/courses/

《UVM实战》学习笔记——第七章 UVM中的寄存器模型2——期望值/镜像值、自动/显示预测、操作方式

文章目录 前言一、寄存器模型对DUT的模拟1.1 期望值和镜像值1.2 常见操作对期望值和镜像值的影响 二、prediction分类2.1 自动预测2.2 显式预测 三、访问寄存器方式四、mem和reg的联系和差别五、内建built_in sequence5.1 寄存器模型内建序列5.2 存储器模型内建序列5.3 禁止域名…

安装APP时弹窗未知风险未知开发者,还能下载吗?

随着移动互联网的不断普及&#xff0c;人们的日常生活已与手机密不可分。根据相关研究&#xff0c;在使用手机时&#xff0c;人们90%以上的时间都花在某些应用程序上&#xff0c;巨大的需求使得各种各样的APP被开发出来。然而人们在使用APP时必须更加注意其是否来源可信企业&am…

数据结构与算法基础-学习-21-查找之平衡二叉树(AVL树)

目录 一、个人理解 二、最小失衡子树 三、平衡调整的四种类型 1、LL型 2、RR型 3、LR型 4、RL型 四、如何平衡调整 1、LL型调整 2、LR型调整 五、宏定义 六、结构体类型定义 1、AVL树结点类型 2、AVL树类型 3、AVL树结点搜索路径类型 七、函数定义 1、初始化AV…

基于CMS项目的JDBC的实战

基于CMS项目的JDBC的实战 使用的Javase技术&#xff0c;进行控制台输出的客户管理系统&#xff08;CMS&#xff09;&#xff0c;主要功能包含登录&#xff0c;注册、客户信息的展示&#xff0c;客户信息的更新&#xff0c;客户信息添加删除客户、退出系统。 设计创建数据库 …

PEX高效批量网络装机

目录 一、部署PXE远程安装服务 1&#xff09;PXE概述 若要搭建PEX网络体系&#xff0c;必须满足以下几个前提条件 2&#xff09;搭建PXE远程安装服务器 ①安装并启用 TFTP 服务 ②安装并启用 DHCP 服务 ​编辑 ③准备 Linux 内核、初始化镜像文件 ④准备 PXE 引导程序 …

CUDA下载与对应版本查询

文章目录 1 算力&#xff0c;CUDA Driver Version&#xff0c;CUDA Runtime Version2 显卡型号3 实操4 镜像 1 算力&#xff0c;CUDA Driver Version&#xff0c;CUDA Runtime Version 比如说我们进入pytorch官网中&#xff0c;点击下载&#xff0c;如何找到适合自己的CUDA版本…

SCAU 统计学 实验5

8.14 总体平均值&#xff08;μ&#xff09;&#xff1a;7.0 cm 总体方差&#xff08;σ&#xff09;&#xff1a;0.03 cm 样本平均值&#xff08;x̄&#xff09;&#xff1a;6.97 cm 样本方差&#xff08;s&#xff09;&#xff1a;0.0375 cm 样本大小&#xff08;n&#xff…

复旦MOSS大模型开源了!Github和Hugging Face同时上线

来源&#xff1a;量子位 复旦大模型MOSS&#xff0c;正式开源了&#xff01; 作为国内首个开放测试的类ChatGPT产品&#xff0c;MOSS开源地址一放出&#xff0c;又冲上知乎热搜&#xff1a; 从官网介绍来看&#xff0c;MOSS是一个拥有160亿参数的开源对话语言模型。 它由约7…

EventLog Analyzer:高效保护网络安全的强大工具

网络安全是当今数字化世界中最为重要的话题之一。随着越来越多的组织、企业和个人将其业务转移到互联网上&#xff0c;网络安全问题变得越来越严峻。针对这个问题&#xff0c;EventLog Analyzer提供了一个有效的解决方案&#xff0c;让网络管理员可以更好地监控和保护其网络环境…

【虚拟仿真】Unity3D中实现UI的单击、双击、按压、拖动的不同状态判断

推荐阅读 CSDN主页GitHub开源地址Unity3D插件分享简书地址我的个人博客 大家好&#xff0c;我是佛系工程师☆恬静的小魔龙☆&#xff0c;不定时更新Unity开发技巧&#xff0c;觉得有用记得一键三连哦。 一、前言 之前写了一篇在Unity中鼠标的单击、双击、拖动的文章&#xff…

Improving Language Understanding by Generative Pre-Training 论文阅读

论文题目&#xff1a;通过生成式预训练提高语言理解能力 GPT的全称&#xff1a;Generative Pre-trained Transformer。 Generative是指GPT可以利用先前的输入文本来生成新的文本。GPT的生成过程是基于统计的&#xff0c;它可以预测输入序列的下一个单词或字符&#xff0c;从而生…

春招,进阿里了....

个人背景是东北某 985 科班本硕&#xff0c;做的 测试开发&#xff0c;有两个自己写的小项目。下面是一些印象比较深刻的面试题 阿里一面 什么是软件测试&#xff1f; 软件测试过程中会面向哪些群体&#xff1f; 开发一个软件都要经过哪些阶段&#xff1f; 什么是黑盒测试&…