网络编程套接字,Linux下实现echo服务器和客户端

news2024/9/30 13:15:27

目录

1、一些网络中的名词

1.1 IP地址

1.2 端口号port

1.3  "端口号" 和 "进程ID"

1.4 初始TCP协议

1.5 UDP协议

2、socket编程接口

2.1 socket 常见API

2.2 sockaddr结构

3、简单的网络程序

3.1 udp实现echo服务器和客户端

3.1.1 echo服务器实现

3.1.2 echo客户端实现

3.1.3 运行结果

3.2  tcp实现echo服务器和客户端

3.2.1 多进程的echo服务器

3.2.2 基于线程池tcp的echo服务器

 3.3 代码中的一些函数

3.3.1 地址转换函数

3.3.2 udp使用的的函数

3.3.3 tcp使用的函数

4、结语


1、一些网络中的名词

1.1 IP地址

        IP地址就和我们现实中的地址是一个概念,只不过一个在网络中定位,一个在现实中定位,

        在一台服务器往另一台服务器发送数据的时候,IP数据包的头部中,有两个IP地址,一个是源IP地址,另一个是目的IP地址,

1.2 端口号port

端口号(port)是传输层协议的内容.

        端口号是一个2字节16位的整数;

        端口号用来标识一个进程, 告诉操作系统, 当前的这个数据要交给哪一个进程来处理;

        IP地址 + 端口号能够标识网络上的某一台主机的某一个进程;

        一个端口号只能被一个进程占用.

1.3  "端口号" 和 "进程ID"

        pid 表示唯一一个进程; 此处我们的端口号也是唯一表示一个进程。一个进程可以绑定多个端口号; 但是一个端口号不能被多个进程绑定。

1.4 初始TCP协议

传输层协议

有连接

可靠传输

面向字节流

1.5 UDP协议

传输层协议

无连接

不可靠传输

面向数据报

网络字节序

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

        发送主机通常将发送缓冲区中的数据按内存地址从低到高的顺序发出;

        接收主机把从网络上接到的字节依次保存在接收缓冲区中,也是按内存地址从低到高的顺序保存;

        因此,网络数据流的地址应这样规定:先发出的数据是低地址,后发出的数据是高地址.

        TCP/IP协议规定,网络数据流应采用大端字节序,即低地址高字节.

        不管这台主机是大端机还是小端机, 都会按照这个TCP/IP规定的网络字节序来发送/接收数据;

        如果当前发送主机是小端, 就需要先将数据转成大端; 否则就忽略, 直接发送即可;

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

#include <arpa/inet.h>

uint32_t htonl(uint32_t hostlong);
uint16_t htons(uint16_t hostshort);
uint32_t ntohl(uint32_t netlong);
uint16_t ntohs(uint16_t netshort);

h表示host,n表示network,l表示32位长整数,s表示16位短整数。

例如htonl表示将32位的长整数从主机字节序转换为网络字节序,例如将IP地址转换后准备发送。

如果主机是小端字节序,这些函数将参数做相应的大小端转换然后返回;

如果主机是大端字节序,这些  函数不做转换,将参数原封不动地返回。

2、socket编程接口

2.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.2 sockaddr结构

        socket API是一层抽象的网络编程接口,适用于各种底层网络协议,如IPv4、IPv6,以及UNIX Domain Socket. 然而, 各种网络协议的地址格式并不相同.

         IPv4和IPv6的地址格式定义在netinet/in.h中,IPv4地址用sockaddr_in结构体表示,包括16位地址类型, 16位端口号和32位IP地址.

        IPv4、IPv6地址类型分别定义为常数AF_INET、AF_INET6. 这样,只要取得某种sockaddr结构体的首地址,不需要知道具体是哪种类型的sockaddr结构体,就可以根据地址类型字段确定结构体中的内容.

        socket API可以都用struct sockaddr *类型表示, 在使用的时候需要强制转化成sockaddr_in; 这样的好处是程序的通用性, 可以接收IPv4, IPv6, 以及UNIX Domain Socket各种类型的sockaddr结构体指针做为参数;

