网络编程套接字(三)之TCP服务器简单实现

news2024/11/26 8:50:41

目录

一、服务端TcpServer

1、tcp_server.hpp

2、tcp_server.cc

二、客户端TcpClient

tcp_client.cc

三、服务器和客户端进行通信

四、完整代码


一、服务端TcpServer

首先我们需要对服务端进行封装。我们需要的成员变量有IP地址,端口号port,以及监听套接字listensock。Tcp服务器实现的一般步骤就是:创建套接字,进行绑定,监听等待连接,获取连接,开始通信服务。下面我们一一讲解。

1、tcp_server.hpp

第一步,创建套接字使用的函数和UDP完全一样,只不过我们需要把socket的第二个参数由基于数据报的SOCK_DGRAM 更改为基于字节流式的SOCK_STREAM,SOCK_STREAM提供的就是一个有序的、可靠的、全双工的、基于连接的流式服务。其他参数和后续步骤完全一样。

第二步,进行绑定时使用的bind函数及其参数设置与UDP完全一样。

第三步,TCP和UDP不一样的地方就是这里了。前面我们知道UDP不是面向连接的,而TCP是面向连接的,所以服务器和客户端之间需要建立连接。而服务器首先需要做的就是进行监听,等待客户端的连接。需要使用listen函数。

NAME
       listen - listen for connections on a socket

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

       int listen(int sockfd, int backlog);

参数说明:

sockfd:需要设置为监听状态的套接字对应的文件描述符。

backlog:全连接队列的最大长度。如果有多个客户端同时发来连接请求,此时未被服务器处理的连接就会放入连接队列,该参数代表的就是这个全连接队列的最大长度,一般不要设置太大,设置为5或10即可。

返回值:监听成功返回0,监听失败返回-1,同时错误码会被设置。

以上三步就是服务器的初始化。

第四步,TCP服务器初始化后就可以开始运行了,但TCP服务器在与客户端进行网络通信之前,服务器需要先获取到客户端的连接请求。我们需要使用accept函数。

NAME
       accept, accept4 - accept a connection on a socket

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

       int accept(int sockfd, struct sockaddr *addr, socklen_t *addrlen);

参数说明:

sockfd:特定的监听套接字,表示从该监听套接字中获取连接。

addr:对端网络相关的属性信息,包括协议家族、IP地址、端口号等。

addrlen:调用时传入期望读取的addr结构体的长度,返回时代表实际读取到的addr结构体的长度,这是一个输入输出型参数。

返回值:获取连接成功返回接收到的套接字的文件描述符,获取连接失败返回-1,同时错误码会被设置。

注意:为什么获取连接成功了还要返回一个新的套接字呢?

类里面的监听套接字:用于获取客户端发来的连接请求。accept函数会不断从监听套接字当中获取新连接。
accept函数返回的套接字:用于为本次accept获取到的连接提供服务。

所以说,监听套接字的任务只是不断获取新连接,而真正为这些连接提供服务的套接字是accept函数返回的套接字,而不是监听套接字。

第五步,我们只要获取到客户端的连接,就可以对客户端进行服务了。

2、tcp_server.cc

与UDP相同,我们需要创建TCP服务器对象,然后对其进行初始化,然后运行。我们也是只需要绑定端口号即可。

二、客户端TcpClient

tcp_client.cc

作为TCP客户端,首先也是需要创建套接字。然后向服务器发起连接,最后就可以开始通信了。和UDP一样,客户端不需要绑定端口号。

客户端向服务器发起连接需要使用connect函数。

NAME
       connect - initiate a connection on a socket

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

       int connect(int sockfd, const struct sockaddr *addr,
                   socklen_t addrlen);

参数说明:

sockfd:特定的套接字,表示通过该套接字发起连接请求。

addr:对端网络相关的属性信息,包括协议家族、IP地址、端口号等。

addrlen:传入的addr结构体的长度。

返回值:连接或绑定成功返回0,连接失败返回-1,同时错误码会被设置。

三、服务器和客户端进行通信

