Linux网络编程—Day9

news2025/1/17 6:00:36

今天!我们来学习新的章节——I/O。

Linux提供了很多高级的I/O函数。它们并不像Linux基础I/O函数 (比如open和read)那么常用,但在特定的条件下却表现出优秀的性能。我们学习下和网络编程相关的几个,这些函数大致分为三类:

  • 用于创建文件描述符的函数,包括pipe、dup/dup2函数
  • 用于读写数据的函数,包括readv/writev、sendfile、 mmap/munmap、splice和tee函数
  • 用于控制I/O行为和属性的函数,包括fcntl函数

创建文件描述符的函数

pipe函数

pipe函数可用于创建一个管道,以实现进程间通信。之后学习如何使用管道来实现进程间通信,目前只学习其基本使用方式。 pipe函数的定义如下:

#include<unistd.h>
int pipe(int fd[2]);

pipe函数的参数是一个包含两个int型整数的数组指针。该函数成功时返回0,并将一对打开的文件描述符值填入其参数指向的数组。如果失败,则返回-1并设置errno。通过pipe函数创建的这两个文件描述符fd[0]和fd[1]分别构成管道的两端,往fd[1]写入的数据可以从fd[0]读出。并且,fd[0]只能用于从管道读出数据,fd[1]则只能用于往管道写入数据,而不能反过来使用。如果要实现双向的数据传输,就应该使用两个管道。

管道内部传输的数据是字节流,这和TCP字节流的概念相同。但二者又有细微的区别。应用层程序能往一个TCP连接中写入多少字节的数据,取决于对方的接收通告窗口的大小和本端的拥塞窗口的大小。而管道本身拥有一个容量限制,它规定如果应用程序不将数据从管道读走的话,该管道最多能被写入多少字节的数据。自Linux 2.6.11内核起,管道容量的大小默认是65536字节。我们可以使用fcntl函数来修改管道容量。

dup函数和dup2函数

若想把标准输入重定向到一个文件,或者把标准输出重定向到一个网络连接(比如CGI编程)。这可以通过下面的用于复制文件描述符的dup或dup2函数来实现:

#include<unistd.h>
int dup(int file_descriptor);
int dup2(int file_descriptor_one,int file_descriptor_two);

dup函数创建一个新的文件描述符,该新文件描述符和原有文件描述符file_descriptor指向相同的文件、管道或者网络连接。并且dup返回的文件描述符总是取系统当前可用的最小整数值。dup2和dup类似,不过它将返回第一个不小于file_descriptor_two的整数值。dup和dup2系统调用失败时返回-1并设置errno。通过dup和dup2创建的文件描述符并不继承原文件描述符的属性。

示例:利用dup函数实现了一个基本的CGI服务器(是一种根据请求信息动态产生回应内容的技术。通过CGI,Web 服务器可以将根据请求不同启动不同的外部程序,并将请求内容转发给该程序,在程序执行结束后,将执行结果作为回应返回给客户端)

服务器端程序代码:

#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <assert.h>
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <errno.h>
#include <string.h>

int main( int argc, char* argv[] )
{
    if( argc <= 2 )
    {
        printf( "usage: %s ip_address port_number\n", basename( argv[0] ) );
        return 1;
    }
    const char* ip = argv[1];
    int port = atoi( argv[2] );

    struct sockaddr_in address;
    bzero( &address, sizeof( address ) );
    address.sin_family = AF_INET;
    inet_pton( AF_INET, ip, &address.sin_addr );
    address.sin_port = htons( port );

    int sock = socket( PF_INET, SOCK_STREAM, 0 );
    assert( sock >= 0 );

    int ret = bind( sock, ( struct sockaddr* )&address, sizeof( address ) );
    assert( ret != -1 );

    ret = listen( sock, 5 );
    assert( ret != -1 );

    struct sockaddr_in client;
    socklen_t client_addrlength = sizeof( client );
    int connfd = accept( sock, ( struct sockaddr* )&client, &client_addrlength );
    if ( connfd < 0 )
    {
        printf( "errno is: %d\n", errno );
    }
    else
    {
        //连接到客户端之后
        close( STDOUT_FILENO );//先关闭标准输出文件描述符
        dup( connfd );//复制socket文件描述符connfd
        //服务器输出到标准输出的内容(“abcd”)就会直接发送到与客户连接对应的
        //socket上,因此printf调用的输出将被客户端获得(而不是显示在服务器程序的终端上)。
        printf( "abcd\n" );
        close( connfd );
    }

    close( sock );
    return 0;
}