sockaddr 结构

struct sockaddr
  {
    __SOCKADDR_COMMON (sa_);	/* Common data: address family and length.  */
    char sa_data[14];		/* Address data.  */
  };

sockaddr_in 结构

struct sockaddr_in
  {
    __SOCKADDR_COMMON (sin_);
    in_port_t sin_port;			/* Port number.  */
    struct in_addr sin_addr;		/* Internet address.  */

    /* Pad to size of `struct sockaddr'.  */
    unsigned char sin_zero[sizeof (struct sockaddr) -
			   __SOCKADDR_COMMON_SIZE -
			   sizeof (in_port_t) -
			   sizeof (struct in_addr)];
  };

        虽然socket api的接口是sockaddr, 但是我们真正在基于IPv4编程时, 使用的数据结构是sockaddr_in; 这个结构里主要有三部分信息: 地址类型, 端口号, IP地址.

in_addr结构

struct in_addr
  {
    in_addr_t s_addr;
  };

in_addr用来表示一个IPv4的IP地址. 其实就是一个32位的整数;

3、简单的网络程序

3.1 udp实现echo服务器和客户端

3.1.1 echo服务器实现

//udp_server.hpp
#include <iostream>
#include <string>
#include <cstring>
#include <cstdlib>
#include <unordered_map>
#include <vector>

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

class udpserver{
public:
    udpserver(std::string ip, int16_t port):_fd(-1), _ip(ip), _port(port),_users(0)
    {}

    ~udpserver(){
        if (_fd > 0) {
            close(_fd);
        }
    }

    void initServer() {
        _fd = socket(AF_INET, SOCK_DGRAM, 0);
        if (_fd < 0) {
            perror("注册socket失败");
            exit(2);
        }
        struct sockaddr_in local;
        memset(&local, 0, sizeof(local));
        local.sin_family = AF_INET;
        local.sin_port = htons(_port);
        local.sin_addr.s_addr = inet_addr(_ip.c_str());

        if (bind(_fd, (struct sockaddr*)&local, sizeof(local)) < 0) {
            perror("绑定失败!");
            exit(3);
        }
        //std::cout << "绑定成功!"<< std::endl;
    }

    void startServer(){

        //准备用来接收客户端发送的消息的缓冲区
        char buffer[1024];
        while (1) {
            
            //准备用来接收发送消息的客户端信息
            memset(buffer, '\0', sizeof(buffer));
            struct sockaddr_in peer;
            memset(&peer, 0, sizeof(peer));
            socklen_t len = sizeof(peer);
            //接收数据,以及接收发送数据的客户端信息
            //std::cout << "正在接收!" << std::endl;
            ssize_t recv_size = recvfrom(_fd, buffer, sizeof(buffer) - 1, 0, (struct sockaddr*)&peer, &len);
            
            //打印客户端发送来的数据
            //std::cout << "接收成功!正在打印:" << std::endl;
            // if (recv_size > 0) {
            //     buffer[recv_size] = 0;
            //     std::string ip = inet_ntoa(peer.sin_addr);
            //     int16_t port = ntohs(peer.sin_port);
            //     std::cout << "[" << ip << ":" << port << "]:";
            //     std::cout << buffer << std::endl;
            // }
            //处理数据
            buffer[recv_size] = 0;
            std::string massage;
            massage += inet_ntoa(peer.sin_addr);
            massage += ":";
            massage += ntohs(peer.sin_port);

            //_users.insert(make_pair<std::string,struct sockaddr_in>(massage, peer);
            _users.insert({massage, peer});

            massage += "#";
            massage += buffer;
            //_users.insert(makepair(, peer);

            //回写数据
            //sendto(_fd, buffer, strlen(buffer), 0, (struct sockaddr*)&peer, len);
            for (auto &s : _users) {
                sendto(_fd, massage.c_str(), massage.size(),0 ,(struct sockaddr*)&(s.second), sizeof(s.second));
            }
        }
    }

private:
    int _fd;
    std::string _ip;
    int16_t _port;
    std::unordered_map<std::string,struct sockaddr_in> _users;
};
//udp_server.cpp
#include "udpserver.hpp"
#include <memory>