首先,我们要知道:对于TCP服务器和客户端,我们也可以使用write函数和read函数进行消息的读取和发送。

我们先写一个服务,实现客户端给服务器发送什么消息,服务器就返回什么消息。

我们先写一个单进程版本的服务。tcp_server.hpp:

#include <iostream>
#include <string>
#include <cstring>
#include <unistd.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <cassert>
#include <signal.h>

static int gmv = 20;

static void service(int sock, const std::string &clientip, const uint16_t &clientport)
{
    char buffer[1024];
    while (true)
    {
        ssize_t s = read(sock, buffer, sizeof(buffer) - 1);
        if (s < 0)
        {
            std::cout << "接收信息失败!" << std::endl;
            break;
        }
        else if (s > 0)
        {
            buffer[s] = 0;
            std::cout << clientip << ":" << clientport << std::endl;
        }
        else
        {
            std::cout << "客户端关闭!" << std::endl;
            break;
        }
        write(sock, buffer, strlen(buffer));
    }
}

class TcpServer
{
public:
    TcpServer(const uint16_t port, const std::string &ip = "") : listensock_(-1), ip_(ip), port_(port)
    {
    }

    void initTcpServer()
    {
        // 1.创建套接字
        listensock_ = socket(AF_INET, SOCK_STREAM, 0);
        if (listensock_ < 0)
        {
            std::cout << "创建套接字失败!" << std::endl;
            exit(0);
        }

        // 2.进行绑定
        struct sockaddr_in src_server;
        bzero(&src_server, sizeof(src_server));
        src_server.sin_family = AF_INET;
        src_server.sin_port = htons(port_);
        inet_pton(AF_INET, ip_.c_str(), &src_server);
        socklen_t len = sizeof(src_server);
        if (bind(listensock_, (struct sockaddr *)&src_server, len) < 0)
        {
            std::cout << "绑定失败!" << std::endl;
            exit(1);
        }

        // 3.开始监听,等待连接
        if (listen(listensock_, gmv) < 0)
        {
            std::cout << "监听失败!" << std::endl;
            exit(2);
        }

        std::cout << "服务器初始化成功!" << std::endl;
    }

    void start()
    {
        //signal(SIGCHLD, SIG_IGN);
        while (true)
        {
            // 4.获取链接
            struct sockaddr_in client_sock;
            socklen_t len = sizeof(client_sock);
            int serversock = accept(listensock_, (struct sockaddr *)&client_sock, &len);
            if (serversock < 0)
            {
                std::cout << "获取链接失败!" << std::endl;
                exit(3);
            }
            // 5.开始通信服务
            uint16_t client_port = ntohs(client_sock.sin_port);
            std::string client_ip = inet_ntoa(client_sock.sin_addr);
            std::cout << "[" << client_port << "-" << client_ip << "]" << std::endl;

            //version 1:单进程版服务————服务器一次只能处理一个客户端,只有处理完一个,才能处理下一个
            service(serversock, client_ip, client_port);
        }
    }

    ~TcpServer()
    {
        close(listensock_);
    }

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

结果:

上面的结果没有什么问题,那么,如果我们同时有两个客户端访问服务器,会发生什么呢?

因为我们目前所写的是一个单执行流版的服务器,这个服务器一次只能为一个客户端提供服务。所以我们发现,当其中一个客户端在和服务器进行通信的时候,另一个客户端并不能与服务器通信,也就是说服务器在某一时刻只能向一个客户端提供服务,只有对一个客户端提供服务完成后,才能对下一个服务器提供服务。

很显然,这种提供服务的方式是不符合实际的,一个服务器应该要能够同时给多个客户端提供服务。所以我们需要改进代码。

多进程服务版本:既然单进程不符合实际,那么我们最先想到的就是使用多进程——创建子进程给新的连接提供服务,父进程去继续获取新的连接。

#include <iostream>
#include <string>
#include <cstring>
#include <unistd.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <cassert>
#include <signal.h>

static int gmv = 20;

static void service(int sock, const std::string &clientip, const uint16_t &clientport)
{
    char buffer[1024];
    while (true)
    {
        ssize_t s = read(sock, buffer, sizeof(buffer) - 1);
        if (s < 0)
        {
            std::cout << "接收信息失败!" << std::endl;
            break;
        }
        else if (s > 0)
        {
            buffer[s] = 0;
            std::cout << clientip << ":" << clientport << std::endl;
        }
        else
        {
            std::cout << "客户端关闭!" << std::endl;
            break;
        }
        write(sock, buffer, strlen(buffer));
    }
}

class TcpServer
{
public:
    TcpServer(const uint16_t port, const std::string &ip = "") : listensock_(-1), ip_(ip), port_(port)
    {
    }