客户端程序代码:

#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <assert.h>
#include <stdio.h>
#include <unistd.h>
#include <string.h>
#include <stdlib.h>
#include <errno.h>
int main( int argc, char* argv[] )
{
    if( argc <= 2 )
    {
        printf( "usage: %s ip_address port_number\n", basename( argv[0] ) );
        return 1;
    }
    const char* ip = argv[1];
    int port = atoi( argv[2] );

    struct sockaddr_in server_address;
    bzero( &server_address, sizeof( server_address ) );
    server_address.sin_family = AF_INET;
    inet_pton( AF_INET, ip, &server_address.sin_addr );
    server_address.sin_port = htons( port );

    int sockfd = socket( PF_INET, SOCK_STREAM, 0 );
    assert( sockfd >= 0 );
    if (connect(sockfd, (struct sockaddr* )&server_address, sizeof(server_address ))<0)
    {
        printf( "connection failed\n" );
    }
    else
    {
        char buf[30];
        printf("connection successful!\n");
        if (recv(sockfd, buf, sizeof(buf), 0) > 0)
        {
            printf("%s\n",buf);
        }
        else
        {
            printf("errno is:%d\n",errno);
        }
    }

    close( sockfd );
    return 0;
}

分别在服务器端,客户端运行两个程序

在服务器代码中,我们先关闭标准输出文件描述符 STDOUT_FILENO(其值是1),然后复制socket文件描述符connfd。因为dup总是返回系统中最小的可用文件描述符,所以它的返回值实际上是1,即之前关闭的标准输出文件描述符的值。这样一来,服务器输出到标准输出的内容(“abcd”)就会直接发送到与客户连接对应的 socket上,因此printf调用的输出将被客户端获得(而不是显示在服务器程序的终端上)。这就是CGI服务器的基本工作原理。

读写数据的函数

readv函数和writev函数

readv函数将数据从文件描述符读到分散的内存块中,即分散读; writev函数则将多块分散的内存数据一并写入文件描述符中,即集中写。它们的定义如下:

#include<sys/uio.h>
ssize_t readv(int fd,const struct iovec*vector,int count);
ssize_t writev(int fd,const struct iovec*vector,int count);

fd参数是被操作的目标文件描述符。vector参数的类型是iovec结构数组,该结构体描述一块内存区。 count参数是vector数组的长度,即有多少块内存数据需要从fd读出或写到fd。readv和writev在成功时返回读出/写入fd的字节数,失败则返回-1并设置errno。它们相当于简化版的recvmsg和sendmsg函数。

struct iovec
{
    void*iov_base;/*内存起始地址*/
    size_t iov_len;/*这块内存的长度*/
};

在Day4中,学习了Web服务器的http请求:当Web服务器解析完一个HTTP请求之后,如果目标文档存在且客户具有读取该文档的权限,那么它就需要发送一个HTTP应答来传输该文档。这个HTTP应答包含1个状态行、 多个头部字段、1个空行和文档的内容。其中,前3部分的内容可能被 Web服务器放置在一块内存中,而文档的内容则通常被读入到另外一块单独的内存中(通过read函数或mmap函数)。事实上,不需要把这两部分内容拼接到一起再发送,而是可以使用writev函数将它们同时写出。看下面代码示例:

服务器端:

#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <assert.h>
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <errno.h>
#include <string.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <fcntl.h>

#define BUFFER_SIZE 1024
static const char* status_line[2] = { "200 OK", "500 Internal server error" };