int main(int argc, char* args[]) {

    std::string ip;
    int16_t port = 0;
    if (argc == 3) {
        ip = args[1];
        port = atoi(args[2]);
    }
    else if (argc == 2) {
        ip = "0.0.0.0";
        port = atoi(args[1]);
    }
    else{
        perror("输入错误!");
        return 1;
    }

    std::unique_ptr<udpserver> server(new udpserver(ip,port));
    server->initServer();
    server->startServer();
    return 0;
}

3.1.2 echo客户端实现

//udp_client.cpp
#include <iostream>
#include <cstring>
#include <cstdlib>
#include <unistd.h>
#include <pthread.h>
#include <cstdio>

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

struct sendData{
    int _sock;
    struct sockaddr_in *server;
};

void* sending(void *arg) {
    struct sendData* data = (struct sendData*)arg;
    int sock = data->_sock;
    struct sockaddr_in server = *(data->server);

    while (1) {
        std::string massage;
        std::cerr << "请输入内容:" ;
        std::getline(std::cin, massage);
        //发送数据
        sendto(sock, massage.c_str(), massage.size(), 0, (struct sockaddr*)&server, sizeof(server));
    }
}

void* receive(void *arg) {
    struct sendData* data = (struct sendData*)arg;
    int sock = data->_sock;
    char buffer[1024];
    while (1) {
        memset(buffer, '\0', sizeof(buffer));
        struct sockaddr_in from;
        socklen_t len = sizeof(from);
        ssize_t recv_size = recvfrom(sock, buffer, sizeof(buffer), 0, (struct sockaddr*)&from, &len);
        if(recv_size < 1) {
            continue;
        }
        buffer[recv_size] = '\0';
        //printf("[%s:%u]#%s\n",inet_ntoa(from.sin_addr),ntohs(from.sin_port),buffer);
        std::cout << buffer << std::endl;
    }
}

//客户端,负责给服务端发送消息
int main(int argc, char* args[]) {

    if (argc != 3) {
        std::cerr << "请正确输入参数!" << std::endl;
        exit(1);
    }
    std::string ip = args[1];
    int16_t port = atoi(args[2]);

    //创建套接字
    int _sock = socket(AF_INET, SOCK_DGRAM, 0);
    //这里依然会绑定,但是不需要手动绑定,回自动绑定,在第一次send的时候自动绑定,  
    if (_sock < 0) {
        exit(2);
    }

    struct sockaddr_in server;
    server.sin_addr.s_addr = inet_addr(ip.c_str());
    server.sin_family = AF_INET;
    server.sin_port = htons(port);
    socklen_t len = sizeof(server);

    sendData data;
    data._sock = _sock;
    data.server = &server;

    //创建线程,让线程1负责发送,线程2负责接收
    pthread_t send,recv;
    pthread_create(&send,nullptr,sending,(void*)&data);
    pthread_create(&send,nullptr,receive,(void*)&data);

    pthread_join(send,nullptr);
    pthread_join(recv,nullptr);

    close(_sock);
    return 0;
}

3.1.3 运行结果

3.2  tcp实现echo服务器和客户端

3.2.1 多进程的echo服务器


#include <iostream>
#include <string>
#include <cerrno>
#include <cstring>
#include <signal.h>

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