    void initTcpServer()
    {
        // 1.创建套接字
        listensock_ = socket(AF_INET, SOCK_STREAM, 0);
        if (listensock_ < 0)
        {
            std::cout << "创建套接字失败!" << std::endl;
            exit(0);
        }

        // 2.进行绑定
        struct sockaddr_in src_server;
        bzero(&src_server, sizeof(src_server));
        src_server.sin_family = AF_INET;
        src_server.sin_port = htons(port_);
        inet_pton(AF_INET, ip_.c_str(), &src_server);
        socklen_t len = sizeof(src_server);
        if (bind(listensock_, (struct sockaddr *)&src_server, len) < 0)
        {
            std::cout << "绑定失败!" << std::endl;
            exit(1);
        }

        // 3.开始监听,等待连接
        if (listen(listensock_, gmv) < 0)
        {
            std::cout << "监听失败!" << std::endl;
            exit(2);
        }

        std::cout << "服务器初始化成功!" << std::endl;
    }

    void start()
    {
        signal(SIGCHLD, SIG_IGN);
        while (true)
        {
            // 4.获取链接
            struct sockaddr_in client_sock;
            socklen_t len = sizeof(client_sock);
            int serversock = accept(listensock_, (struct sockaddr *)&client_sock, &len);
            if (serversock < 0)
            {
                std::cout << "获取链接失败!" << std::endl;
                exit(3);
            }
            // 5.开始通信服务
            uint16_t client_port = ntohs(client_sock.sin_port);
            std::string client_ip = inet_ntoa(client_sock.sin_addr);
            std::cout << "[" << client_port << "-" << client_ip << "]" << std::endl;
            // version 2.0:多进程版本——创建子进程给新的连接提供服务,父进程去继续获取新的连接
            pid_t id = fork();
            assert(id != -1);
            if (id == 0)
            {
                // child 并且注意僵尸状态
                // child会继承父进程打开的文件及其fd,子进程不需要进行监听的套接字,关闭
                close(listensock_);
                //子进程只需要给连接客户端提供服务的套接字
                service(serversock, client_ip, client_port);
                exit(0);
            } 
            close(serversock);
        }
    }

    ~TcpServer()
    {
        close(listensock_);
    }

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

结果:

这种方法可以解决问题。

当然,我们还有如下的多进程版本:

​
#include <iostream>
#include <string>
#include <cstring>
#include <unistd.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <cassert>
#include <signal.h>

static int gmv = 20;

static void service(int sock, const std::string &clientip, const uint16_t &clientport)
{
    char buffer[1024];
    while (true)
    {
        ssize_t s = read(sock, buffer, sizeof(buffer) - 1);
        if (s < 0)
        {
            std::cout << "接收信息失败!" << std::endl;
            break;
        }
        else if (s > 0)
        {
            buffer[s] = 0;
            std::cout << clientip << ":" << clientport << std::endl;
        }
        else
        {
            std::cout << "客户端关闭!" << std::endl;
            break;
        }
        write(sock, buffer, strlen(buffer));
    }
}

class TcpServer
{
public:
    TcpServer(const uint16_t port, const std::string &ip = "") : listensock_(-1), ip_(ip), port_(port)
    {
    }

