网络(一)

目录

1. 网络基础(一);

2. 网络套接字;

3. TCP实现;


1. 网络基础(一)

1.1 网络发展:

        从一个个计算器都是独立的, 到计算机连接起来进行数据共享, 后期计算机数量很多通过交换器和路由器进行传输(局域网). 广域网就是世界各个计算器进行数据共享, 也是由一个个局域网组成.

 1.2 初始协议

就是一种约定, 网络之间数据流通就是依据一套协议的.

 1.3 协议分层:

        每一层进行数据的处理, 分层处理比较好处理数据, 以及维护为一层即可, 代码健壮性好, 鲁棒性好.

OSI七层协议: 应用层, 表示层, 会话层, 传输层, 网络层, 数据链路层, 物理层.

TCP/IP协议:  应用层, 传输层, 网络层, 数据链路层, 物理层.

 1.4 网络传输基本流程:

        传输通过协议的每一层进行传递. 应用层->传输层->网络层->数据链路层->物理层. 到达对端的物理层向上交付.

 2. 网络套接字:

2.1 源ip地址和目的ip地址:

        由于通信时候一台主机需要传递信息给对端主机, 就需要找到对端主机的ip地址, 而且本主机找到对端主机是否收到, 传递回来的信息也需要源ip地址来响应回来信息. 始终不会改变的.

2.2 源mac地址和目的mac地址:

        数据链路层中的报头中, mac地址只在局域网中有效, 如果出局域网(路由器进行转接)那么就会改变源mac地址和目的mac地址.路由器会将源mac地址报头换成新的报头. 会根据局域网变化改变.

 2.3 端口号:

        找到对端主机之后, 不仅是信息的交流还有完成某种服务, 那么这样的服务就是一个进程, 标志进程在网络中使用的就是端口号. 本质就是向进程发起请求. 用来标识一台主机的进程. 是属于传输层的内容. 包含2字节16位的整数, 一个端口号只能标识一个进程, 但是一个进程可以被多个端口号标识.

         PID也可以标识进程,为啥不用这个呢?

   这个相当于PID是用来操作系统里面标识进程的唯一性, 而PORT是在网络中标识进程的唯一性, 操作系统中有一部分是不需要进行网络通信的, 这样场景就不太合适. 在不同场景中PID和PORT是具有自己的合适场景.

 2.4 网络字节序:

        网络中也存在大小端的问题, 会导致数据读取时候不正确, 导致数据解析时候错误. 这时候大小端很重要辣.

大端: 数据的高字节位保存在内存的低地址位置, 数据的低字节位保存在内存的高地址位置. 

小端: 数据的低字节位保存在内存的低地址位置, 数据的高字节位保存在内存的高地址位置. 

举例: 0x12345:

所以在网络中规定数据流采用大端字节序. 如果发送端是小端要变大端再发送. 接收端如果是小端就要将在网络中大端的变成小段给接收端读取.

介绍几个网络字节序和主机字节序转换的函数:

h代表主机端字节序, n代表网络端字节序, l是32位4字节的长度, s是16位2字节的长度.

2.5 socket编程接口:

(1) 创建套接字:

        domain是协议家族, 本地通信填AF_UNIX, IPV4填AF_INET; IPV6填AF_INET6;

        type是服务类型, tcp是使用SOCK_STREAM或者udp是使用SOCK_DGRAM.

        protocol协议类别: 一般填0就是默认.

        创建成功就是返回正数, 失败返回-1.

(2) 绑定端口号:

        sockfd是套接字的标识, addr是网络相关的属性信息, addrlen是addr结构体的长度.

成功就是正数, 失败为-1, 错误码被标志.

(3) 监听套接字:

(4) 接收套接字:

(5) 建立连接:

2.6 sockaddr结构:

        套接字不仅可以本地通信还可以网络通信, sockaddr_in由于网络通信, sockaddr_un用于本地通信. sockaddr可以用于本地和网络通信;

sockaddr属于什么类型接口?

        我们是在应用层进行程序编写, 然后就是调用的接口就是底层操作系统的系统接口.