int main( int argc, char* argv[] )
{
    if( argc <= 3 )
    {
        printf( "usage: %s ip_address port_number filename\n", basename( argv[0] ) );
        return 1;
    }
    const char* ip = argv[1];//ip地址
    int port = atoi( argv[2] );//端口号
    const char* file_name = argv[3];//文件名

    //创建套接字等一系列操作
    struct sockaddr_in address;
    bzero( &address, sizeof( address ) );
    address.sin_family = AF_INET;
    inet_pton( AF_INET, ip, &address.sin_addr );
    address.sin_port = htons( port );

    int sock = socket( PF_INET, SOCK_STREAM, 0 );
    assert( sock >= 0 );

    int ret = bind( sock, ( struct sockaddr* )&address, sizeof( address ) );
    assert( ret != -1 );

    ret = listen( sock, 5 );
    assert( ret != -1 );

    //获取客户端套接字
    struct sockaddr_in client;
    socklen_t client_addrlength = sizeof( client );
    int connfd = accept(sock, (struct sockaddr* )&client, &client_addrlength );
    if ( connfd < 0 )
        printf("errno is: %d\n", errno);
    else//成功连接之后
    {
        char header_buf[ BUFFER_SIZE ];//用于保存HTTP应答的状态行、头部字段和一个空行的缓存区
        memset(header_buf, '\0', BUFFER_SIZE);
        char* file_buf;//用于存放目标文件内容的应用程序缓存
        struct stat file_stat;//用于获取目标文件的属性,比如是否为目录,文件大小等
        bool valid = true;//记录目标文件是否是有效文件
        int len = 0;//缓存区header_buf目前已经使用了多少字节的空间
        if(stat(file_name, &file_stat) < 0)//目标文件不存在
            valid = false;
        else
        {
            if(S_ISDIR(file_stat.st_mode))//目标文件是一个目录
                valid = false;
            else if(file_stat.st_mode & S_IROTH)//当前用户有读取目标文件的权限
            {
                /*动态分配缓存区file_buf,并指定其大小为目标文件的大小file_stat.st_size加
                1,然后将目标文件读入缓存区file_buf中*/
                int fd = open(file_name, O_RDONLY);
                file_buf = new char [file_stat.st_size + 1];
                memset(file_buf, '\0', file_stat.st_size + 1);
                if (read(fd, file_buf, file_stat.st_size) < 0)
                    valid = false;
            }
            else
                valid = false;
        }
        //如果目标文件有效,则发送正常的HTTP应答
        if(valid)
        {
            /*下面这部分内容将HTTP应答的状态行、“Content-Length”头部字段和一个空行依次
            加入header_buf中*/
            ret = snprintf(header_buf, BUFFER_SIZE-1, "%s %s\r\n", "HTTP/1.1",,status_line[0]);
            len += ret;
            ret = snprintf( header_buf + len, BUFFER_SIZE-1-len, 
                             "Content-Length: %d\r\n", file_stat.st_size );
            len += ret;
            ret = snprintf( header_buf + len, BUFFER_SIZE-1-len, "%s", "\r\n" );
            /*利用writev将header_buf和file_buf的内容一并写出*/
            struct iovec iv[2];
            iv[ 0 ].iov_base = header_buf;
            iv[ 0 ].iov_len = strlen( header_buf );
            iv[ 1 ].iov_base = file_buf;
            iv[ 1 ].iov_len = file_stat.st_size;
            ret = writev( connfd, iv, 2 );
        }
        else/*如果目标文件无效,则通知客户端服务器发生了“内部错误”*/
        {
            ret = snprintf( header_buf, BUFFER_SIZE-1, "%s %s\r\n", "HTTP/1.1", status_line[1] );
            len += ret;
            ret = snprintf( header_buf + len, BUFFER_SIZE-1-len, "%s", "\r\n" );
            send( connfd, header_buf, strlen( header_buf ), 0 );
        }
        close( connfd );
        delete [] file_buf;
    }

    close( sock );
    return 0;
}

在服务器端执行该程序

 然后发现服务器端在等待客户端连接,在客户端执行telnet命令,文件成功被写入到客户端。

 sendfile函数

sendfile函数在两个文件描述符之间直接传递数据(完全在内核中操作),从而避免了内核缓冲区和用户缓冲区之间的数据拷贝,效率很高,这被称为零拷贝。sendfile函数的定义如下:

#include<sys/sendfile.h>
ssize_t sendfile(int out_fd,int in_fd,off_t*offset,size_t count);

in_fd参数是待读出内容的文件描述符,out_fd参数是待写入内容的文件描述符。offset参数指定从读入文件流的哪个位置开始读,如果为空,则使用读入文件流默认的起始位置。count参数指定在文件描述符in_fd和out_fd之间传输的字节数。sendfile成功时返回传输的字节数,失败则返回-1并设置errno。

