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

目录

一、服务端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函数。

NAMElisten - listen for connections on a socketSYNOPSIS#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函数。

NAMEaccept, accept4 - accept a connection on a socketSYNOPSIS#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函数。

NAMEconnect - initiate a connection on a socketSYNOPSIS#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.mzph.cn/diannao/613.shtml

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

相关文章

CentOS7下安装mysql8或者mysql5.7

mysql8 1、下载 访问mysql官网下载mysql8软件包 https://dev.mysql.com/downloads/mysql/ 选择相应的版本如&#xff1a;RPM Bundle mysql-8.0.33-1.el7.x86_64.rpm-bundle.tar RPM Bundle 8.0.33 下载地址&#xff1a;https://dev.mysql.com/get/Downloads/MySQL-8.0/mysql-8.…

Android的一些总结

先打开自定义的app显示欢迎->消失 打开桌面应用程序->在桌面应用程序中也要能一键启动打开视频播放的app 桌面应用程序广播接收者进行监听&#xff0c;然后打开服务/activity是可行的。 ########################## 日志&#xff0c;调试&#xff1a; Usb 无线 串口…

Vue3从入门到实战:深度了解相关API

shallowRef 作用&#xff1a;创建一个响应式数据&#xff0c;但只对顶层属性进行响应式处理。 用法&#xff1a; let myVar shallowRef(initialValue); 特点&#xff1a;只跟踪引用值的变化&#xff0c;不关心值内部的属性变化。 shallowReactive 作用&#xff1a;创建一个…

【MySQL】表的基本约束

文章目录 1、约束类型1.1NOT NULL约束1.2UNIQUE&#xff1a;唯一约束1.3DEFAULT&#xff1a;默认值约束1.4PRIMARY KEY&#xff1a;主键约束1.5FOREIGN KEY&#xff1a;外键约束 2、表的设计2.1一对一2.2一对多2.3多对多 1、约束类型 关键字解释NOT NULL指示某列不能存储NULL值…

在线编辑器 CodeMirror

如何优雅的在网页显示代码 如果开发在线编辑器 引入资源&#xff1a; <link rel"stylesheet" href"https://cdnjs.cloudflare.com/ajax/libs/codemirror/5.60.0/codemirror.min.css"><script src"https://cdnjs.cloudflare.com/ajax/libs/c…

力扣经典150题第三十题:长度最小的子数组

目录 力扣经典150题解析之三十&#xff1a;长度最小的子数组1. 介绍2. 问题描述3. 示例4. 解题思路方法一&#xff1a;滑动窗口 5. 算法实现6. 复杂度分析7. 测试与验证测试用例设计测试结果分析 8. 进阶9. 总结10. 参考文献感谢阅读 力扣经典150题解析之三十&#xff1a;长度最…

Web前端 Javascript笔记6

BOM 前面的笔记讲的都是DOM&#xff08;文档对象模型&#xff09;&#xff0c;DOM几乎被所有浏览器支持&#xff0c;是DOM的作用为操作HTML文档的重要手段。利用DOM可以对HTML文档中的所有元素&#xff0c;节点进行获取与访问&#xff0c;对标签属性与样式进行设置。 下面是一…

OpenHarmony鸿蒙南向开发案例:【智能窗户通风设备】

样例简介 本文档介绍了安全厨房案例中的相关智能窗户通风设备&#xff0c;本安全厨房案例利用轻量级软总线能力&#xff0c;将两块欧智通V200Z-R/BES2600开发板模拟的智能窗户通风设备和燃气告警设备组合成。当燃气数值告警时&#xff0c;无需其它操作&#xff0c;直接通知软总…

Targeted influence maximization in competitive social networks

abstract 利用口碑效应的广告对于推销产品是相当有效的。在过去的十年中&#xff0c;人们对营销中的影响力最大化问题进行了深入的研究。影响力最大化问题旨在将社交网络中的一小群人识别为种子&#xff0c;最终他们将引发网络中最大的影响力传播或产品采用。在网络营销的实际场…

C++ 继承(一)

一、继承的概念 继承是面向对象编程中的一个重要概念&#xff0c;它指的是一个类&#xff08;子类&#xff09;可以从另一个类&#xff08;父类&#xff09;继承属性和方法。子类继承父类的属性和方法后&#xff0c;可以直接使用这些属性和方法&#xff0c;同时也可以在子类中…