    void initTcpServer()
    {
        // 1.创建套接字
        listensock_ = socket(AF_INET, SOCK_STREAM, 0);
        if (listensock_ < 0)
        {
            std::cout << "创建套接字失败!" << std::endl;
            exit(0);
        }

        // 2.进行绑定
        struct sockaddr_in src_server;
        bzero(&src_server, sizeof(src_server));
        src_server.sin_family = AF_INET;
        src_server.sin_port = htons(port_);
        inet_pton(AF_INET, ip_.c_str(), &src_server);
        socklen_t len = sizeof(src_server);
        if (bind(listensock_, (struct sockaddr *)&src_server, len) < 0)
        {
            std::cout << "绑定失败!" << std::endl;
            exit(1);
        }

        // 3.开始监听,等待连接
        if (listen(listensock_, gmv) < 0)
        {
            std::cout << "监听失败!" << std::endl;
            exit(2);
        }

        std::cout << "服务器初始化成功!" << std::endl;
    }

    void start()
    {
        while (true)
        {
            // 4.获取链接
            struct sockaddr_in client_sock;
            socklen_t len = sizeof(client_sock);
            int serversock = accept(listensock_, (struct sockaddr *)&client_sock, &len);
            if (serversock < 0)
            {
                std::cout << "获取链接失败!" << std::endl;
                exit(3);
            }
            // 5.开始通信服务
            uint16_t client_port = ntohs(client_sock.sin_port);
            std::string client_ip = inet_ntoa(client_sock.sin_addr);
            std::cout << "[" << client_port << "-" << client_ip << "]" << std::endl;

            // version 2.1:多进程版
            pid_t pid = fork();
            if (pid == 0)
            {
                // 子进程
                if (fork() > 0)
                    exit(0);
                // 孙子进程
                close(listensock_);
                service(serversock, client_ip, client_port);
            }
             // 父进程
            waitpid(pid, nullptr, 0);
            close(serversock);
        }
    }

    ~TcpServer()
    {
        close(listensock_);
    }

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

我们让子进程创建完孙子进程后立刻退出,此时服务进程(父进程)调用wait/waitpid函数等待子进程就能立刻等待成功,此后服务进程就能继续调用accept函数获取其他客户端的连接请求。

而由于子进程退出,孙子进程就变成孤儿进程,被os领养,最后由os自动回收。 

结果:

多线程版本:我们还可以创建新的线程去对客户端进行服务,让主线程继续获取连接:

#include <iostream>
#include <string>
#include <cstring>
#include <unistd.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <cassert>
#include <signal.h>

static int gmv = 20;

static void service(int sock, const std::string &clientip, const uint16_t &clientport)
{
    char buffer[1024];
    while (true)
    {
        ssize_t s = read(sock, buffer, sizeof(buffer) - 1);
        if (s < 0)
        {
            std::cout << "接收信息失败!" << std::endl;
            break;
        }
        else if (s > 0)
        {
            buffer[s] = 0;
            std::cout << clientip << ":" << clientport << std::endl;
        }
        else
        {
            std::cout << "客户端关闭!" << std::endl;
            break;
        }
        write(sock, buffer, strlen(buffer));
    }
}

class threaddata
{
public:
    int sock_;
    std::string ip_;
    uint16_t port_;
};

class TcpServer
{
public:
    static void *threadRountine(void *arg)
    {
        pthread_detach(pthread_self());
        threaddata *td = static_cast<threaddata *>(arg);
        service(td->sock_, td->ip_, td->port_);
        delete td;
    }

    TcpServer(const uint16_t port, const std::string &ip = "") : listensock_(-1), ip_(ip), port_(port)
    {
    }

