Lesson9.网络基础1
网络协议初识
- 所谓的协议就是人们为了通信的一种约定
- 操作系统要进行协议管理,必然会先描述,再组织
- 协议本质就是软件,软件是可以"分层"
- 协议在设计的时候,就是被层状的划分的,
为什么要划分成为层状结构
- 场景复杂
- 功能解耦(便于人们进行各种维护)
OSI七层模型
局域网中两台主机是可以直接通信的
- 每层都有自己的协议定制方案,
- 每层协议都要有自己的协议报头
- 从上到下交付数据的时候,要添加报头
- 从下到上递交数据的时候,要去掉报头
如何理解报头
- 报头就像快递的快递单号一样,虽然我们不看,但是必须有
- 封装的本质: 添加报头
- 解包: 去掉报头&&展开分析
- 在使用TCP/IP协议的网络中,IP及其向上的协议,看到的报文都是一样的
- 报文是要被封装的,如何解包?
- 决定我们的有效载荷交付给上层的哪一个协议的问题?
- 每一个协议都要考虑,都要有一定的方式解决这两个公共问题
理解源IP地址和目的IP地址
- 把数据哦送到对方的机器不是目的,真正的网络通信: 其实是进程间通信
- IP地址(公网IP),标定了主机的唯一性
- 源IP是固定不变的,目的IP是可以变的
认识端口号
- 端口号是标识特定主机上的网络进程的唯一性
- 端口号是一个2字节16位的整数
- 端口号用来标识一个进程, 告诉操作系统, 当前的这个数据要交给哪一个进程来处理;
- IP地址 + 端口号能够标识网络上的某一台主机的某一个进程;
- 一个端口号只能被一个进程占用
socket编程接口
常见的套接字:
- 域间socket
- 原始socket
- 网络socket
理论上,是三种应用场景,对应的应该是三套接口,不过将所有的接口进行统一
使用网络在两台主机之间进行通信(之间互发信息)
log.hpp
#pragma once#include <iostream>
#include <cstdio>
#include <cstdarg>
#include <ctime>
#include <string>// 日志是有日志级别的
#define DEBUG 0
#define NORMAL 1
#define WARNING 2
#define ERROR 3
#define FATAL 4const char *gLevelMap[] = {"DEBUG","NORMAL","WARNING","ERROR","FATAL"
};// #define LOGFILE "./threadpool.log"// 完整的日志功能,至少: 日志等级 时间 支持用户自定义(日志内容, 文件行,文件名)
void logMessage(int level, const char *format, ...)
{
#ifndef DEBUG_SHOWif(level== DEBUG) return;
#endifchar stdBuffer[1024]; //标准部分time_t timestamp = time(nullptr);snprintf(stdBuffer, sizeof stdBuffer, "[%s] [%ld] ", gLevelMap[level], timestamp);char logBuffer[1024]; //自定义部分va_list args;va_start(args, format);vsnprintf(logBuffer, sizeof logBuffer, format, args);va_end(args);printf("%s%s\n", stdBuffer, logBuffer);
}
Makefile
.PHONY:all
all:udp_client udp_serverudp_client:udp_client.ccg++ -o $@ $^ -std=c++11
udp_server:udp_server.ccg++ -o $@ $^ -std=c++11.PHONY:clean
clean:rm -f udp_client udp_server
udp_client.cc
#include <iostream>
#include <string>
#include <cstring>
#include <unistd.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <netinet/in.h>
#include <memory>
uint16_t serverport = 0;
std::string serverip;static void usage(std::string proc)
{std::cout << "\nUsage: " << proc << " serverIp serverPort\n"<< std::endl;
}// ./udp_client 127.0.0.1 8080
int main(int argc, char *argv[])
{if (argc != 3){usage(argv[0]);exit(1);}// 1.创建套接字int sock = socket(AF_INET, SOCK_DGRAM, 0);if (sock < 0){std::cerr << "socket error" << std::endl;exit(2);}serverport = atoi(argv[2]);serverip = argv[1];// client一般不需要显示的bind指定port,而是让OS自动随机选择(什么时候做的呢?)std::string message;struct sockaddr_in server;memset(&server, 0, sizeof(server));// 清0server.sin_family = AF_INET;server.sin_port = htons(atoi(argv[2]));server.sin_addr.s_addr = inet_addr(argv[1]);char buffer[1024];while(true){//多线程std::cout << "请输入你的信息# ";std::getline(std::cin, message);if(message == "quit") break;// 当client首次发送消息给服务器的时候,OS会自动给client bind他的IP和PORTsendto(sock, message.c_str(), message.size(), 0, (struct sockaddr*)&server, sizeof server);struct sockaddr_in temp;socklen_t len = sizeof(temp);ssize_t s = recvfrom(sock, buffer, sizeof buffer, 0, (struct sockaddr*)&temp, &len);if(s > 0){buffer[s] = 0;std::cout << "server echo# " << buffer << std::endl;}}return 0;
}
udp_server.cc
#include "udp_server.hpp"
#include <memory>
#include <cstdlib>static void usage(std::string proc)
{std::cout << "\nUsage: " << proc << " port\n" << std::endl;
}// ./udp_server ip port //云服务器的问题 bug??
int main(int argc, char *argv[])
{if(argc != 2){usage(argv[0]);exit(1);}// std::string ip = argv[1];uint16_t port = atoi(argv[1]);// 字符串->整数// unique_ptr智能指针std::unique_ptr<UdpServer> svr(new UdpServer(port));svr->initServer();svr->Start();return 0;
}
udp_server.hpp
#ifndef _UDP_SERVER_HPP
#define _UDP_SERVER_HPP#include "log.hpp"
#include <iostream>
#include <unordered_map>
#include <cstdio>
#include <string>
#include <cerrno>
#include <cstring>
#include <cstdlib>
#include <strings.h>
#include <sys/types.h>
#include <queue>// 网络的常用的四个接口
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <unistd.h>#define SIZE 1024class UdpServer
{
public:UdpServer(uint16_t port, std::string ip = "") : _port(port), _ip(ip), _sock(-1){}// 初始化bool initServer(){// 1. 创建套接字_sock = socket(AF_INET, SOCK_DGRAM, 0);if (_sock < 0){logMessage(FATAL, "%d:%s", errno, strerror(errno));exit(2);}// 2. bind: 将用户设置的ip和port在内核中和我们当前的进程强关联struct sockaddr_in local;// sockaddr_in域间套接字bzero(&local, sizeof(local));// 清0local.sin_family = AF_INET;// 服务器的IP和端口未来也是要发送给对方主机的 -> 先要将数据发送到网络!// 主机序列->网络序列local.sin_port = htons(_port);//INADDR_ANY表示让服务器在工作过程中,可以从任意IP中获取数据//inet_add表示将点分十进制字符串(192.168.110.132)风格的IP地址->4字节主机序列->网络序列local.sin_addr.s_addr = _ip.empty() ? INADDR_ANY : inet_addr(_ip.c_str());if (bind(_sock, (struct sockaddr *)&local, sizeof(local)) < 0){logMessage(FATAL, "%d:%s", errno, strerror(errno));exit(2);}logMessage(NORMAL, "init udp server done ... %s", strerror(errno));// donereturn true;}void Start(){// 服务器启动-> 进程 -> 常驻进程 -> 永远在内存中存在,除非挂了!// echo server: client给我们发送消息,我们原封不动返回char buffer[SIZE];for (;;){// 注意:peer是纯输出型参数struct sockaddr_in peer;bzero(&peer, sizeof(peer));// 输入: peer缓冲区大小// 输出: 实际读到的peersocklen_t len = sizeof(peer);ssize_t s = recvfrom(_sock, buffer, sizeof(buffer) - 1, 0, (struct sockaddr *)&peer, &len);if (s > 0){buffer[s] = 0; // 我们目前数据当做字符串uint16_t cli_port = ntohs(peer.sin_port); // 从网络中来的!std::string cli_ip = inet_ntoa(peer.sin_addr); // 4字节的网络序列的IP->本主机的字符串风格的IP,方便显示printf("[%s:%d]# %s\n", cli_ip.c_str(), cli_port, buffer);}// 分析和处理数据,TODO// end. 写回数据sendto(_sock, buffer, strlen(buffer), 0, (struct sockaddr *)&peer, len);}}~UdpServer(){if (_sock >= 0)close(_sock);}private:// 一个服务器,一般必须需要ip地址和port(16位的整数)uint16_t _port;// 端口号std::string _ip;// ip地址int _sock;// 套接字
};
#endif
使用网络在两台主机之间进行通信(交互式的命令)
void Start(){// 服务器启动-> 进程 -> 常驻进程 -> 永远在内存中存在,除非挂了!// echo server: client给我们发送消息,我们原封不动返回char buffer[SIZE];for (;;){// 注意:peer是纯输出型参数struct sockaddr_in peer;bzero(&peer, sizeof(peer));// 输入: peer缓冲区大小// 输出: 实际读到的peersocklen_t len = sizeof(peer);ssize_t s = recvfrom(_sock, buffer, sizeof(buffer) - 1, 0, (struct sockaddr *)&peer, &len);char result[256];char key[64];std::string cmd_echo;if (s > 0){buffer[s] = 0; // 我们目前数据当做字符串uint16_t cli_port = ntohs(peer.sin_port); // 从网络中来的!std::string cli_ip = inet_ntoa(peer.sin_addr); // 4字节的网络序列的IP->本主机的字符串风格的IP,方便显示// 你发过来的字符串是指令 ls -a -l, rm -rm ~if(strcasestr(buffer, "rm") != nullptr || strcasestr(buffer, "rmdir") != nullptr){std::string err_message = "坏人.... ";std::cout << err_message << buffer << std::endl;sendto(_sock, err_message.c_str(), err_message.size(), 0, (struct sockaddr *)&peer, len);continue;}FILE *fp = popen(buffer, "r");if (nullptr == fp){logMessage(ERROR, "popen: %d:%s", errno, strerror(errno));continue;}while (fgets(result, sizeof(result), fp) != nullptr){cmd_echo += result;}fclose(fp);// printf("[%s:%d]# %s\n", cli_ip.c_str(), cli_port, buffer);}// 分析和处理数据,TODO// end. 写回数据sendto(_sock, cmd_echo.c_str(), cmd_echo.size(), 0, (struct sockaddr *)&peer, len);}
使用网络在两台主机之间进行通信(多进程)
makeflie
.PHONY:all
all:udp_client udp_serverudp_client:udp_client.ccg++ -o $@ $^ -std=c++11 -lpthread
udp_server:udp_server.ccg++ -o $@ $^ -std=c++11.PHONY:clean
clean:rm -f udp_client udp_server
log.hpp
#pragma once#include <iostream>
#include <cstdio>
#include <cstdarg>
#include <ctime>
#include <string>// 日志是有日志级别的
#define DEBUG 0
#define NORMAL 1
#define WARNING 2
#define ERROR 3
#define FATAL 4const char *gLevelMap[] = {"DEBUG","NORMAL","WARNING","ERROR","FATAL"
};// #define LOGFILE "./threadpool.log"// 完整的日志功能,至少: 日志等级 时间 支持用户自定义(日志内容, 文件行,文件名)
void logMessage(int level, const char *format, ...)
{
#ifndef DEBUG_SHOWif(level== DEBUG) return;
#endifchar stdBuffer[1024]; //标准部分time_t timestamp = time(nullptr);snprintf(stdBuffer, sizeof stdBuffer, "[%s] [%ld] ", gLevelMap[level], timestamp);char logBuffer[1024]; //自定义部分va_list args;va_start(args, format);vsnprintf(logBuffer, sizeof logBuffer, format, args);va_end(args);printf("%s%s\n", stdBuffer, logBuffer);
}
thread.hpp
#pragma once
#include <iostream>
#include <string>
#include <functional>
#include <cstdio>// typedef std::function<void* (void*)> fun_t;
typedef void *(*fun_t)(void *);class ThreadData
{
public:void *args_;std::string name_;
};class Thread
{
public:Thread(int num, fun_t callback, void *args) : func_(callback){char nameBuffer[64];snprintf(nameBuffer, sizeof nameBuffer, "Thread-%d", num);name_ = nameBuffer;tdata_.args_ = args;tdata_.name_ = name_;}void start(){pthread_create(&tid_, nullptr, func_, (void*)&tdata_);}void join(){pthread_join(tid_, nullptr);}std::string name(){return name_;}~Thread(){}private:std::string name_;fun_t func_;ThreadData tdata_;pthread_t tid_;
};
udp_client.cc
#include <iostream>
#include <string>
#include <cstring>
#include <unistd.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <netinet/in.h>
#include <memory>
#include "thread.hpp"uint16_t serverport = 0;
std::string serverip;static void usage(std::string proc)
{std::cout << "\nUsage: " << proc << " serverIp serverPort\n"<< std::endl;
}static void *udpSend(void *args)
{int sock = *(int *)((ThreadData *)args)->args_;std::string name = ((ThreadData *)args)->name_;std::string message;struct sockaddr_in server;memset(&server, 0, sizeof(server));server.sin_family = AF_INET;server.sin_port = htons(serverport);server.sin_addr.s_addr = inet_addr(serverip.c_str());while (true){std::cerr << "请输入你的信息# "; //标准错误 2打印std::getline(std::cin, message);if (message == "quit")break;// 当client首次发送消息给服务器的时候,OS会自动给client bind他的IP和PORT// sendto表示发送报文sendto(sock, message.c_str(), message.size(), 0, (struct sockaddr *)&server, sizeof server);}return nullptr;
}static void *udpRecv(void *args)
{int sock = *(int *)((ThreadData *)args)->args_;std::string name = ((ThreadData *)args)->name_;char buffer[1024];while (true){memset(buffer, 0, sizeof(buffer));struct sockaddr_in temp;socklen_t len = sizeof(temp);// recvfrom接收解决报文ssize_t s = recvfrom(sock, buffer, sizeof buffer, 0, (struct sockaddr *)&temp, &len);if (s > 0){buffer[s] = 0;std::cout << buffer << std::endl;}}
}// ./udp_client 127.0.0.1 8080
int main(int argc, char *argv[])
{if (argc != 3){usage(argv[0]);exit(1);}int sock = socket(AF_INET, SOCK_DGRAM, 0);if (sock < 0){std::cerr << "socket error" << std::endl;exit(2);}serverport = atoi(argv[2]);serverip = argv[1];std::unique_ptr<Thread> sender(new Thread(1, udpSend, (void *)&sock));std::unique_ptr<Thread> recver(new Thread(2, udpRecv, (void *)&sock));// sender->name();sender->start();recver->start();sender->join();recver->join();close(sock);return 0;
}
udp_server.cc
#include "udp_server.hpp"
#include <memory>
#include <cstdlib>static void usage(std::string proc)
{std::cout << "\nUsage: " << proc << " port\n" << std::endl;
}int main(int argc, char *argv[])
{if(argc != 2){usage(argv[0]);exit(1);}uint16_t port = atoi(argv[1]);std::unique_ptr<UdpServer> svr(new UdpServer(port));svr->initServer();svr->Start();return 0;
}
udp_server.hpp
#ifndef _UDP_SERVER_HPP
#define _UDP_SERVER_HPP#include "log.hpp"
#include <iostream>
#include <unordered_map>
#include <cstdio>
#include <string>
#include <cerrno>
#include <cstring>
#include <cstdlib>
#include <strings.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <unistd.h>
#include <queue>#define SIZE 1024class UdpServer
{
public:UdpServer(uint16_t port, std::string ip = "") : _port(port), _ip(ip), _sock(-1){}bool initServer(){// 1. 创建套接字_sock = socket(AF_INET, SOCK_DGRAM, 0); if (_sock < 0){logMessage(FATAL, "%d:%s", errno, strerror(errno));exit(2);}// 2. bind: 将用户设置的ip和port在内核中和我们当前的进程强关联struct sockaddr_in local;bzero(&local, sizeof(local));// 清0local.sin_family = AF_INET;local.sin_port = htons(_port);// 转换local.sin_addr.s_addr = _ip.empty() ? INADDR_ANY : inet_addr(_ip.c_str());// 转换if (bind(_sock, (struct sockaddr *)&local, sizeof(local)) < 0){logMessage(FATAL, "%d:%s", errno, strerror(errno));exit(2);}logMessage(NORMAL, "init udp server done ... %s", strerror(errno));return true;}void Start(){char buffer[SIZE];for (;;){// 注意:// peer,纯输出型参数struct sockaddr_in peer;// 输出型参数bzero(&peer, sizeof(peer));socklen_t len = sizeof(peer);char result[256];char key[64];std::string cmd_echo;// recvfrom读取报文ssize_t s = recvfrom(_sock, buffer, sizeof(buffer) - 1, 0, (struct sockaddr *)&peer, &len);if (s > 0){buffer[s] = 0; // 我们目前数据当做字符串uint16_t cli_port = ntohs(peer.sin_port);// 转换std::string cli_ip = inet_ntoa(peer.sin_addr);// 转换snprintf(key, sizeof(key), "%s-%u", cli_ip.c_str(), cli_port); // 127.0.0.1-8080logMessage(NORMAL, "key: %s", key);auto it = _users.find(key);if (it == _users.end()){// 不存在插入logMessage(NORMAL, "add new user : %s", key);_users.insert({key, peer});}}for (auto &iter : _users){std::string sendMessage = key;sendMessage += "# ";sendMessage += buffer; // 127.0.0.1-1234# 你好logMessage(NORMAL, "push message to %s", iter.first.c_str());sendto(_sock, sendMessage.c_str(), sendMessage.size(), 0, (struct sockaddr *)&(iter.second), sizeof(iter.second));}}}~UdpServer(){if (_sock >= 0)close(_sock);}private:// 一个服务器,一般必须需要ip地址和port(16位的整数)uint16_t _port;std::string _ip;int _sock;std::unordered_map<std::string, struct sockaddr_in> _users;std::queue<std::string> messageQueue;
};#endif
- netstat -antp// 查看网络进程服务
- 无论是多线程读还是写,用的sock都是一个,
- sock代表的就是文件,udp是全双工的,可以同时进行收发而不受干扰
使用网络在两台主机之间进行通信(多进程-父子进程):tcp实现
tcp_server.hpp
#pragma once#include <iostream>
#include <string>
#include <cstring>
#include <cerrno>
#include <cassert>
#include <signal.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <netinet/in.h>
#include "log.hpp"static void service(int sock, const std::string &clientip, const uint16_t &clientport)
{//echo serverchar buffer[1024];while(true){// read && write 可以直接被使用!ssize_t s = read(sock, buffer, sizeof(buffer)-1);if(s > 0){buffer[s] = 0; //将发过来的数据当做字符串std::cout << clientip << ":" << clientport << "# " << buffer << std::endl;}else if(s == 0) //对端关闭连接{logMessage(NORMAL, "%s:%d shutdown, me too!", clientip.c_str(), clientport);break;}else{ // logMessage(ERROR, "read socket error, %d:%s", errno, strerror(errno));break;}write(sock, buffer, strlen(buffer));}
}class TcpServer
{
private:const static int gbacklog = 20;
public:TcpServer(uint16_t port, std::string ip=""):listensock(-1), _port(port), _ip(ip){}void initServer(){// 1. 创建socket -- 进程和文件listensock = socket(AF_INET, SOCK_STREAM, 0);if(listensock < 0){logMessage(FATAL, "create socket error, %d:%s", errno, strerror(errno));exit(2);}logMessage(NORMAL, "create socket success, listensock: %d", listensock); // 3// 2. bind -- 文件 + 网络struct sockaddr_in local;memset(&local, 0, sizeof local);local.sin_family = AF_INET;local.sin_port = htons(_port);local.sin_addr.s_addr = _ip.empty() ? INADDR_ANY : inet_addr(_ip.c_str());if(bind(listensock, (struct sockaddr*)&local, sizeof(local)) < 0){logMessage(FATAL, "bind error, %d:%s", errno, strerror(errno));exit(3);}// 3. 因为TCP是面向连接的,当我们正式通信的时候,需要先建立连接if(listen(listensock, gbacklog) < 0){logMessage(FATAL, "listen error, %d:%s", errno, strerror(errno));exit(4);}logMessage(NORMAL, "init server success");}void start(){// 对SIGCHLD,主动忽略SIGCHLD信号,子进程退出的时候,会自动释放自己的僵尸状态signal(SIGCHLD, SIG_IGN); while(true){// sleep(1);// 4. 获取连接struct sockaddr_in src;socklen_t len = sizeof(src);int servicesock = accept(listensock, (struct sockaddr*)&src, &len);if(servicesock < 0){logMessage(ERROR, "accept error, %d:%s", errno, strerror(errno));continue;}// 获取连接成功了uint16_t client_port = ntohs(src.sin_port);std::string client_ip = inet_ntoa(src.sin_addr);logMessage(NORMAL, "link success, servicesock: %d | %s : %d |\n",\servicesock, client_ip.c_str(), client_port);// 开始进行通信服务啦// 多进程版 --- 创建子进程pid_t id = fork();assert(id != -1);if(id == 0){// 子进程是来进行提供服务的,需不需要知道监听socket呢?close(listensock);service(servicesock, client_ip, client_port);exit(0); // 僵尸状态}close(servicesock); // 如果父进程关闭servicesock,会不会影响子进程??}}~TcpServer(){}
private:uint16_t _port;std::string _ip;int listensock;// 监听套接字
};
tcp_server.cc
#include "tcp_server.hpp"
#include <memory>static void usage(std::string proc)
{std::cout << "\nUsage: " << proc << " port\n" << std::endl;
}// ./tcp_server port
int main(int argc, char *argv[])
{if(argc != 2){usage(argv[0]);exit(1);}uint16_t port = atoi(argv[1]);std::unique_ptr<TcpServer> svr(new TcpServer(port));svr->initServer();svr->start();return 0;
}
- telnet ip 端口号// 创建临时客户端
- while :; do ps ajx | grep tcp-server;echo "####";sleep 1;done
tcp_client.cc
#include <iostream>
#include <string>
#include <cstdio>
#include <cstring>
#include <unistd.h>
#include <sys/socket.h>
#include <sys/types.h>
#include <arpa/inet.h>
#include <netinet/in.h>void usage(std::string proc)
{std::cout << "\nUsage: " << proc << " serverIp serverPort\n"<< std::endl;
}// ./tcp_client targetIp targetPort
int main(int argc, char *argv[])
{if (argc != 3){usage(argv[0]);exit(1);}std::string serverip = argv[1];uint16_t serverport = atoi(argv[2]);bool alive = false;int sock = 0;std::string line;while (true) // TODO{if (!alive){sock = socket(AF_INET, SOCK_STREAM, 0);if (sock < 0){std::cerr << "socket error" << std::endl;exit(2);}// client 要不要bind呢?不需要显示的bind,但是一定是需要port// 需要让os自动进行port选择// 连接别人的能力!struct sockaddr_in server;memset(&server, 0, sizeof(server));server.sin_family = AF_INET;server.sin_port = htons(serverport);server.sin_addr.s_addr = inet_addr(serverip.c_str());if (connect(sock, (struct sockaddr *)&server, sizeof(server)) < 0){std::cerr << "connect error" << std::endl;exit(3); // TODO}std::cout << "connect success" << std::endl;alive = true;}std::cout << "请输入# ";std::getline(std::cin, line);if (line == "quit")break;ssize_t s = send(sock, line.c_str(), line.size(), 0);if (s > 0){char buffer[1024];ssize_t s = recv(sock, buffer, sizeof(buffer) - 1, 0);if (s > 0){buffer[s] = 0;std::cout << "server 回显# " << buffer << std::endl;}else if (s == 0){alive = false;close(sock);}}else{alive = false;close(sock);}}return 0;
}
- 客户端需要具有连接能力,是通过connect实现的
相关文章:
![](https://img-blog.csdnimg.cn/5c716e0eefac420cbe6cd59c5fd4bebd.png)
Lesson9.网络基础1
网络协议初识 所谓的协议就是人们为了通信的一种约定 操作系统要进行协议管理,必然会先描述,再组织协议本质就是软件,软件是可以"分层"协议在设计的时候,就是被层状的划分的, 为什么要划分成为层状结构 场景复杂功能解耦(便于人们进行各种维护)OSI七层模型 局域网中…...
![](https://img-blog.csdnimg.cn/img_convert/4f756c61770e11cf1f68e2df1c66c07c.png)
这几个SQL语法的坑,你踩过吗
本文已经收录到Github仓库,该仓库包含计算机基础、Java基础、多线程、JVM、数据库、Redis、Spring、Mybatis、SpringMVC、SpringBoot、分布式、微服务、设计模式、架构、校招社招分享等核心知识点,欢迎star~ Github地址:https://github.com/…...
![](https://img-blog.csdnimg.cn/793b977f158f4247b66b06fc4ca78c78.png)
算法基础——复杂度
前言 算法是解决问题的一系列操作的集合。著名的计算机科学家Niklaus Wirth曾提出:算法数据结构程序,由此可见算法在编程中的重要地位。本篇主要讨论算法性能好坏的标准之一——复杂度。 1 复杂度概述 1.1 什么是复杂度 本文所讨论的复杂度是指通过事先…...
![](https://img-blog.csdnimg.cn/cba59e324f264c95be49b790b85ca4d7.jpeg)
基类与派生类对象的关系 派生类的构造函数
🐶博主主页:ᰔᩚ. 一怀明月ꦿ ❤️🔥专栏系列:线性代数,C初学者入门训练,题解C,C的使用文章,「初学」C 🔥座右铭:“不要等到什么都没有了,才下…...
![](https://img-blog.csdnimg.cn/a9118c6035a3446cae62fd6e54bd51bb.png)
【算法】生成分布式 ID 的雪花算法
ID 是数据的唯一、不变且不重复的标识,在查询数据库的数据时必须通过 ID 查询,在分布式环境下生成全局唯一的 ID 是一个重要问题。 雪花算法(snowflake)是一种生成分布式环境下全局唯一 ID 的算法,该算法由 Twitter 发…...
![](https://img-blog.csdnimg.cn/a6fce92223d74f9ba34e4ddb10797179.png)
Linux系统编程 - 基础IO(IO操作)
目录 预备知识 复习C文件IO相关操作 printf相关函数 fprintf snprintf 读取文件 系统文件IO操作 open函数 umask()函数 open函数返回值 预备知识 1.你真的理解文件原理和操作了吗?不是语言问题,是系统问题2.是不是只有C/C有文件操作呢&#x…...
![](https://www.ngui.cc/images/no-images.jpg)
基于 Avue 的 CRUD 表格组件封装
在 components 文件夹中,创建一个新的 .vue 文件,例如:AvueCrudTable.vue。 透传父组件传递的属性和事件 : 1、利用v-bind=“ a t t r s " 支持所有 a v u e 的使用方法并在其基础上进行封装 2 、使用 v − o n = " attrs"支持所有 avue 的使用方法并在其基…...
![](https://www.ngui.cc/images/no-images.jpg)
树莓派学习笔记(十三)基于框架编写驱动代码
文章目录一、代码分析:二、源码一、代码分析: 在内核中由于代码文件多,避免函数名重复,使用static将函数的作用域限制在该文件内 内核的打印函数printk和printf类似 file_operations结构体使用符号“ . ”指定参数,省…...
![](https://img-blog.csdnimg.cn/4bd9154e7d8c448680dd959fc1e79571.png)
vue事件修饰符之.prevent
.prevent 事件修饰符只是阻止默认事件,不会自动触发任何事件处理函数。因此,在使用 .prevent 事件修饰符时,需要自己编写相应的事件处理函数来处理事件。 例如,在上面的例子中,我们通过在表单上绑定 submit.prevent&q…...
![](https://img-blog.csdnimg.cn/c63ba5597c8d42faa4a0af1e3a88739c.png)
【SpringCloud AlibabaSentinel实现熔断与限流】
本笔记内容为尚硅谷SpringCloud AlibabaSentinel部分 目录 一、Sentinel 1、官网 2、Sentinel是什么 3、下载 4、特性 5、使用 二、安装Sentinel控制台 1、sentinel组件由2部分构成 2、安装步骤 1.下载 2.运行命令 3.访问sentinel管理界面 三、初始化演示工程 …...
![](https://img-blog.csdnimg.cn/img_convert/53b1b800084538ef3cc30d1040291475.png)
类与对象-封装
一、封装的意义封装是C面向对象三大特性之一语法: class name { 访问权限:属性行为 };注意:类中的属性和行为 统称为成员属性 又称 成员属性 / 成员变量行为 又称 成员函数 / 成员方法封装将属性和行为作为一个整体,表现生活中的事物例①&…...
![](https://img-blog.csdnimg.cn/e936faf2f1d149e9afe56aa1af71d3ba.png)
【回忆杀】2012年拥有第一台电脑【致逝去的青春】
高中说起 在2012年的时候吧,高考过后,那个时候一门心思的想当一名体育老师【现在居然还有这个想法,哈哈】,最后没有考上自己希望的大学我记得好像是2012年7月的时候就去重庆投靠朋友,他教我做模具,2012年做…...
![](https://img-blog.csdnimg.cn/511aae8d579240cd888d3287238baa16.png)
PointNeXt: Revisiting PointNet++ with Improved Training and Scaling Strategies
Abstract PointNet 是点云理解领域最有影响力的神经网络架构之一。虽然近期出现了 PointMLP 和 Point Transformer 等新型网络,它们的精度已经大大超过了 PointNet,但我们发现大部分性能提升是由于改进的训练策略,例如数据增强和优化技术以及…...
![](https://img-blog.csdnimg.cn/2901148a5531422d889fdca7f042d9f3.png)
打印九九乘法表-课后程序(JavaScript前端开发案例教程-黑马程序员编著-第2章-课后作业)
【案例2-9】打印九九乘法表 一、案例描述 考核知识点 for双重循环 练习目标 掌握for循环应用。实现九九乘法表。 需求分析 九九乘法表相信大家一点也不陌生,之前见到的乘法表是印刷在课程本之上的。而在本案例中我们将用JavaScript代码来实现九九乘法表。 案例分…...
![](https://img-blog.csdnimg.cn/60e3a6028436421990ad737ca37d4375.png#pic_center)
【Linux】基于阻塞队列的生产者消费者模型
🌠 作者:阿亮joy. 🎆专栏:《学会Linux》 🎇 座右铭:每个优秀的人都有一段沉默的时光,那段时光是付出了很多努力却得不到结果的日子,我们把它叫做扎根 目录👉为何要使用…...
![](https://www.ngui.cc/images/no-images.jpg)
【华为OD机试 2023最新 】 真正的密码(C++)
文章目录 题目描述输入描述输出描述用例题目解析C++题目描述 在一行中输入一个字符串数组,如果其中一个字符串的所有以索引0开头的子串在数组中都有,那么这个字符串就是潜在密码, 在所有潜在密码中最长的是真正的密码,如果有多个长度相同的真正的密码,那么取字典序最大的…...
![](https://www.ngui.cc/images/no-images.jpg)
差分算法(蓝桥杯复习+例题讲解+模板c++)
文章目录差分介绍差分应用区间加区间求和总结3729. 改变数组元素100. 增减序列文章首发于:My Blog 欢迎大佬们前来逛逛 差分介绍 差分是一种常见的算法,用于快速修改数组中某一段区间的值。 差分的思想就是预处理出数组的差分数组,然后修改…...
![](https://img-blog.csdnimg.cn/e01885404c2345fcb97311ce9fd4ce9f.png)
CSS+ JS 实现手电筒效果
前言概述 JavaScript 结合 CSS 打造的一款图片特效,当鼠标拖拽滑块时,让本该置灰的图片局部恢复本来的颜色。且该效果随着你的鼠标的按下时的移动而移动。 核心功能 图片置灰 拖拽功能 让滑块位置处的图片恢复本来的颜色 实现原理 这个的实现原理并不…...
![](https://img-blog.csdnimg.cn/img_convert/c792566ef75d518c12d0dd22acdf27cb.png)
2021地理设计组二等奖:基于InSAR和指数分析的地面沉降风
作品简介 一、作品背景 地面沉降是指地面高程的降低, 又称地面下沉或地沉, 是以缓慢、难以察觉的向下垂直运动为主, 是指在自然和人为因素作用下, 由于地壳表层土体压缩而导致区域性地面标高降低的一种环境现象。目前, 地面沉降己成为城市化进程中普遍存在的生态环境问题, 成为…...
![](https://img-blog.csdnimg.cn/69ac8716cc054587a1eb4b52977118dd.png)
计算机操作系统(第四版)第二章进程的描述与控制—课后习题答案
1.什么是前趋图?为什么要引入前趋图? 前趋图是一个有向无循环图,记为DAG,用于描述进程之间执行的先后关系。 2.试画出下面四条语句的前趋图: S1:axy; S2:bz1; S3:ca-b; S4:wc1; 3.为什么程序并发执行会产生间断性特征&…...
![](https://img-blog.csdnimg.cn/81dd03aa3ac84129826f7d9495382df7.png)
CAN通信----电路图
CAN通信----基本原理 一、CAN总线网络连接 1.闭环总线网络----ISO11898 闭环总线网络高速、短距离,它的总线最大长度为 40m,通信速度最高为 1Mbps,总线的两端各要求有一个120 欧的电阻。 2.开环总线网络----ISO11519 开环总线网络低速、…...
![](https://img-blog.csdnimg.cn/img_convert/47e451fdfc22576035a46d4f978714b8.png)
Windows系统安装ElasticSearch(一)
一 ES介绍Elasticsearch 是一个分布式可扩展的实时搜索和分析引擎,一个建立在全文搜索引擎 Apache Lucene(TM) 基础上的搜索引擎.当然 Elasticsearch 并不仅仅是 Lucene 那么简单,它不仅包括了全文搜索功能,还可以进行以下工作:分布式实时文件存储&#…...
![](https://www.ngui.cc/images/no-images.jpg)
linux 产生随机数 并遍历
1、产生随机数 varRANDOMvarRANDOM varRANDOMvar[ $var % 150 ] 2、产生不重复的随机数 $ entries($(shuf -i 0-149 -n 15)) $ echo “${entries[]}” 3、对随机数排序 $ entries($(shuf -i 0-149 -n 15 | sort -n)) $ echo “entries[]"12224549546678798393118119124140…...
![](https://www.ngui.cc/images/no-images.jpg)
【3.24】Mybatis常见面试题
Mybatis常见面试题 #{}和¥{}的区别是什么? 【#】:底层执行SQL使用PreparedStatement对象,预编译SQL,相对安全。入参使用占位符的方式。 【$】:底层执行SQL使用Statement对象,入参使用SQL拼接的…...
![](https://img-blog.csdnimg.cn/img_convert/28421c67df734404ab8f6e8d6b7086c0.png)
IDEA 热部署,修改代码不用重启项目
热部署指在修改项目代码的时候不重启服务器让修改生效。安装JRebel and XRebelFile->Settings,然后Plugins-> Marketplace,输入JRebel,安装如下插件——JRebel and XRebel ,重启idea激活JRebel and XRebel第一行输入网址&am…...
![](https://img-blog.csdnimg.cn/img_convert/0ac16dc022799d93f4f3f6855348a5fd.webp?x-oss-process=image/format,png)
将 XLS 转换为 EXE:xlCompiler Crack
只需单击几下即可将Excel文件转换为应用程序 xl编译器无需编程即可将您的Excel电子表格转换为软件应用程序 将 XLS 转换为 EXE 将Excel文件转换为具有保护选项的应用程序。Excel 到 EXE 转换器为您提供了分发 Excel 模型的竞争优势和灵活性。将 Excel 的功能丰富的环境保存在应…...
![](https://img-blog.csdnimg.cn/a4a4fc3e9d5a4cacba2c67d1494a07a1.png)
【百面成神】spring基础12问,你能坚持到第几问
前 言 🍉 作者简介:半旧518,长跑型选手,立志坚持写10年博客,专注于java后端 ☕专栏简介:java面试宝典,特点:全、精、深、简,力求每个核心知识点1分钟回答好。 dz…...
![](https://img-blog.csdnimg.cn/53a96d16648d4997a8cd65a518232c5e.png?x-oss-process=image/watermark,type_d3F5LXplbmhlaQ,shadow_50,text_Q1NETiBA5pma6aOO5pC66Zyy,size_20,color_FFFFFF,t_70,g_se,x_16)
javaSE类和对象(下)
目录君1.封装2.访问限定符3.包的定义及使用4.static成员变量5.static成员方法6.代码块及其分类实例代码块静态代码块静态代码块与实例代码块的执行顺序static成员变量(类变量)初始化1.封装 面向对象程序三大特性:封装、继承、多态。而类和对象阶段,主要…...
![](https://img-blog.csdnimg.cn/fbb0d5cce5524190afd1737d15d2693b.png)
【数据结构】第四站:单链表力扣题(二)
目录 一、链表的回文结构 二、相交链表 三、环形链表 四、环形链表Ⅱ 五、复制带随机指针的链表 一、链表的回文结构 题目描述:链表的回文结构_牛客题霸_牛客网 对于这道题,如果没有前面的一些题的基础,是非常难做的,我们的思…...
![](https://img-blog.csdnimg.cn/5d5c80efbdb24811b2f210e434b9274b.png)
KafKa知识汇总
前言 汇总相关知识 Kafka快速实战与基本原理详解...
![](/images/no-images.jpg)
百度商桥怎样绑定网站/口碑营销策略
全国计算机技术与软件专业技术资格(水平)考试 2016年上半年 信息系统项目管理师 下午试卷 II (考试时间 15:20~17:20 共 120 分钟) 1. 本试卷满分 75 分。 2. 在答题纸的指定位置填写你所在的省、自治区、直辖市、计划单列市的名称。 3. 在答题纸的指定位置填写准考证…...
![](https://img-blog.csdnimg.cn/2021040210133836.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L0N5YmVyVmVpbg==,size_16,color_FFFFFF,t_70#pic_center)
黄石做企业网站/成人就业技术培训机构
本文转载自News BTC对CROSS的特别报道。以下为翻译的原文: 拍卖是财产权利转让的最古老方式之一。自从人类有了剩余价值的流动性需求,就有了拍卖这种方式。说到捡漏收藏界尽知的当属“明成化斗彩鸡缸杯”了。它的传承过程,似乎就是一部“拣大…...
![](/images/no-images.jpg)
wap网站有哪些/深圳全网推互联科技有限公司
https://segmentfault.com/a/1190000013822385转载于:https://www.cnblogs.com/EricZ-Blog/p/10817168.html...
![](https://img-blog.csdnimg.cn/20201217144926454.png)
影视网站建设源码/百度售后电话人工服务
良好的书写规范有利于自己后期查看,也有助于其他ABAPer阅读,在部分代码检查工具是自动规范检查。 良好的书写规范包括:定义,SQL,逻辑处理,条件判断等等。 记录书写规范,随时对比,自…...
![](https://img-blog.csdnimg.cn/0a416d1b3fd5491daff35068637ed8f6.png?x-oss-process=image/watermark,type_d3F5LXplbmhlaQ,shadow_50,text_Q1NETiBAQWVyb2JhdGljcw==,size_12,color_FFFFFF,t_70,g_se,x_16#pic_center)
影院资讯 wordpress/北京seo顾问服务
请写出图的邻接矩阵和邻接表,深度和广度遍历结果,最小生成树的结果 1.邻接矩阵 详见本人博客:图的存储结构:邻接矩阵 2.邻接表 详见本人博客:图的存储结构:邻接表 3.深度遍历结果:1234657 &am…...
![](http://hi.csdn.net/attachment/200912/5/11443_1259987453hIFf.jpg)
wordpress投票插件/西安网络科技公司排名
键盘脏一些也未必全无好处,可以借机研究一下按键频度。键盘特写,哪个键用的多,哪个键按的少,一目了然。 A到Z二十六个字母键,上下左右光标键,全部光秃秃的,可见使用频率很高;周边的回…...