static void servise(int serviseSock, std::string userip, int16_t userport) {
    char buffer[1024];
    while (1) {
        memset(buffer, 0, sizeof(buffer));
        size_t s = read(serviseSock,buffer,sizeof(buffer));    

        if (s > 0) {
            buffer[s] = '\0';
            std::cout << userip.c_str() << ":" << userport << "#" << buffer << std::endl;
        }
        else if (s == 0) {
            //表示对方关闭了连接
            std::cerr << userip << ":" << userport << " shutdowm,me too!" << std::endl;
            break;
        }
        else {
            std::cerr << "read socket error," << errno << strerror(errno) << std::endl;
            break;
        }

        write(serviseSock, buffer, strlen(buffer));
    }
}


class tcpServer{
public:
    tcpServer(int16_t port, std::string ip = "")
    :_ip(ip),
    _port(port),
    _listenSock(-1)
    {}
    
    ~tcpServer(){
        if (_listenSock > 0) {
            close(_listenSock);
        }
    }

    void initServer(){
        //backlog不能太大也不能太小
        static int gbacklog = 20;
        //申请描述符
        _listenSock = socket(AF_INET, SOCK_STREAM, 0);
        if (_listenSock < 0) {
            std::cerr << "注册socket失败" << std::endl;
            exit(2);
        }
        //绑定端口号和IP地址
        struct sockaddr_in local;
        local.sin_family = AF_INET;
        local.sin_addr.s_addr = _ip.empty() ? INADDR_ANY : inet_addr(_ip.c_str());
        local.sin_port = htons(_port);

        if (bind(_listenSock, (struct sockaddr*)&local, sizeof(local)) < 0) {
            std::cerr << "绑定ip和端口号失败" << std::endl;
            exit(3);
        }

        //设置监听状态
        if (listen(_listenSock, gbacklog) < 0) {
            std::cerr << "设置监听失败" << std::endl;
            exit(4);
        }
    }

    void start() {

        //将子进程的信号改为忽略
        signal(SIGCHLD, SIG_IGN);
        
        while (1) {

            struct sockaddr_in user;
            socklen_t len = sizeof(user);
            int serviseSock = accept(_listenSock, (struct sockaddr*)&user, &len);
            std::string userip = inet_ntoa(user.sin_addr);
            int16_t userport = ntohs(user.sin_port);
            
            //servise(serviseSock,userip,userport);
            int pid = fork();
            if (pid == 0) {
                close(_listenSock);
                servise(serviseSock,userip,userport);
                close(serviseSock);
                exit(0);
            }
            close(serviseSock);
        }
    }

private:
    std::string _ip;
    int16_t _port;
    int _listenSock;
};

        但是我们都知道,在操作系统中,进程是资源分配的基本单位,如果使用多进程的方案的话,就非常的浪费资源,所以,相比之下,使用多线程的方式回更好,我们在实现一个基于线程池的实现方式。

3.2.2 基于线程池tcp的echo服务器

//自己实现的循环队列,当中使用的锁和信号都是自己封装的,这里就不放代码了
//ringqueue.hpp
#include <iostream>
#include <vector>
#include "sem.hpp"
#include "mutex.hpp"

template<class T>
class ringqueue {

public:
    ringqueue(int capacity = 10)
    :_ring_queue(capacity),
    _start(0),
    _tail(0),
    _space_sem(capacity),
    _data_sem(0),
    _mtx()
    {}

    void push(const T &in){

        _space_sem.p();
        _mtx.lock();
        _ring_queue[_start++] = in;
        _start %= _ring_queue.size();
        _data_sem.v();
        _mtx.unlock();
    }

    void pop(T & out){
        _data_sem.p();
        _mtx.lock();
        out = _ring_queue[_tail++];
        _tail %= _ring_queue.size();
        _space_sem.v();
        _mtx.unlock();
    }