示例:利用sendfile函数将服务器上的一个文件传送给客户端。

#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <assert.h>
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <errno.h>
#include <string.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <sys/sendfile.h>

int main( int argc, char* argv[] )
{
    if( argc <= 3 )
    {
        printf( "usage: %s ip_address port_number filename\n", basename( argv[0] ) );
        return 1;
    }
    const char* ip = argv[1];
    int port = atoi( argv[2] );
    const char* file_name = argv[3];

    int filefd = open( file_name, O_RDONLY );
    assert( filefd > 0 );
    struct stat stat_buf;
    fstat( filefd, &stat_buf );

    struct sockaddr_in address;
    bzero( &address, sizeof( address ) );
    address.sin_family = AF_INET;
    inet_pton( AF_INET, ip, &address.sin_addr );
    address.sin_port = htons( port );

    int sock = socket( PF_INET, SOCK_STREAM, 0 );
    assert( sock >= 0 );

    int ret = bind( sock, ( struct sockaddr* )&address, sizeof( address ) );
    assert( ret != -1 );

    ret = listen( sock, 5 );
    assert( ret != -1 );

    struct sockaddr_in client;
    socklen_t client_addrlength = sizeof( client );
    int connfd = accept( sock, ( struct sockaddr* )&client, &client_addrlength );
    if ( connfd < 0 )
    {
        printf( "errno is: %d\n", errno );
    }
    else
    {
        //connfd是待写入内容的文件描述符,filefd是待读出内容的文件描述符
        sendfile( connfd, filefd, NULL, stat_buf.st_size );
        close( connfd );
    }

    close( sock );
    return 0;
}

我们将目标文件作为第3个参数传递给服务器程序,客户telnet到该服务器上即可获得该文件。相比上一个程序,这个代码没有为目标文件分配任何用户空间的缓存,也没有执行读取文件的操作,但同样实现了文件的发送,其效率显然要高得多。

 mmap函数和munmap函数

mmap函数用于申请一段内存空间。我们可以将这段内存作为进程间通信的共享内存,也可以将文件直接映射到其中。munmap函数则释放由mmap创建的这段内存空间。它们的定义如下:

#include<sys/mman.h>
void*mmap(void*start,size_t length,int prot,int flags,int fd,off_t offset);
int munmap(void*start,size_t length);

start参数允许用户使用某个特定的地址作为这段内存的起始地址。如果它被设置成NULL,则系统自动分配一个地址。length参数指定内存段的长度。prot参数用来设置内存段的访问权限。它可以取以下几个值的按位或:

  • PROT_READ,内存段可读;
  • PROT_WRITE,内存段可写;
  • PROT_EXEC,内存段可执行;
  • PROT_NONE,内存段不能被访问。

flags参数控制内存段内容被修改后程序的行为。它可以被设置为下表的某些值(仅列出了常用的值)的按位或(其中MAP_SHARED和MAP_PRIVATE是互斥的,不能同时指定)。

 fd参数是被映射文件对应的文件描述符。它一般通过open系统调用获得。offset参数设置从文件的何处开始映射(对于不需要读入整个文件的情况)。

mmap函数成功时返回指向目标内存区域的指针,失败则返回 MAP_FAILED并设置errno。munmap函数成功时返回0,失败则返回-1并设置errno。

splice函数

splice函数用于在两个文件描述符之间移动数据,也是零拷贝操作。splice函数的定义如下:

#include<fcntl.h>
ssize_t splice(int fd_in,loff_t*off_in,int fd_out,loff_t*off_out,size_t len,unsigned int flags);

 fd_in参数是待输入数据的文件描述符,如果fd_in是一个管道文件描述符,那么off_in参数必须被设置为NULL。fd_in不是一个管道 文件描述符,off_in表示从输入数据流的何处开始读取数据,fd_out/off_out参数的含义与fd_in/off_in相同,用于输出数据流,len参数指定移动数据的长度,flags参数则控制数据如何移动,它可以被设置为下表的某些值的按位或。

 splice函数调用成功时返回移动字节的数量。它可能返回0,表示没有数据需要移动,splice函数失败时返回-1并设置 errno。