    void initTcpServer()
    {
        // 1.创建套接字
        listensock_ = socket(AF_INET, SOCK_STREAM, 0);
        if (listensock_ < 0)
        {
            std::cout << "创建套接字失败!" << std::endl;
            exit(0);
        }

        // 2.进行绑定
        struct sockaddr_in src_server;
        bzero(&src_server, sizeof(src_server));
        src_server.sin_family = AF_INET;
        src_server.sin_port = htons(port_);
        inet_pton(AF_INET, ip_.c_str(), &src_server);
        socklen_t len = sizeof(src_server);
        if (bind(listensock_, (struct sockaddr *)&src_server, len) < 0)
        {
            std::cout << "绑定失败!" << std::endl;
            exit(1);
        }

        // 3.开始监听,等待连接
        if (listen(listensock_, gmv) < 0)
        {
            std::cout << "监听失败!" << std::endl;
            exit(2);
        }

        std::cout << "服务器初始化成功!" << std::endl;
    }

    void start()
    {
        while (true)
        {
            // 4.获取链接
            struct sockaddr_in client_sock;
            socklen_t len = sizeof(client_sock);
            int serversock = accept(listensock_, (struct sockaddr *)&client_sock, &len);
            if (serversock < 0)
            {
                std::cout << "获取链接失败!" << std::endl;
                exit(3);
            }
            // 5.开始通信服务
            uint16_t client_port = ntohs(client_sock.sin_port);
            std::string client_ip = inet_ntoa(client_sock.sin_addr);
            std::cout << "[" << client_port << "-" << client_ip << "]" << std::endl;

            // version 3——多线程版本
            pthread_t tid;
            threaddata *td = new threaddata();
            td->sock_ = serversock;
            td->ip_ = client_ip;
            td->port_ = client_port;
            pthread_create(&tid, nullptr, threadRountine, td);
        }
    }

    ~TcpServer()
    {
        close(listensock_);
    }

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

 

四、完整代码

tcp_server.hpp

#include <iostream>
#include <string>
#include <cstring>
#include <unistd.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <cassert>
#include <signal.h>

static int gmv = 20;

static void service(int sock, const std::string &clientip, const uint16_t &clientport)
{
    char buffer[1024];
    while (true)
    {
        ssize_t s = read(sock, buffer, sizeof(buffer) - 1);
        if (s < 0)
        {
            std::cout << "接收信息失败!" << std::endl;
            break;
        }
        else if (s > 0)
        {
            buffer[s] = 0;
            std::cout << clientip << ":" << clientport << std::endl;
        }
        else
        {
            std::cout << "客户端关闭!" << std::endl;
        }
        write(sock, buffer, strlen(buffer));
    }
}

class threaddata
{
public:
    int sock_;
    std::string ip_;
    uint16_t port_;
};

class TcpServer
{
public:
    static void *threadRountine(void *arg)
    {
        pthread_detach(pthread_self());
        threaddata *td = static_cast<threaddata *>(arg);
        service(td->sock_, td->ip_, td->port_);
        delete td;
    }

    TcpServer(const uint16_t port, const std::string &ip = "") : listensock_(-1), ip_(ip), port_(port)
    {
    }

    void initTcpServer()
    {
        // 1.创建套接字
        listensock_ = socket(AF_INET, SOCK_STREAM, 0);
        if (listensock_ < 0)
        {
            std::cout << "创建套接字失败!" << std::endl;
            exit(0);
        }

        // 2.进行绑定
        struct sockaddr_in src_server;
        bzero(&src_server, sizeof(src_server));
        src_server.sin_family = AF_INET;
        src_server.sin_port = htons(port_);
        inet_pton(AF_INET, ip_.c_str(), &src_server);
        socklen_t len = sizeof(src_server);
        if (bind(listensock_, (struct sockaddr *)&src_server, len) < 0)
        {
            std::cout << "绑定失败!" << std::endl;
            exit(1);
        }

        // 3.开始监听,等待连接
        if (listen(listensock_, gmv) < 0)
        {
            std::cout << "监听失败!" << std::endl;
            exit(2);
        }

        std::cout << "服务器初始化成功!" << std::endl;
    }