    ~ringqueue()
    {
        
    }

private:
    std::vector<T> _ring_queue;
    int _start;
    int _tail;
    sem _space_sem;
    sem _data_sem;
    mutex _mtx;
};
//单例模式的线程池
//其中的线程也是自己进行封装的,不做代码展示
//thread_pool.hpp
#include "thread.hpp"
#include "ringQueue.hpp"
#include <ctime>
#include <unistd.h>

template <class T>
struct poolData
{
    Thread* _self;
    ringqueue<T>* _rq;
};

template <class T>
class Pool
{

public:
    static Pool<T>* getpool(int num = 10){
        if (nullptr == _pool) {
            pthread_mutex_lock(&mtx);
            if (nullptr == _pool) {
                _pool = new Pool<T>(num);
            }
            pthread_mutex_unlock(&mtx);
        }
        return _pool;
    }

private:
    Pool(int num) 
    :_consumer(num),
    _rq(10)
    {}
    Pool(const Pool& pool) = delete;
    Pool& operator=(const Pool& pool) = delete;
public:
    void strat()
    {
        poolData<T> condata[_consumer.size()];
        for (int i = 0; i < _consumer.size(); ++i) {
            _consumer[i] = new Thread(i);
            condata[i]._self = _consumer[i];
            condata[i]._rq = &_rq;
            _consumer[i]->create(consumer,&condata[i]);
        }
    }

    // 生产者
    void pushTask(T task)
    {
        _rq.push(task);
    }
    // 消费者
    static void *consumer(void *args)
    {
        poolData<T> *pd = (poolData<T>*)args;
        Thread *self = pd->_self;
        ringqueue<T> *rq = pd->_rq;
        std::cout << self->name() << " Successfully started!" << std::endl;
        while (true) {
            T t;
            rq->pop(t);
            (*t)();
            delete t;
        }
    }

    ~Pool()
    {
        for (int i = 0; i < _consumer.size(); ++i) {
            _consumer[i]->join();
            delete _consumer[i];
        }
        
    }

private:
    
    ringqueue<T> _rq;
    std::vector<Thread*> _consumer;
    static pthread_mutex_t mtx;
    static Pool<T>* _pool;
};

template<class T>
Pool<T>* Pool<T>::_pool = nullptr;

template<class T>
pthread_mutex_t Pool<T>::mtx = PTHREAD_MUTEX_INITIALIZER;
//tcp_server.hpp
#include "thread_pool.hpp"
#include "Task.hpp"

#include <iostream>
#include <string>
#include <cerrno>
#include <cstring>
#include <signal.h>

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

static void servise(int serviseSock, std::string & userip, int16_t userport) {
    char buffer[1024];
    while (1) {
        memset(buffer, 0, sizeof(buffer));
        size_t s = read(serviseSock,buffer,sizeof(buffer));    
        if (s > 0) {
            buffer[s] = '\0';
            std::cout << userip.c_str() << ":" << userport << "#" << buffer << std::endl;
        }
        else if (s == 0) {
            //表示对方关闭了连接
            std::cerr << userip << ":" << userport << " shutdowm,me too!" << std::endl;
            break;
        }
        else {
            std::cerr << "read socket error," << errno << strerror(errno) << std::endl;
            break;
        }

        write(serviseSock, buffer, strlen(buffer));
    }
    close(serviseSock);
}

class tcpServer{
public:
    tcpServer(int16_t port, std::string ip = "")
    :_ip(ip),
    _port(port),
    _listenSock(-1),
    _pool_ptr(Pool<Task*>::getpool())
    {}
    
    ~tcpServer(){
        if (_listenSock > 0) {
            close(_listenSock);
        }
    }