示例:使用splice函数来实现一个零拷贝的回射服务器,它将客户端发送的数据原样返回给客户端

服务器端代码:

#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <assert.h>
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <errno.h>
#include <string.h>
#include <fcntl.h>

int main( int argc, char* argv[] )
{
    if( argc <= 2 )
    {
        printf( "usage: %s ip_address port_number\n", basename( argv[0] ) );
        return 1;
    }
    const char* ip = argv[1];
    int port = atoi( argv[2] );

    struct sockaddr_in address;
    bzero( &address, sizeof( address ) );
    address.sin_family = AF_INET;
    inet_pton( AF_INET, ip, &address.sin_addr );
    address.sin_port = htons( port );

    int sock = socket( PF_INET, SOCK_STREAM, 0 );
    assert( sock >= 0 );

    int ret = bind( sock, ( struct sockaddr* )&address, sizeof( address ) );
    assert( ret != -1 );

    ret = listen( sock, 5 );
    assert( ret != -1 );

    struct sockaddr_in client;
    socklen_t client_addrlength = sizeof( client );
    int connfd = accept( sock, ( struct sockaddr* )&client, &client_addrlength );
    if ( connfd < 0 )
    {
        printf( "errno is: %d\n", errno );
    }
    else
    {
        int pipefd[2];
        assert( ret != -1 );
        ret = pipe( pipefd );//创建管道
        //将connfd上流入的客户数据定向到管道中
        ret = splice( connfd, NULL, pipefd[1], NULL, 32768, SPLICE_F_MORE | SPLICE_F_MOVE ); 
        assert( ret != -1 );
        /*将管道的输出定向到connfd客户连接文件描述符*/
        ret = splice( pipefd[0], NULL, connfd, NULL, 32768, SPLICE_F_MORE | SPLICE_F_MOVE );
        assert( ret != -1 );
        close( connfd );
    }

    close( sock );
    return 0;
}

服务器端执行程序

客户端在连接到服务器之后,像服务器写内容,可以看到立马会有一模一样的信息回射到客户端。

通过splice函数将客户端的内容读入到pipefd[1]中,然后再使用splice函数从pipefd[0]中读出该内容到客户端,从而实现了简单高效的回射服务。整个过程未执行recv/send操作,因此也未涉及用户空间和内核空间之间的数据拷贝。

tee函数

tee函数在两个管道文件描述符之间复制数据,也是零拷贝操作。它不消耗数据,因此源文件描述符上的数据仍然可以用于后续的读操作。 tee函数的原型如下:

#include<fcntl.h>
ssize_t tee(int fd_in,int fd_out,size_t len,unsigned int flags);

该函数的参数的含义与splice相同(但fd_in和fd_out必须都是管道文件描述符)。tee函数成功时返回在两个文件描述符之间复制的数据数量(字节数)。返回0表示没有复制任何数据,tee失败时返回-1并设置 errno。

控制I/O行为和属性的函数

fcntl函数

fcntl函数,提供了对文件描述符的各种控制操作。另外一个常见的控制文件描述符属性和行为的 系统调用是ioctl,而且ioctl比fcntl能够执行更多的控制。但是,对于控制文件描述符常用的属性和行为,fcntl函数是由POSIX规范指定的首选方法。

#include<fcntl.h>
int fcntl(int fd,int cmd,…);

fd参数是被操作的文件描述符,cmd参数指定执行何种类型的操作。根据操作类型的不同,该函数可能还需要第三个可选参数arg。fcntl函数支持的常用操作及其参数如下表所示:

 在网络编程中,fcntl函数通常用来将一个文件描述符设置为非阻塞的,具体代码如下:

int setnonblocking(int fd)
{
    int old_option=fcntl(fd,F_GETFL);/*获取文件描述符旧的状态标志*/
    int new_option=old_option|O_NONBLOCK;/*设置非阻塞标志*/
    fcntl(fd,F_SETFL,new_option);
    return old_option;/*返回文件描述符旧的状态标志,以便日后恢复该状态标志*/
}