    void start()
    {
        signal(SIGCHLD, SIG_IGN);
        while (true)
        {
            // 4.获取链接
            struct sockaddr_in client_sock;
            socklen_t len = sizeof(client_sock);
            int serversock = accept(listensock_, (struct sockaddr *)&client_sock, &len);
            if (serversock < 0)
            {
                std::cout << "获取链接失败!" << std::endl;
                exit(3);
            }
            // 5.开始通信服务
            uint16_t client_port = ntohs(client_sock.sin_port);
            std::string client_ip = inet_ntoa(client_sock.sin_addr);
            std::cout << "[" << client_port << "-" << client_ip << "]" << std::endl;

            // version 1:单进程版服务————服务器一次只能处理一个客户端,只有处理完一个,才能处理下一个
            // service(serversock, client_ip, client_port);

            // version 2.0:多进程版本——创建子进程给新的连接提供服务,父进程去继续获取新的连接
            /* pid_t id = fork();
            assert(id != -1);
            if (id == 0)
            {
                // child 并且注意僵尸状态
                // child会继承父进程打开的文件及其fd,子进程不需要进行监听的套接字,关闭
                close(listensock_);
                //子进程只需要给连接客户端提供服务的套接字
                service(serversock, client_ip, client_port);
                exit(0);
            } */

            /* // version 2.1:多进程版
            pid_t pid = fork();
            if (pid == 0)
            {
                // 子进程
                if (fork() > 0)
                    exit(0);
                // 孙子进程
                close(listensock_);
                service(serversock, client_ip, client_port);
            }
            // 父进程
            waitpid(pid, nullptr, 0); */

            // version 3——多线程版本
            pthread_t tid;
            threaddata *td = new threaddata();
            td->sock_ = serversock;
            td->ip_ = client_ip;
            td->port_ = client_port;
            pthread_create(&tid, nullptr, threadRountine, td);

            close(serversock);
        }
    }