    void initServer(){
        //backlog不能太大也不能太小
        static int gbacklog = 20;
        //申请描述符
        _listenSock = socket(AF_INET, SOCK_STREAM, 0);
        if (_listenSock < 0) {
            std::cerr << "注册socket失败" << std::endl;
            exit(2);
        }
        //绑定端口号和IP地址
        struct sockaddr_in local;
        local.sin_family = AF_INET;
        local.sin_addr.s_addr = _ip.empty() ? INADDR_ANY : inet_addr(_ip.c_str());
        local.sin_port = htons(_port);

        if (bind(_listenSock, (struct sockaddr*)&local, sizeof(local)) < 0) {
            std::cerr << "绑定ip和端口号失败" << std::endl;
            exit(3);
        }
        //设置监听状态
        if (listen(_listenSock, gbacklog) < 0) {
            std::cerr << "设置监听失败" << std::endl;
            exit(4);
        }
    }

    void start() {
        _pool_ptr->strat();
        while (1) {
            struct sockaddr_in user;
            socklen_t len = sizeof(user);
            int serviseSock = accept(_listenSock, (struct sockaddr*)&user, &len);
            std::string userip = inet_ntoa(user.sin_addr);
            int16_t userport = ntohs(user.sin_port);
            Task *task = new Task(serviseSock, userip, userport, servise);
            _pool_ptr->pushTask(task);
        }
    }

private:
    std::string _ip;
    int16_t _port;
    int _listenSock;
    Pool<Task*>* _pool_ptr;
};
//服务器入口,
//tcp_server.cpp
#include "tcp_server.hpp"
#include <memory>

int main(int argc, char* args[]) {
    std::string ip;
    int16_t port;
    if (argc == 2) {
        ip = "";
        port = atoi(args[1]);
    }
    else if (argc == 3) {
        ip = args[1];
        port = atoi(args[2]);
    }
    else {
        std::cerr << "输入错误!" << std::endl;
        exit(1);
    }
    std::unique_ptr<tcpServer> server(new tcpServer(port,ip));
    server->initServer();
    server->start();

    return 0;
}

3.2.3 运行结果

 3.3 代码中的一些函数

3.3.1 地址转换函数

        本节基于IPv4的socket网络编程,sockaddr_in中的成员struct in_addr sin_addr表示32位  的IP 地址但是我们通常用点分十进制的字符串表示IP 地址,以下函数可以在字符串表示  和in_addr表示之间转换;

字符串与in_addr的一些函数:

#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>

int inet_aton(const char *cp, struct in_addr *inp);
in_addr_t inet_addr(const char *cp);
in_addr_t inet_network(const char *cp);
char *inet_ntoa(struct in_addr in);
struct in_addr inet_makeaddr(int net, int host);
in_addr_t inet_lnaof(struct in_addr in);
in_addr_t inet_netof(struct in_addr in);

3.3.2 udp使用的的函数

发送函数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);

接收函数recvfrom:

#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);

3.3.3 tcp使用的函数

发送函数

#include <sys/types.h>
#include <sys/socket.h>

ssize_t send(int sockfd, const void *buf, size_t len, int flags);
#include <unistd.h>

ssize_t write(int fd, const void *buf, size_t count);

接收函数

#include <unistd.h>

ssize_t read(int fd, void *buf, size_t count);
#include <sys/types.h>
#include <sys/socket.h>

ssize_t recv(int sockfd, void *buf, size_t len, int flags);

4、结语

        本文中若有错误,请私信或评论指出,谢谢!

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

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

相关文章

Python 如何使用 csv、openpyxl 库进行读写 Excel 文件详细教程(更新中)

csv 基本概述 首先介绍下 csv (comma separated values)&#xff0c;即逗号分隔值&#xff08;也称字符分隔值&#xff0c;因为分隔符可以不是逗号&#xff09;&#xff0c;是一种常用的文本格式&#xff0c;用以存储表格数据&#xff0c;包括数字或者字符。 程序在处理数据时…

《机器人学一(Robotics(1))》_台大林沛群 第 6 周 【轨迹规划_直线转折处抛物线平滑】Quiz 6