sockaddr底层做什么的?

        sockaddr是被进程调用的, 然后进程会维护一个进程地址空间PCB, 以及文件描述符.

在pcb中的文件队列中进行打开具体网络文件. 每个文件又有文件缓冲区, 磁盘中读取文件, 操作系统会定期从文件缓冲区中读取数据到网卡中.

 2.7 简单UDP实现:

(1) 初始化和析构:

        构造函数创建套接字, 以及使用析构函数关闭套接字.

class UdpServer
{
public:bool InitServer(){//创建套接字_sockfd = socket(AF_INET, SOCK_DGRAM, 0);if (_sockfd < 0){ //创建套接字失败std::cerr << "socket error" << std::endl;return false;}std::cout << "socket create success, sockfd: " << _sockfd << std::endl;return true;}~UdpServer(){if (_sockfd >= 0){close(_sockfd);}};
private:int _sockfd; //文件描述符
};
(2) 服务器绑定:

        因为套接字创建好无法找到数据是写入磁盘还是网卡中, 进行绑定.

绑定的时候需要将ip地址和网络文件告诉网络文件, 改变文件对应指向网卡, 然后就是将文件和网卡进行绑定起来.

字符串ip转整数ip:

整数ip转字符串ip:

class UdpServer
{
public:UdpServer(std::string ip, int port):_sockfd(-1),_port(port),_ip(ip){};bool InitServer(){//创建套接字_sockfd = socket(AF_INET, SOCK_DGRAM, 0);if (_sockfd < 0){ //创建套接字失败std::cerr << "socket error" << std::endl;return false;}std::cout << "socket create success, sockfd: " << _sockfd << std::endl;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(_sockfd, (struct sockaddr*)&local, sizeof(sockaddr)) < 0){cout << "bind error!" << endl;return false;}cout << "bind success" << endl;return true;}~UdpServer(){if (_sockfd >= 0){close(_sockfd);}};
private:int _sockfd; //文件描述符int _port;std::string _ip;
};
(3) 运行服务器:

ssize_t recvfrom(int sockfd, void *buf, size_t len, int flags, struct sockaddr *src_addr, socklen_t *addrlen);
sockfd: 套接字, buf是读取数据存放的位置, len是读取的长度, flag是读取的方式, 0是阻塞读取, src_addr是网络相关的属性信息, addrlen是读取src_addr的长度.

可以获取对端主机的IP以及端口号.