    ~TcpServer()
    {
        close(listensock_);
    }

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

tcp_server.cc

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

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

// ./tcpserver 8080
int main(int argc, char *argv[])
{
    if (argc != 2)
    {
        usage(argv[0]);
        exit(0);
    }
    uint16_t server_port=atoi(argv[1]);
    std::unique_ptr<TcpServer> sev(new TcpServer(server_port));
    sev->initTcpServer();
    sev->start();

    return 0;
}

tcp_client.cc 

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

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

// ./tcpclient IP port
int main(int argc, char *argv[])
{
    if (argc != 3)
    {
        usage(argv[0]);
        exit(1);
    }
    std::string server_ip = argv[1];
    uint16_t server_port = atoi(argv[2]);

    int sock = socket(AF_INET, SOCK_STREAM, 0);
    if (sock < 0)
    {
        std::cout << "创建套接字失败!" << std::endl;
        exit(0);
    }

    struct sockaddr_in server;
    memset(&server, 0, sizeof server);
    server.sin_family = AF_INET;
    server.sin_port = htons(server_port);
    server.sin_addr.s_addr = inet_addr(server_ip.c_str());
    if (connect(sock, (struct sockaddr *)&server, sizeof server) < 0)
    {
        std::cout << "连接失败!" << std::endl;
        exit(0);
    }

    // 开始通信
    while (true)
    {
        std::string line;
        std::cout << "请输入# ";
        std::getline(std::cin, line);
        ssize_t s = write(sock, line.c_str(), line.size());

        char buffer[1024];
        ssize_t m = read(sock, buffer, sizeof buffer - 1);
        buffer[m] = 0;
        std::cout << "服务器:" << buffer << std::endl;
    }

    return 0;
}

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

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

相关文章

49.基于SpringBoot + Vue实现的前后端分离-爱心公益网站系统(项目 + 论文)

项目介绍 本站是一个B/S模式系统&#xff0c;采用SpringBoot Vue框架&#xff0c;MYSQL数据库设计开发&#xff0c;充分保证系统的稳定性。系统具有界面清晰、操作简单&#xff0c;功能齐全的特点&#xff0c;使得基于SpringBoot Vue技术的爱心公益网站系统设计与实现管理工作…

Gartner发布信任、风险和安全管理领域的生成式人工智能创新指南:生成式AI整个生命周期运行中的攻击面

生成式人工智能带来了三类新风险&#xff1a;内容异常、数据保护和人工智能应用安全。使用或构建 GenAI 应用的 IT 领导者可以利用这项研究来了解市场动态并评估新兴的 GenAI TRiSM 技术和解决新风险的提供商。 主要发现 在企业应用中集成大语言模型&#xff08;LLM &#xff0…

《QT实用小工具·三十》基于QT开发的访客管理平台demo

1、概述 源码放在文章末尾 该项目为访客管理平台demo&#xff0c;包含主界面、系统设置、警情查询、调试帮助、用户退出功能。 项目部分代码如下&#xff1a; #ifndef QTHELPER_H #define QTHELPER_H#include "head.h"class QtHelper { public://获取所有屏幕区域…

Vue 3 项目构建与效率提升:vite-plugin-vue-setup-extend 插件应用指南

一、Vue3项目创建 前提是已安装Node.js&#xff08;点击跳转Node官网&#xff09; npm create vuelatest这一指令将会安装并执行 create-vue&#xff0c;它是 Vue 官方的项目脚手架工具。你将会看到一些诸如 TypeScript 和测试支持之类的可选功能提示&#xff1a; ✔ Projec…

InstantMesh:利用稀疏视图大规模重建模型从单张图像高效生成3D网格

作者&#xff1a;Jiale Xu&#xff0c;Weihao Cheng&#xff0c;Yiming Gao等 编译&#xff1a;东岸因为一点人工一点智能 InstantMesh&#xff1a;利用稀疏视图大规模重建模型从单张图像高效生成3D网格在这项工作中&#xff0c;我们提出了InstantMesh&#xff0c;一个开源的…

5.MMD ray渲染天空盒的导入和参数介绍

现在已经有了一个TDAMiku的模型 1. Sky with box 导入Sky with box.pmx 天空盒 再把ray.x和ray_controler.pmx放进去 调节背景模型绘制顺序 天空盒在最上面 上材质 给miku上main.fx材质 在自发光一栏给天空盒添加对应的材质 Sky with lighting.fx 调节参数 按TAB…

python中的守护进程、僵尸进程、孤儿进程

继续上一篇文章的探讨&#xff1a;https://blog.csdn.net/weixin_39743356/article/details/137885419 守护进程 守护进程&#xff08;Daemon Process&#xff09;是一种在后台运行的特殊类型的进程&#xff0c;它独立于控制终端&#xff0c;并且周期性地执行某种任务或等待处…

性能分析与调优

性能分析方法 自底向上&#xff1a;通过监控硬件及操作系统性能指标&#xff08;cpu、内存、磁盘、网络等硬件资源的性能指标&#xff09;来分析性能问题&#xff08;配置、程序问题&#xff09; 先检查&#xff0c;再下药 自顶向下&#xff1a;通过生成负载来观察被测试的系…

解锁外贸财务系统宝藏:多语言多货币平台推荐汇总

本文将为您推荐几款主流的多语言多货币外贸财务系统有&#xff1a;Zoho Books 、SAP、Oracle、QuickBooks、Xero、TradeGecko&#xff0c;并分析了每款产品的主要特点&#xff0c;希望能帮助到你&#xff01; 一、Zoho Books Zoho Books是一款支持180种货币、17种语言的外贸…

视频技术笔记-色差分量

色差分量接口采用YPbPr和YCbCr两种标识。 YPbPr&#xff1a;表示逐行扫描色差输出。 YCbCr&#xff1a;后者表示隔行扫描色差输出。 色差分量接口一般利用3根信号线分别传送亮色和两路色差信号。 色差分量接口是色差接口使用不是很普遍&#xff0c;主要的原因是一些CRT电视机…

CSS 格式化上下文 + CSS兼容处理

个人主页&#xff1a;学习前端的小z 个人专栏&#xff1a;HTML5和CSS3悦读 本专栏旨在分享记录每日学习的前端知识和学习笔记的归纳总结&#xff0c;欢迎大家在评论区交流讨论&#xff01; 文章目录 ✍CSS 格式化上下文&#x1f525;1 格式化上下文&#x1f337;1.1 块级格式化…

BIM数据管理快速指南

在我的日常工作中&#xff0c;作为数字协作协调员&#xff0c;我花费大量时间收集、检查和管理各种 BIM 数据。 很多次收到一组数据后我就无奈地举手——质量远远达不到我可以使用的程度。 然后我会开始一个普通的数据清理过程。 我无数次咒骂过这种情况——大多数建设项目的人…

vue动态添加style的样式

vue在动态添加style样式的时候&#xff0c;有以下注意点 1.凡是有-的style属性名都要变成驼峰式&#xff0c;比如font-weight,需要写成fontWeight 2.除了绑定值&#xff0c;其他属性名的值要用引号括起来&#xff0c;比如width&#xff1a;‘75px’,不要忘记引号 3.动态绑定时&…

ipv4Bypass:一款基于IPv6实现的IPv4安全绕过与渗透测试工具

关于ipv4Bypass ipv4Bypass是一款基于IPv6实现的安全绕过与渗透测试工具&#xff0c;该工具专为红队研究人员设计&#xff0c;可以帮助广大研究人员通过IPv6绕过目标安全策略&#xff0c;以此来检测安全检测机制的健壮性。 20世纪90年代是互联网爆炸性发展时期&#xff0c;随着…

2024华中杯C题完整解题思路及代码

C 题 基于光纤传感器的平面曲线重建算法建模 光纤传感技术是伴随着光纤及光通信技术发展起来的一种新型传感器技 术。它是以光波为传感信号、光纤为传输载体来感知外界环境中的信号&#xff0c;其 基本原理是当外界环境参数发生变化时&#xff0c;会引起光纤传感器中光波参量&…

python环境引用《解读》----- 环境隔离

首先我先讲一下Anaconda&#xff0c;因为我用的是Anaconda进行包管理。方便后面好理解一点。 大家在python中引用环境的时候都会经历下面这一步&#xff1a; 那么好多人就会出现以下问题&#xff08;我就是遇到了这个问题&#xff09;&#xff1a; 我明明下载了包&#xff0c…

oracle 数据库 迁移 mysql

将 Oracle 数据库迁移到 MySQL 是一项复杂的任务&#xff0c;因为这两种数据库管理系统具有不同的架构、语法和功能。下面是一个基本的迁移步骤&#xff0c;供你参考&#xff1a; 步骤一&#xff1a;评估和准备工作 1.评估数据库结构&#xff1a;仔细分析 Oracle 数据库的结构…

Python实战:批量加密Excel文件,保护数据安全!

在日常工作中&#xff0c;我们经常需要处理大量的Excel文件。 为了保护敏感数据的安全性&#xff0c;我们可能需要对这些文件进行加密。 本文将介绍如何使用Python实现批量加密Excel文件的操作&#xff0c;以提高工作效率和数据安全性。 安装所需的库 在开始之前&#xff0…

Java 网络编程之TCP:基于BIO

环境&#xff1a; jdk 17 IntelliJ IDEA 2023.1.1 (Ultimate Edition) Windows 10 专业版 22H2 TCP&#xff1a;面向连接的&#xff0c;可靠的数据传送协议 Java中的TCP网络编程&#xff0c;其实就是基于常用的BIO和NIO来实现的&#xff0c;本文先讨论BIO&#xff1b; BIO…

润开鸿与蚂蚁数科达成战略合作,发布基于鸿蒙的mPaaS移动应用开发产品

4月18日&#xff0c;江苏润和软件股份有限公司&#xff08;以下简称“润和软件”&#xff09; 旗下专注鸿蒙方向的专业技术公司及终端操作系统发行版厂商江苏润开鸿数字科技有限公司&#xff08;以下简称“润开鸿”&#xff09;与蚂蚁数科举行战略合作签约仪式&#xff0c;并发…