步骤&#xff1a; 1、 编程 将PPT 的例子 跑一遍&#xff0c; 确保代码无误 2、根据题目 修改 相关参数 文章目录 求解代码_Python 解决的问题&#xff1a; 线段间转折点 的 速度 不连续 解决方法&#xff1a; 将直线段 两端 修正为 二次方程式 二次项圆滑 求解代码_Python …

图床项目性能测试

文章目录 一、FastDFS文件性能测试1.1 上传文件测试1.2 下载测试1.3 删除文件测试1.4 如何提高 二、图床项目wrklua性能测试2.1 wrk2.2 MySQL索引优化2.2 注册测试2.2.1 无索引性能2.2.2 有索引性能 2.3 登录测试2.3.1 无索引性能2.3.2 有索引性能 2.4 读取文件测试2.4.1 无索引…

ctfshow-web-新年好?

0x00 前言 CTF 加解密合集CTF Web合集网络安全知识库 文中工具皆可关注 皓月当空w 公众号 发送关键字 工具 获取 0x01 题目 0x02 Write Up 尝试访问之后返回了源码。可以看到重点是在setTimeout 首先来看setTimeout&#xff0c;setTimeout的意思就是说在一段时间后执行指定…

H5页面,上下滑动翻页(整页翻书切换效果)

使用 H5页面&#xff0c;上下滑动翻页 修改 页面间的切换&#xff1a;整页翻书的切换效果 demo代码&#xff1a; <!DOCTYPE html> <html> <head> <meta charset"UTF-8"> <meta http-equiv"X-UA-Compatible" content"IEe…

突破传统显示技术,探索OLED透明屏的亮度革命

OLED透明屏作为未来显示技术的颠覆者&#xff0c;其亮度性能成为其引人注目的特点之一。 那么&#xff0c;今天尼伽便深入探讨OLED透明屏的亮度&#xff0c;通过引用数据、报告和行业动态&#xff0c;为读者提供高可读性和专业性强的SEO软文&#xff0c;增加可信度和说服力。 …

计算机丢失mfc140.dll怎么办,分享几种常见解决方案[dll修复工具下载】

一、mfc140.dll 的详细介绍 1.mfc140.dll 的作用 MFC140.dll 是 MFC 的一个组件&#xff0c;它提供了许多用于创建 Windows 应用程序的类和函数&#xff0c;如窗口管理、控件、对话框、菜单、消息处理等。这些类和函数可以帮助开发者更轻松地开发图形用户界面&#xff08;GUI&a…

【Linux】shell脚本和bat脚本:

文章目录 一、脚本对应环境&#xff1a;【1】shell&#xff1a;linux环境&#xff1b;后缀名为.sh【2】bat&#xff1a;windows环境&#xff1b;后缀名为.bat或者.cmd 二、脚本执行&#xff1a;【1】shell执行【2】bat脚本执行 三、脚本相关命令&#xff1a;1. shell命令【1】s…

【UE】材质描边、外发光、轮廓线

原教学视频链接&#xff1a; ue4 材质描边、外发光、轮廓线_哔哩哔哩_bilibili 步骤 1. 首先新建一个材质&#xff0c;这里命名为“Mat_outLine” 在此基础上创建一个材质实例 2. 在视口中添加一个后期处理体积 设置后期处理体积为无限范围 点击添加一个数组 选择“资产引用”…

如何将JACOCO应用到企业实战中~测试过招,只需6点

1、下载 jacoco 官网&#xff1a;https://www.jacoco.org/jacoco/index.html 如果你想学习自动化测试&#xff0c;我这边给你推荐一套视频&#xff0c;这个视频可以说是B站播放全网第一的自动化测试教程&#xff0c;同时在线人数到达1000人&#xff0c;并且还有笔记可以领取及…

Win10右键 nvidia rtx desktop manager 怎么删除(最新)

在更新了最新的 nvidia后原来的隐藏鼠标右键菜单后不行了&#xff0c;新方法如下&#xff1a; 步骤一&#xff1a;在键盘“WINR”键同时操作下&#xff0c;启动运行框&#xff0c;在框内输入“regedit”&#xff0c;打开深度系统win7 的注册表编辑器。 步骤二&#xff1a;为防…

Spring学习|Bean的作用域、自动装配Bean、注解实现自动装配、Spring注解开发

Bean的作用域 单例模式(Spring默认机制) 原型模式:每次从容器中get的时候&#xff0c;都会产生一个新对象! Bean的自动装配 自动装配是Spring满足bean依赖一种方式! Spring会在上下文中自动寻找&#xff0c;并自动给bean装配属性! 在Spring中有三种装配的方式 1.在xml中显示的…

在字节跳动和拼多多干了5年测试,熬夜总结出来的划水经验....

先简单交代一下背景吧&#xff0c;某不知名 985 的本硕&#xff0c;17 年毕业加入字节&#xff0c;之后跳槽到了拼多多&#xff0c;一直从事软件测试的工作。之前没有实习经历&#xff0c;算是5年的工作经验吧。 这5年之间完成了一次晋升&#xff0c;换了一家公司&#xff0c;…

java八股文面试[数据库]——MySQL死锁的原因和处理方法

1) 表的死锁 产生原因: 用户A访问表A&#xff08;锁住了表A&#xff09;&#xff0c;然后又访问表B&#xff1b;另一个用户B访问表B&#xff08;锁住了表B&#xff09;&#xff0c;然后企图访问表A&#xff1b;这时用户A由于用户B已经锁住表B&#xff0c;它必须等待用户B释放…