 void Start(){
#define SIZE 128char buffer[SIZE];for(;;){struct sockaddr_in peer;socklen_t len = sizeof(peer);ssize_t size = recvfrom(_sockfd, &buffer, sizeof(buffer)-1, 0, (sockaddr*)&peer, &len);if(size > 0){buffer[size] = '\0';int port = ntohs(peer.sin_port);string ip = inet_ntoa(peer.sin_addr);cout << ip << ":" << port << "#" << buffer << endl;}else{cerr << "recvfrom fail" << endl;}}}

netstat指令: 可以查看网络状况:

-n: 直接使用ip地址.

-u: 监控udp;

-t: 监控tcp;

-l:显示监控中的服务器的Socket

-p: 显示正在使用Socket的程序识别码和程序名称

 (4) 客户端初始化:

        

 


class UdpClient
{
public:UdpClient(std::string server_ip, int server_port):_sockfd(-1),_server_port(server_port),_server_ip(server_ip){}~UdpClient(){if (_sockfd >= 0){close(_sockfd);}}
private:int _sockfd; //文件描述符int _server_port; //服务端端口号std::string _server_ip; //服务端IP地址
};
(5) 客户端绑定问题:

        服务器需要绑定端口号, ip地址, 因为服务器需要让别人找到它的ip和端口号去访问服务, 客户端却不需要, 客户端在访问服务器时候只要唯一即可, 不需要绑定. 如果客户端绑定端口号会导致端口号只能给这个客户端其他的无法使用, 那别的客户端如何使用只能等这个客户端使用. 所以非常不好, 操作系统会给客户端分配端口号来标识唯一性.

 (6) 客户端发送数据:

ssize_t sendto(int sockfd, const void *buf, size_t len, int flags, const struct sockaddr *dest_addr, socklen_t addrlen);
sockfd:套接字; buf: 待写入数据的存放位置, len: 写入数据的大小, flag写入方式,

dest_addr:对端网络相关的属性信息; addrlen: 对端属性大小.

class UdpClient
{
public:void Start(){std::string msg;struct sockaddr_in peer;memset(&peer, '\0', sizeof(peer));peer.sin_family = AF_INET;peer.sin_port = htons(_server_port);peer.sin_addr.s_addr = inet_addr(_server_ip.c_str());for (;;){std::cout << "Please Enter# ";getline(std::cin, msg);sendto(_sockfd, msg.c_str(), msg.size(), 0, (struct sockaddr*)&peer, sizeof(peer));}}
private:int _sockfd; //文件描述符int _server_port; //服务端端口号std::string _server_ip; //服务端IP地址
};
 (7) INADDR_ANY:

        如果让服务器被外网访问, 就需要使用INADDR_ANY, 因为我们的ip不是真正的公网ip, 如果使用INADDR_ANY就会如下图给服务器.

class UdpServer
{
public:bool InitServer(){//创建套接字_sockfd = socket(AF_INET, SOCK_DGRAM, 0);if (_sockfd < 0){ //创建套接字失败std::cerr << "socket error" << std::endl;return false;}std::cout << "socket create success, sockfd: " << _sockfd << std::endl;//填充网络通信相关信息struct sockaddr_in local;memset(&local, '\0', sizeof(local));local.sin_family = AF_INET;local.sin_port = htons(_port);local.sin_addr.s_addr = INADDR_ANY; //绑定INADDR_ANY//绑定if (bind(_sockfd, (struct sockaddr*)&local, sizeof(sockaddr)) < 0){ //绑定失败std::cerr << "bind error" << std::endl;return false;}std::cout << "bind success" << std::endl;return true;}
private:int _sockfd; //文件描述符int _port; //端口号std::string _ip; //IP地址
};
(8) 回声服务器:

        就是服务器多一个返回响应的消息, sendto. 客户端接收这个响应. recvfrom.

//服务器:
void Start()
{
#define SIZE 128char buffer[SIZE];for (;;){struct sockaddr_in peer;socklen_t len = sizeof(peer);ssize_t size = recvfrom(_sockfd, buffer, sizeof(buffer)-1, 0, (struct sockaddr*)&peer, &len);if (size > 0){buffer[size] = '\0';int port = ntohs(peer.sin_port);std::string ip = inet_ntoa(peer.sin_addr);std::cout << ip << ":" << port << "# " << buffer << std::endl;}else{std::cerr << "recvfrom error" << std::endl;}std::string echo_msg = "server get!->";echo_msg += buffer;sendto(_sockfd, echo_msg.c_str(), echo_msg.size(), 0, (struct sockaddr*)&peer, len);}
}//客户端:
void Start()
{std::string msg;struct sockaddr_in peer;memset(&peer, '\0', sizeof(peer));peer.sin_family = AF_INET;peer.sin_port = htons(_server_port);peer.sin_addr.s_addr = inet_addr(_server_ip.c_str());for (;;){std::cout << "Please Enter# ";getline(std::cin, msg);sendto(_sockfd, msg.c_str(), msg.size(), 0, (struct sockaddr*)&peer, sizeof(peer));#define SIZE 128char buffer[SIZE];struct sockaddr_in tmp;socklen_t len = sizeof(tmp);ssize_t size = recvfrom(_sockfd, buffer, sizeof(buffer)-1, 0, (struct sockaddr*)&tmp, &len);if (size > 0){buffer[size] = '\0';std::cout << buffer << std::endl;}}
}

3. TCP

3.1 实现:

(1) 服务器创建套接字:

        在sock创建时候, type是SOCK_STREAM不是SOCK_DGRAM.

class TcpServer
{
public:void InitServer(){//创建套接字_sock = socket(AF_INET, SOCK_STREAM, 0);if (_sock < 0){std::cerr << "socket error" << std::endl;exit(2);}}~TcpServer(){if (_sock >= 0){close(_sock);}}
private:int _sock; //套接字
};
(2) 服务器绑定:

        和udp差不多. 绑定有时候失败就是由于资源还没有释放干净或者端口号被其他进程绑定了.还有就是1024一下的端口都被绑定, 尽量绑定8080以上的端口号这些. 还有就是云服务器没有开发端口. 

class TcpServer
{
public:TcpServer(int port): _sock(-1), _port(port){}void InitServer(){//创建套接字_sock = socket(AF_INET, SOCK_STREAM, 0);if (_sock < 0){std::cerr << "socket error" << std::endl;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 = INADDR_ANY;if (bind(_sock, (struct sockaddr*)&local, sizeof(local)) < 0){std::cerr << "bind error" << std::endl;exit(3);}}~TcpServer(){if (_sock >= 0){close(_sock);}}
private:int _sock; //监听套接字int _port; //端口号
};
(3) 服务器监听:

        TCP进行绑定之后就是进行连接, 连接成功才可以通信, 就是要进行监听连接状况.

sockfd: 监听套接字, backlog就是全连接队列, 多个客户端同时进行连接请求, 就会被放到全连接队列中, 参数代表最大连接个数. 成功就是返回0, 失败返回-1.

#define BACKLOG 5class TcpServer
{
public:void InitServer(){//创建套接字_listen_sock = socket(AF_INET, SOCK_STREAM, 0);if (_listen_sock < 0){std::cerr << "socket error" << std::endl;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 = INADDR_ANY;if (bind(_listen_sock, (struct sockaddr*)&local, sizeof(local)) < 0){std::cerr << "bind error" << std::endl;exit(3);}//监听if (listen(_listen_sock, BACKLOG) < 0){std::cerr << "listen error" << std::endl;exit(4);}}
private:int _listen_sock; //监听套接字int _port; //端口号
};
(4) 服务器获取连接:

        sockfd:监听套接字, addr: 对端网络相关的属性信息, addrlen: 网络属性大小. 成功返回对端套接字fd, 失败为-1.

返回的套接字和监听套接字有啥不同?

        监听套接字获取客户端的连接, 连接套接字是真正提供服务的套接字后期使用的也是这个.

class TcpServer
{
public:void Start(){for (;;){//获取连接struct sockaddr_in peer;memset(&peer, '\0', sizeof(peer));socklen_t len = sizeof(peer);int sock = accept(_listen_sock, (struct sockaddr*)&peer, &len);if (sock < 0){std::cerr << "accept error, continue next" << std::endl;continue;}std::string client_ip = inet_ntoa(peer.sin_addr);int client_port = ntohs(peer.sin_port);std::cout<<"get a new link->"<<sock<<" ["<<client_ip<<"]:"<<client_port<<std::endl;}}
private:int _listen_sock; //监听套接字int _port; //端口号
};

telnet命令: 连接一个服务器;

 (5) 服务器处理请求:

        进行读取数据, fd:文件描述符, accept返回值就是这个, buf是读取数据放在的地点, count是数据个数. 返回值> 0代表读取到数据, 返回值 == 0代表对端关闭, 返回值<0代表读取失败.

这里读完需要关闭套接字, 防止内存泄漏.

        将读取到的数据进行写入数据,buf是写入的数据, count写入的大小.

 (6) 客户端创建套接字:

        

class TcpClient
{
public:TcpClient(std::string server_ip, int server_port): _sock(-1), _server_ip(server_ip), _server_port(server_port){}void InitClient(){//创建套接字_sock = socket(AF_INET, SOCK_STREAM, 0);if (_sock < 0){std::cerr << "socket error" << std::endl;exit(2);}}~TcpClient(){if (_sock >= 0){close(_sock);}}
private:int _sock; //套接字std::string _server_ip; //服务端IP地址int _server_port; //服务端端口号
};
(6) 客户端连接服务器:

        sockfd:是套接字, addr: 对端网络相关的属性信息, addrlen: 是网络属性大小. 服务器发起监听和获取连接, 就是捕捉客户端连接, 以及建立连接. 成功返回0, 失败返回-1.

class TcpClient
{
public:void Start(){struct sockaddr_in peer;memset(&peer, '\0', sizeof(peer));peer.sin_family = AF_INET;peer.sin_port = htons(_server_port);peer.sin_addr.s_addr = inet_addr(_server_ip.c_str());if (connect(_sock, (struct sockaddr*)&peer, sizeof(peer)) == 0){ //connect successstd::cout << "connect success..." << std::endl;Request(); //发起请求}else{ //connect errorstd::cerr << "connect failed..." << std::endl;exit(3);}}
private:int _sock; //套接字std::string _server_ip; //服务端IP地址int _server_port; //服务端端口号
};
(7) 客户端发起请求:

        先进行写入数据给服务器, 然后在进行将数据客户端读取一次.

class TcpClient
{
public:void Request(){std::string msg;char buffer[1024];while (true){std::cout << "Please Enter# ";getline(std::cin, msg);write(_sock, msg.c_str(), msg.size());ssize_t size = read(_sock, buffer, sizeof(buffer)-1);if (size > 0){buffer[size] = '\0';std::cout << "server echo# " << buffer << std::endl;}else if (size == 0){std::cout << "server close!" << std::endl;break;}else{std::cerr << "read error!" << std::endl;break;}}}void Start(){struct sockaddr_in peer;memset(&peer, '\0', sizeof(peer));peer.sin_family = AF_INET;peer.sin_port = htons(_server_port);peer.sin_addr.s_addr = inet_addr(_server_ip.c_str());if (connect(_sock, (struct sockaddr*)&peer, sizeof(peer)) == 0){ //connect successstd::cout << "connect success..." << std::endl;Request(); //发起请求}else{ //connect errorstd::cerr << "connect failed..." << std::endl;exit(3);}}
private:int _sock; //套接字std::string _server_ip; //服务端IP地址int _server_port; //服务端端口号
};

上述tcp的弊端:

        只允许一个客户端进行消息交流, 另外一个客户端只能等上个客户端退出才能进行数据交流.

客户端为什么会显示连接成功?

        因为在进行accept的时候连接放在全连接队列中, 但是还没有被使用.

 (8) 多进程版TCP:

        父进程创建后, 子进程继承父进程的连接服务. 父进程就可以继续进行监听连接, 子进程进行连接服务即可. 由于父进程需要等待子进程退出不然就僵尸进程., 内存泄漏. wait进行迭代子进程. 其实也可以不需要等待子进程,  子进程创建孙子进程, 孙子进程来提供网络服务.

爷爷进程: 获取客户端连接请求;

爸爸进程: 爷爷调用fork创建的进程;

孙子进程: 爸爸进程fork出来的进程, 来实现网络服务的.

爸爸进程创建孙子进程就退出, 那么爷爷进程就等待成功, 爷爷进程可以接着进程accept. 

不用等待孙子进程退出, 操作系统可以系统回收孤儿进程.

父子进程之间的文件描述符是互相不干扰的, 爷爷进程创建爸爸进程之后就可以关闭文件描述符了, 爸爸进程创建孙子进程之后也要关闭. 关闭才不会导致资源泄漏.

 

void Start(){for(;;){struct sockaddr_in peer;memset(&peer, '\0', sizeof(peer));socklen_t len = sizeof(peer);int sock = accept(_listen_sock, (struct sockaddr*)&peer, &len);if(sock < 0){cerr << "accept fail!" << endl;continue;}string client_ip = inet_ntoa(peer.sin_addr);int client_port = ntohs(peer.sin_port);cout << "get a new link->" << sock << "[" << client_ip << "]:" << client_port << endl;pid_t id = fork();//创建爸爸进程;if(id == 0){close(_listen_sock);//关闭爷爷文件描述符;if(fork() > 0){exit(0);//爸爸进程退出;}Service(sock, client_ip, client_port);//孙子进程进行网络服务;exit(0);//孙子进程提供完服务退出.}close(sock); //关闭爸爸文件描述符;waitpid(id, nullptr, 0);//等待爸爸进程退出.}}
(9) 多线程版TCP:

        创建和维护进程的成本比较高, 线程比较少些, 因为线程本质在进程地址空间运行, 共享大部分的资源, 可以使用线程来提供网络服务. 可以不用等待线程结束, 使用线程分离, 然后accept其他客户端. 共享同一张文件描述符. 但是新线程不知道客户端对应哪个文件描述符, 所以主线程需要告诉它.

        由于是共用一张文件描述符表, 主线程无法关闭文件描述符, 只有新线程使用完才可以.

 

static void* HandlerRequest(void* arg){pthread_detach(pthread_self());Param* p = (Param*)arg;Service(p->_sock, p->_ip, p->_port);delete p;//析构参数申请的堆空间.return nullptr;}void Start(){for(;;){struct sockaddr_in peer;memset(&peer, '\0', sizeof(peer));socklen_t len = sizeof(peer);int sock = accept(_listen_sock, (struct sockaddr*)&peer, &len);if(sock < 0){cerr << "accept fail!" << endl;continue;}string client_ip = inet_ntoa(peer.sin_addr);int client_port = ntohs(peer.sin_port);cout << "get a new link->" << sock << "[" << client_ip << "]:" << client_port << endl;Param* p = new Param(sock, client_ip, client_port);pthread_t tid;pthread_create(&tid, nullptr, HandlerRequest, p);}}

3.2 TCP英翻汉服务器:

        前面有多线程版本的TCP, 改变一下Headler即可.就可以是一个TCP英翻汉服务器.

class Handler
{
public:Handler(){}~Handler(){}void operator()(int sock, std::string client_ip, int client_port){unordered_map<string, string> dict;dict.insert(make_pair("hello", "你好"));dict.insert(make_pair("world", "世界"));dict.insert(make_pair("love", "爱"));char buffer[1024];string value;while(true){size_t size = read(sock, buffer, sizeof(buffer)-1);if(size > 0){buffer[size] = '\0';cout << client_ip << ":" << client_port << "#" << buffer << endl;string key = key;auto it = dict.find(key);if(it != dict.end()){value = it->second;}else{value = key;}write(sock, value.c_str(), value.size());}else if(size == 0){cout << client_ip << ":" << client_port << "close!" << endl;break;}else{cerr << sock << "read fail!" << endl;break;}}close(sock);cout << client_ip << ":" << client_port << "Service done!" << endl;}
};

 3.3 TCP通信流程:

        TCP客户端和服务器端的通信图:

 (1) 三次握手:

        服务器进程创建套接字, 绑定, 监听套接字的初始化, 就可以开始accpet阻塞获取客户端连接, 客户端进行创建套接字, 然后进行connection, 向服务器发起第一次连接请求, 发起SYN阻塞等待服务器应答, 服务器收到客户端的SYN就会响应SYN+ACK(接收到请求以及同意连接)为第二次握手, 客户端收到服务器的响应给服务器一个应答(ACK)为第三次握手.

 

(2) 数据传输:

        客户端发起数据请求, 服务器接收到返回响应然后数据可以一同发送过去, 或者应答发出后再发服务器数据, 其次客户端给出服务器的应答.

        服务器调用accpet之后进行read读取数据, 客户端进行写入数据, 服务器收到后进行响应, 处理网络服务后再write返回数据客户端,  客户端再进行read读取服务器的响应.

 (3) 四次挥手:

        客户端没有更多的请求了就会发送服务器FIN断开连接请求, 调用close进行关闭为第一次挥手, 其次就是服务器接收到客户端FIN请求进行响应ACK同意断开, 并且将read置0标识对端关闭为第二次挥手,  read返回后客户端也知道自己要关闭连接,  给客户端发送FIN断开请求为第三次挥手, 客户端接收之后就返回给服务器ACK为第四次挥手.

断开连接, 目的就是释放资源, 因为维护连接也需要系统资源, 连接多了不断开释放就会内存泄漏. 资源浪费.

 3.4 TCP对比UDP:

TCP: 可靠,面向连接和字节流的. 

UDP: 不可靠, 面向无连接和数据报.

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

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

相关文章

风光并网对电网电能质量影响的matlab/simulink仿真建模

这个课题早在一几年的时候比较热门&#xff0c;之前作电科院配电网的一个项目中也有所涉及&#xff0c;我把其中一部分经典仿真模型思路分享给大家&#xff0c;电能质量影响这部分&#xff0c;我在模型中主要体现的就是不同容量的光伏、风电接入&#xff0c;对并网点的电压影响…

【深度学习入门】深度学习知识点总结

一、卷积 &#xff08;1&#xff09;什么是卷积 定义&#xff1a;特征图的局部与卷积核做内积的操作。 作用&#xff1a;① 广泛应用于图像处理领域。卷积操作可以提取图片中的特征&#xff0c;低层的卷积层提取局部特征&#xff0c;如&#xff1a;边缘、线条、角。 ② 高层…

MySQL(4)多表查询

引言&#xff1a;为什么需要多表的查询&#xff1f; A&#xff1a;提高效率&#xff0c;多线进行。 高内聚、低耦合。 一、多表查询的条件 1、错误的多表查询&#xff1a; SELECT employee_id,department_name FROM employees,departments; SELECT employee_id,department…

PIC单片机HEX文件格式分析

在调试PIC单片机在bootloader程序时&#xff0c;需要将hex文件转换为bin文件&#xff0c;在转换之前先了解一下hex文件中数据是如何定义的。 直接打开一个LED灯闪烁的程序生成的hex文件&#xff0c;芯片型号为PIC18F46K80 可以看到每条数据都是由6部分组成的&#xff0c;下面分…

CANoe Trace窗口

文章目录 一、Trace窗口简介二、Trace窗口打开三、Trace窗口菜单栏介绍1. Detail View2. Statistic View3. Difference view4. Predefined filter5. Analysis filter6. Toggle time mode7. Toggle display mode8. Change font size 四、Trabe窗口配置1. 打开 Trace配置窗口2. 增…

c#配置config文件

1&#xff0c;引用命名空间 Configuration 及配置信息

idea新增java快捷键代码片段

最近在写一些算法题&#xff0c;有很多的List<List这种编写&#xff0c;想着能否自定义一下快捷键 直接在写代码输入&#xff1a;lli&#xff0c;即可看见提示

vim练级攻略(精简版)

vim推荐配置: curl -sLf https://gitee.com/HGtz2222/VimForCpp/raw/master/install.sh -o ./install.sh && bash ./install.sh 0. 规定 Ctrl-λ 等价于 <C-λ> :command 等价于 :command <回车> n 等价于 数字 blank字符 等价于 空格&#xff0c;tab&am…

鸿蒙参考文档和问题记录

本文用于记录鸿蒙使用过程中的问题和相关参考文档 问题记录 1. 兼容性测试套件问题 ActsStartAbilityForResultNoTargetBundleListStageTest套件测试失败&#xff1a;模块FreeInstall 技术资料 1. HarmonyOS应用如何打包HAP并安装到真机 HarmonyOS应用如何打包HAP并安装到真…

NewStar CTF week1 web wp

谢谢皮蛋 做这题之前需要先去学习一些数据库的知识 1 order by 2 1可以理解为输入的id&#xff0c;是一个占位符&#xff0c;按第二列排序用来测试列数&#xff0c;如果没有两列则会报错-1 union select 1,2 -1同样是占位符&#xff0c;union的作用是将注入语句合并到原始语句…

3.CSS的背景

通过CSS背景属性&#xff0c;可以给页面元素添加背景样式。 背景属性可以设置背景颜色、背景图片、背景平铺、背景图片位置、背景图像固定等。 3.1 背景颜色 background-color属性定义了元素的背景颜色 background-color:颜色值&#xff1b; 一般情况下元素背景颜色默认值…

登录认证(4):令牌技术:JWT令牌

如上文所说&#xff08;登录认证&#xff08;1&#xff09;&#xff1a;登录的基本逻辑及实现思路登录&#xff09;&#xff0c;因为 HTTP协议是无状态的协议&#xff0c;我们需要使用会话跟踪技术实现同一会话中不同请求之间的数据共享&#xff0c;但Cookie技术和Session技术都…

2025.1.20——二、buuctf BUU UPLOAD COURSE 1 1 文件上传

题目来源&#xff1a;buuctf BUU UPLOAD COURSE 1 1 目录 一、打开靶机&#xff0c;查看信息 二、解题思路 step 1&#xff1a;上传一句话木马.php文件康康回显 step 2&#xff1a;蚁剑连接 三、小结 一、打开靶机&#xff0c;查看信息 这里提示到了文件会被上传到./uplo…

【玩转全栈】----Django制作部门管理页面

目录 大致效果 BootStrap BootStrap简介 BootStrap配置 BootStrap使用 基本配置 部分代码解释及注意&#xff1a; 用户编辑&#xff1a; 新添数据&#xff1a; 删除数据&#xff1a; 大致效果 我先给个大致效果&#xff0c;基本融合了Django、Bootstrap、css、html等等。 基于D…

新年好(Dijkstra+dfs/全排列)

1135. 新年好 - AcWing题库 思路&#xff1a; 1.先预处理出1,a,b,c,d,e到其他点的单源最短路&#xff0c;也就是进行6次Dijkstra 2.计算以1为起点的这6个数的全排列&#xff0c;哪种排列方式所得距离最小&#xff0c;也可以使用dfs 1.Dijkstradfs #define int long longusing …

Golang之Context详解

引言 之前对context的了解比较浅薄&#xff0c;只知道它是用来传递上下文信息的对象&#xff1b; 对于Context本身的存储、类型认识比较少。 最近又正好在业务代码中发现一种用法&#xff1a;在每个协程中都会复制一份新的局部context对象&#xff0c;想探究下这种写法在性能…

AIGC浪潮下,图文内容社区数据指标体系如何构建?

文章目录 01 案例&#xff1a;以图文内容社区为例实践数据指标体构建02 4个步骤实现数据指标体系构建1. 明确业务目标&#xff0c;梳理北极星指标2. 梳理业务流程&#xff0c;明确过程指标3. 指标下钻分级&#xff0c;构建多层级数据指标体系4. 添加分析维度&#xff0c;构建完…

数据结构:二叉树

目录 一、树型结构 1、基本概念 2、重要概念 3、树的表示形式 二、二叉树 1、概念 2、两种特殊的二叉树 3、二叉树的性质 4、二叉树的存储 5、二叉树的遍历 二叉树的构建 &#xff08;1&#xff09;前序遍历 &#xff08;2&#xff09;中序遍历 &#xff08;3&am…

SpringBoot项目中的异常处理

定义错误页面 SpringBoot 默认的处理异常的机制&#xff1a;SpringBoot 默认的已经提供了一套处理异常的机制。一旦程序中出现了异常 SpringBoot 会像/error 的 url 发送请求。在 springBoot 中提供了一个叫 BasicExceptionController 来处理/error 请求&#xff0c;然后跳转到…

《安富莱嵌入式周报》第349期:VSCode正式支持Matlab调试,DIY录音室级麦克风,开源流体吊坠,物联网在军工领域的应用,Unicode字符压缩解压

周报汇总地址&#xff1a;嵌入式周报 - uCOS & uCGUI & emWin & embOS & TouchGFX & ThreadX - 硬汉嵌入式论坛 - Powered by Discuz! 视频版&#xff1a; 《安富莱嵌入式周报》第349期&#xff1a;VSCode正式支持Matlab调试&#xff0c;DIY录音室级麦克风…