此外,SIGIO和SIGURG这两个信号与其他Linux信号不同,它们必须与某个文件描述符相关联方可使用:当被关联的文件描述符可读或可写时,系统将触发SIGIO信号;当被关联的文件描述符上有带外数据可读时,系统将触发SIGURG信号。将信号和文件描述符关联的方法,就是使用fcntl函数为目标文件描述符指定宿主进程或进程组,那么被指定的宿主进程或进程组将捕获这两个信号。使 用SIGIO时,还需要利用fcntl设置其O_ASYNC标志,更多内容今后会学习。

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

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

相关文章

二、注册中心与服务调用-Nacos

文章目录 二、注册中心与服务调用1、Nacos概述1.1 什么是Nacos1.2 常见的注册中心1.3 Nacos结构图1.4 Nacos下载和安装 2、注册服务2.1 Nacos注册service-hosp 二、注册中心与服务调用 目前在医院列表中需要医院的信息和等级信息,而两段信息属于不同的的模块,service-hosp和se…

【自然语言处理】- 作业4: 预训练语言模型BERT实现与应用

课程链接: 清华大学驭风计划 代码仓库&#xff1a;Victor94-king/MachineLearning: MachineLearning basic introduction (github.com) 驭风计划是由清华大学老师教授的&#xff0c;其分为四门课&#xff0c;包括: 机器学习(张敏教授) &#xff0c; 深度学习(胡晓林教授), 计算…

Leetcode—回溯算法

​ 回溯算法的本质 是穷举&#xff0c;穷举所有可能&#xff0c;然后选出合适的答案&#xff0c;一般用于解决以下类型的问题&#xff1a; 组合问题&#xff1a;N个数里面按一定规则找出k个数的集合切割问题&#xff1a;一个字符串按一定规则有几种切割方式子集问题&#xf…

Day4——数据库基础1

Day4——数据库基础 数据库基础--基于phpstudy自带的MySQL数据库&#xff08;下载了PHPstudy后就无需下载额外的MySQL&#xff09; 一、数据库概念1、为什么要学习数据库&#xff1f;2、什么是数据库&#xff1f;3、数据库的访问方式4、数据管理技术经历的三个阶段5、关系型数据…

Three.js深度冲突(模型闪烁)与解决方案

Mesh面重合渲染测试 下面代码创建两个重合的矩形平面Mesh&#xff0c;通过浏览器预览&#xff0c;当你旋转三维场景的时候&#xff0c;你会发现模型渲染的时候产生闪烁。 这种现象&#xff0c;主要是两个Mesh重合&#xff0c;电脑GPU分不清谁在前谁在后&#xff0c;这种现象&a…

Debug | Litemall项目上线

文章目录 1. litemall资源1.1 Litemall项目开源网址1.2 Litemall网盘地址 2. 项目上线2.1 项目框架2.2 项目配置2.3 依赖工具安装2.4 数据库导入2.5 启动小商城和管理后台的后台服务2.6 启动管理后台的前台服务 3项目展示&#xff1a; 1. litemall资源 1.1 Litemall项目开源网…

webpack 自定义loader

文章目录 项目目录搭建项目my-raw-loader参数schema-utils tpl-loader 项目目录 让我们实现一些简易的loader&#xff0c;从大量的简易loader的实现过程中学习编写如何 webpack loader ├── loaders # loader目录 ├── src …

数据库sql语句-----游标和存储过程

关键词&#xff1a; create procedure xxx&#xff08;&#xff09;as.......go 查询&#xff1a; exec ... 从例子中感悟一下&#xff1a; create table cartoon( linenum int, name varchar(50) not null, line varchar(100) not null )insert into cartoon values(1,灰太…

基于SpringBoot+Vue+MybatisPlus的智慧校园系统

智慧校园系统 1. 项目简介2. 项目模块3. 技术栈4. 软件环境4.1 安装数据库4.2 安装数据库客户端Navicat工具4.4 安装IDEA4.4 安装Maven 5. 系统页面5.1 首页登录页5.2 系统功能模块5.3 Swagger2接口文档查阅5.4 运行截图 6. 源代码下载 1. 项目简介 智慧校园管理系统是一个基于…

从零开始Vue3+Element Plus后台管理系统(十三)——富文本编辑器、Markdown编辑器、代码编辑器