DLT 直接线性变换

DLT 直接线性变换 对于单应变换 x i ′ H x i x_i^{\prime}Hx_i xi′​Hxi​&#xff0c;易知两图中对应的特征点&#xff0c;如何找出所需要的 H H H​&#xff0c;为了解决这个问题&#xff0c;可以采用DLT算法 原理 其中采用Least Squares Error去拟合 其中目标是获得最佳…

【办公类-22-05】20240419 UIBOT填写“PATHS课程”的《SSBS校园行为问卷》

背景需求&#xff1a; 每年都有一个PATHS课程的“家长问卷调查”和“教师问卷调查”需要填写 作为教师&#xff0c;每次要对全班所有的孩子进行评价&#xff0c;每位孩子64题&#xff01; 反复点题目&#xff0c;感觉非常累&#xff0c;工作操作就是两位老师&#xff0c;每人做…

Golang | Leetcode Golang题解之第25题K个一组翻转链表

题目&#xff1a; 题解&#xff1a; func reverseKGroup(head *ListNode, k int) *ListNode {hair : &ListNode{Next: head}pre : hairfor head ! nil {tail : prefor i : 0; i < k; i {tail tail.Nextif tail nil {return hair.Next}}nex : tail.Nexthead, tail my…

U盘秒变0字节?别慌,数据恢复有妙招!

在日常的工作和生活中&#xff0c;U盘已成为我们不可或缺的数据存储工具。然而&#xff0c;有时候我们可能会遇到一个令人头疼的问题&#xff1a;原本存有重要文件的U盘&#xff0c;突然间容量显示为0字节。这意味着U盘中的数据全部丢失&#xff0c;无法读取。那么&#xff0c;…

hackthebox - Redeemer

2024.4.19 TASK 1 Which TCP port is open on the machine? 6379 TASK 2 Which service is running on the port that is open on the machine? redis TASK 3 What type of database is Redis? Choose from the following options: (i) In-memory Database, (ii) Traditiona…

UltraScale+的10G/25G Ethernet Subsystem IP核使用

文章目录 前言一、设计框图1.1、xxv_ethernet_01.2、xxv_ethernet_0_sharedlogic_wrapper1.3、xxv_ethernet_0_clocking_wrapper1.4、xxv_ethernet_0_common_wrapper 二、IP核配置三、仿真四、上板测速 前言 前面我们学习了很多基于XILINX 7系列的高速接口使用&#xff0c;本文…

组合预测 | Matlab实现ICEEMDAN-SMA-SVM基于改进完备集合经验模态分解-黏菌优化算法-支持向量机的时间序列预测

组合预测 | Matlab实现ICEEMDAN-SMA-SVM基于改进完备集合经验模态分解-黏菌优化算法-支持向量机的时间序列预测 目录 组合预测 | Matlab实现ICEEMDAN-SMA-SVM基于改进完备集合经验模态分解-黏菌优化算法-支持向量机的时间序列预测预测效果基本介绍程序设计参考资料预测效果 基本…

爬取微博评论数据

# -*- coding: utf-8 -*- import requests #用于发送请求并且拿到源代码 from bs4 import BeautifulSoup #用于解析数据 1.找到数据源地址并且分析链接 2.发送请求并且拿到数据 3.在拿到的数据中解析出需要的数据 4.存储数据 headers { "User-Agent": "…

C++中string的用法总结+底层剖析

前言&#xff1a;在C语言中&#xff0c;我们经常使用字符串进行一系列操作&#xff0c;经常使用的函数如下&#xff1a;增删改查 &#xff08;自己造轮子&#xff09;&#xff0c;C中设计出string容器&#xff0c;STL库中为我们提供了以上函数&#xff0c;所以我们使用string容…

QTableView获取可见的行数

场景 当我们需要实时刷新QTableView时&#xff0c;而此时tableView的数据量较大&#xff0c;如果全部刷新显然不合理&#xff0c;如果可以只对用户看的到的数据进行刷新那就最好了&#xff0c;经过一番摸索找到了几种方式&#xff0c;可供参考 代码 方法1 QVector<int>…