ZooInspector

一、在window&#xff0c;使用我们先打开Zookeeper,目录bin下的zkServer.cmd&#xff0c;把Zookeeper运行起来 ​编辑https://img.111com.net/attachment/art/187687/5f0c25fbe580c.png 二、可以使用目录bin下的zkCli.cmd&#xff0c;查询Zookeeper数据的方式&#xff0c;但是…

大模型浪潮席卷!和鲸科技入选创业邦“2023值得关注的125家AIGC企业”榜单

为以专业视角发掘现阶段积极探索 AIGC 技术和应用的企业&#xff0c;对接企业与场景&#xff0c;推动技术落地&#xff0c;创业邦于近日发布了“2023 值得关注的 125 家 AIGC 企业”榜单&#xff0c;和鲸科技凭借旗下 ModelWhale 数据科学协同平台在承载气象大模型的构建与应用…

企业怎么选择广告片的投放途径

TVC广告片和宣传片都是常见的企业营销工具&#xff0c;每种形式都有各自的特点和适用场景。选择哪种形式更适合企业投放&#xff0c;取决于多个因素&#xff0c;包括目标受众、营销目标、预算和信息传递需求等。接下来由深圳TVC广告片制作公司老友记小编从以下几个方面介绍它们…

【Python报错解决方案】DeprecationWarning: `np.float` is a deprecated

报错amoconda libsite-packages sklearn linear model least ngl.py30: Deprecationmarming: mp.float is a deprecated alias for the builtinfloat , To silence this warning, wse flat by itself. Doing this will not modify amy behavior and is safe. If you specifical…

机器学习基础算法--回归类型和评价分析

目录 1.数据归一化处理 2.数据标准化处理 3.Lasso回归模型 4.岭回归模型 5.评价指标计算 1.数据归一化处理 """ x的归一化的方法还是比较多的我们就选取最为基本的归一化方法 x(x-x_min)/(x_max-x_min) """ import numpy as np from sklea…

python超详细基础教程:元组和集合

前言 嗨喽~大家好呀&#xff0c;这里是魔王呐 ❤ ~! 元组和集合是Python中的基本类型 python更多源码/资料/解答/教程等 点击此处跳转文末名片免费获取 一&#xff0c;元组 元组&#xff08;tuple&#xff09;由小括号、逗号和数据对象构成的集合&#xff0c;各个项通过逗号…