早就想着要放几个编辑器的Demo到项目中&#xff0c;这也是项目开始就立下的flag。 今天专门挑选了几款主流编辑器&#xff0c;包括绕不开的富文本编辑器&#xff0c;码农最爱的markdown编辑器&#xff0c;还有用途相对少的代码编辑器。 时间有限的情况下&#xff0c;仅引入4个…

去哪儿酒店数据下载

字段内容包含&#xff1a; id int(11) NOT NULL AUTO_INCREMENT, hotelid varchar(50) DEFAULT NULL, url varchar(200) DEFAULT NULL, hotelname2 varchar(100) DEFAULT NULL, name varchar(100) DEFAULT NULL, province varchar(50) DEFAULT NULL, d…

zabbix安装完成后,无法找到zabbix/bin目录

问题 zabbix安装完成后&#xff0c;无法找到zabbix/bin目录 详细问题 笔者安装zabbix后&#xff0c;自定义item key进行测试。需在zabbix-server 端 切换目录&#xff1a; cd /usr/local/zabbix/bin 执行查询命令&#xff1a; ./zabbix_get -s 192.168.174.132 -p 10050 -k …

Bat批处理中的 FINDSTR用法

linux中的grep等同于win中的findstr命令。可以在txt文本中截取到有特定关键字的行&#xff0c;并显示出来。 grep也可以通过关键字&#xff0c;在一个文件夹下查找多个有这些关键字的文件&#xff0c;并生成结果。 一、findstr命令介绍 findstr是Windows系统自带的命令,简单来说…

chatgpt赋能Python-python_end的用法

简介 Python是一种广泛使用的编程语言&#xff0c;被广泛应用于数据科学&#xff0c;机器学习&#xff0c;网络编程&#xff0c;Web开发等领域。Python内置了许多有用的操作符和关键字&#xff0c;其中包括end。end是Python中一个非常有用的操作符&#xff0c;它可以在将多个输…

C001--Visual C++ 6.0集成开发环境的下载与安装并运行简单的C语言程序

visual c的下载安装过程不复杂&#xff0c;只需要运行setup.exe程序&#xff0c;然后按照安装程序的提示信息进行操作&#xff0c;也可以指定系统文件存放的路径。 目录 一&#xff0c;下载安装集成开发环境 1&#xff0c;流程 2&#xff0c;熟悉visual c工作界面 二&am…

Element-UI介绍:主题定制、自定义组件和插件扩展

部分数据来源&#xff1a;ChatGPT 什么是Element-UI Element-UI是一款简单好用的前端UI库&#xff0c;基于Vue.js开发。它提供了常用的组件和样式&#xff0c;可以帮助我们快速地构建美观、实用的交互界面。 在使用Element-UI开发项目过程中&#xff0c;我们有时遇到了一些特…

chatgpt赋能Python-python_dim

Python dim&#xff1a;将维度降至极致 在机器学习和数据分析领域&#xff0c;我们经常需要处理高维数据。然而&#xff0c;高维数据不仅处理起来麻烦&#xff0c;而且往往也不利于数据分析和模型训练。因此&#xff0c;数据科学家和工程师需要一个有效的方法来降低数据维度。…

【数据结构与算法】- 期末考试

课程链接: 清华大学驭风计划 代码仓库&#xff1a;Victor94-king/MachineLearning: MachineLearning basic introduction (github.com) 驭风计划是由清华大学老师教授的&#xff0c;其分为四门课&#xff0c;包括: 机器学习(张敏教授) &#xff0c; 深度学习(胡晓林教授), 计算…

python笔记 第一章

学习用到的资源链接&#xff1a;https://pan.baidu.com/s/1Ftptx_9iH9xFYj3NbugMrg?pwd1234 提取码&#xff1a;1234 文章目录 1.1 简介Python版本 1.2 解释器下载Python解释器安装Python解释器验证是否安装 扩展 1.3 PyCharm安装PyCharm基本使用新建项目设置只看项目文件运行…

【深度学习】- 作业6: 图像自然语言描述生成

课程链接: 清华大学驭风计划 代码仓库&#xff1a;Victor94-king/MachineLearning: MachineLearning basic introduction (github.com) 驭风计划是由清华大学老师教授的&#xff0c;其分为四门课&#xff0c;包括: 机器学习(张敏教授) &#xff0c; 深度学习(胡晓林教授), 计算…