【Linux】套接字的理解 基于TCP协议的套接字编程(单/多进程 / 线程池|英汉互译 / C++)

文章目录

  • 1. 前言
    • 1.1 网络方面的预备知识👇
    • 1.2 了解TCP协议
  • 2. 关于套接字编程
    • 2.1 什么是套接字 Socket
    • 2.2 socket 的接口函数
    • 2.3 Udp套接字编程的步骤
    • 2.4 sockaddr 结构
  • 3. 代码实现
    • 3.1 makefile
    • 3.2 log.hpp
    • 3.3 tcp_server.hpp
      • ① 框架
      • ② service() 通信服务
      • ③ initServer()(初始化服务器)
      • ④ startServer()(启动服务器)
    • 3.4 tcp_server.cc
    • 3.5 tcp_client.cc
    • 3.6 结果演示
  • 4. 线程池版本(实现英汉互译)
    • 4.1 线程池ThreadPool的实现
      • Task.hpp
    • 4.2 对多进程代码的修改 - TcpServer
    • tcp_server.hpp
      • 框架
      • service() 通信服务
      • dictOnline()(英汉互译/网络词典)
      • initServer()(初始化服务器)
      • startServer()(启动服务器)
    • 结果演示
  • 5. 完整代码

1. 前言

1.1 网络方面的预备知识👇

网络基础 - 预备知识(协议、网络协议、网络传输流程、地址管理)


1.2 了解TCP协议

首先我们对Tcp协议进行一个了解:

【网络基础】深入理解TCP协议:协议段、可靠性、各种机制

简单总结而言, TCP协议具有以下特点:

1.可靠传输:TCP通过三次握手建立连接,并使用序号、确认ACK和重传机制来确保数据的可靠性传输。
2.面向连接:在数据传输之前,TCP必须先建立连接,双方通过握手过程建立起通信信道。
3.数据流式传输:TCP将数据拆分成小块,并通过IP地址和端口号标识发送和接收方的数据包,保证数据的有序传输。
4.拥塞控制和流量控制:TCP具有拥塞控制和流量控制机制,通过调整发送方的发送速率和接收方的反馈机制来避免网络拥塞和资源浪费。


2. 关于套接字编程

2.1 什么是套接字 Socket

套接字(Socket) 是计算机网络中用于实现进程间通信的一种机制。它允许在不同计算机之间或同一计算机的不同进程之间进行数据传输和通信

套接字可以看作是网络通信中的一个端点 ,它由 IP地址 端口号 组成, 用于唯一标识网络中的通信实体点 。套接字提供了一组接口(通常是API)用于创建、连接、发送、接收和关闭连接等操作,以实现数据的传输和通信。


套接字可以分为两种类型(了解) 流套接字(Stream Socket) 数据报套接字(Datagram Socket)

  1. 流套接字 :基于 传输控制协议(TCP) 的套接字,提供面向连接的、可靠的、双向的数据传输。

    • 流套接字通过建立连接来实现数据的可靠传输,适用于需要保证数据完整性和顺序性的应用,如网页浏览、文件传输等。
  2. 数据报套接字:基于 用户数据报协议(UDP) 的套接字,提供无连接的、不可靠的数据传输。

    • 数据报套接字不需要建立连接,可以直接发送数据报给目标主机,适用于实时性要求高、对数据完整性和顺序性要求不高的应用,如视频流传输、实时游戏等。

2.2 socket 的接口函数

下面列举在我们进行Tcp与Udp的套接字编程所用的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.3 Udp套接字编程的步骤

  1. 创建套接字:使用 socket() 函数创建套接字。

  2. 绑定套接字:利用 bind() 将套接字绑定到一个 IP 地址和端口上。

  3. 监听连接:对于服务器端,使用 listen() 开始监听连接请求。

  4. 接受连接:对于服务器端,使用 accept() 接受客户端的连接请求并创建新的套接字用于通信。

  5. 发送数据:使用 send() 函数发送数据到连接的另一端。

  6. 接收数据:使用 recv() 函数接收从连接的另一端发送过来的数据。

  7. 关闭连接:通信结束后,使用 close() 关闭连接的套接字以释放资源。


2.4 sockaddr 结构

首先:

  • IPv4、 IPv6地址类型分别定义为常数AF_INET、 AF_INET6
    • 这样只要取得某种sockaddr结构体的首地址,不需要知道具体是哪种类型的sockaddr结构体,就可以根据地址类型字段确定结构体中的内容
  • socket API 可以都用struct sockaddr *类型表示, 在使用的时候需要强制转化成sockaddr_in;
    • 好处在于程序的通用性, 可以接收IPv4, IPv6, 以及UNIX Domain Socket各种类型的sockaddr结构体指针做为参数;

sockaddr 结构是在套接字编程中表示网络地址的通用结构。本身是一个抽象的结构,最常见的使用是:用于表示 IPv4 地址。

sockaddr 结构定义如下:

struct sockaddr {sa_family_t sa_family; // 地址家族(如 AF_INET)char sa_data[14]; // 地址数据
};

在实际使用中,经常使用 struct sockaddr_in 结构来表示 IPv4 地址,定义如下:

struct sockaddr_in {sa_family_t sin_family; // 地址家族(AF_INET)in_port_t sin_port; // 端口号struct in_addr sin_addr; // IP 地址char sin_zero[8]; // 填充字段,通常为0
};

3. 代码实现

3.1 makefile

首先写一个简单的makefile文件,用于后续执行程序便于测试

.PHONY:all
all:tcp_client tcp_servertcp_client:tcp_client.ccg++ -o $@ $^ -std=c++11 #-lpthread
tcp_server:tcp_server.ccg++ -o $@ $^ -std=c++11.PHONY:clean
clean:rm -f tcp_client tcp_server

3.2 log.hpp

在我们编写代码时,对于 异常情况报告或正常情况通知 ,利用log.hpp进行日志信息的记录:

#pragma once#include <iostream>
#include <cstdio>
#include <cstdarg>#include "log.hpp"// 宏定义 日志级别
#define DEBUG   0
#define NORMAL  1
#define WARNING 2
#define ERROR   3
#define FATAL   4// 全局字符串数组 : 将日志级别映射为对应的字符串
const char *gLevelMap[] = {"DEBUG","NORMAL","WARNING","ERROR","FATAL"
};#define LOGFILE "./threadpool.log" // LOGFILE: 表示日志文件的路径void logMessage(int level, const char* format, ...)
{// 判断DEBUG_SHOW 是否定义,分别执行操作#ifndef DEBUG_SHOW // 将日志级别映射为对应的字符串if(level == DEBUG) return; // DEBUG_SHOW不存在 且 日志级别为 DEBUG时,返回
#endif// DEBUG_SHOW存在 则执行下面的日志信息 char stdBuffer[1024];time_t timestamp = time(nullptr);// 将日志级别和时间戳格式化后的字符串将会被写入到 stdBuffer 缓冲区中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);
}

对于该日志类,不再重点,根据需要可以自行调整编写,不多讲解。

3.3 tcp_server.hpp

① 框架

tcp_server.hpp 即服务器类,首先是对于该服务器的 框架

  • 该框架包含了我们实现该类中会编写的 相关函数 以及 成员变量
// 通信服务的代码
static void service(int sock, const string& clientip, const int16_t& clientport){}class TcpServer // tcp服务器类
{
private:const static int gbacklog = 20;public:TcpServer(uint16_t port, string ip = ""): _port(port), _ip(ip), listensock(-1) // 设定缺省值{}// 初始化服务器void initServer(){}// 启动服务器void startServer(){}~TcpServer(){}private:uint16_t _port; // 端口号string _ip; // ip地址int listensock; // 套接字
};

② service() 通信服务

该代码用于 处理客户端与服务器间的通信:

  • 读取sock的内容:
    • 读取成功,则打印出客户端信息与发送的内容
    • 读取失败,打印日志并退出
  • 最后回显内容给客户端并关闭sock
static void service(int sock, const string& clientip, const int16_t& clientport)
{char buffer[1024];while(true){// 网络通信 可以直接使用read/writessize_t s = read(sock, buffer, sizeof(buffer) - 1);if(s > 0) {// 成功读取buffer[s] = 0;cout << clientip << " : " << clientport << "# " << buffer << endl;}else if (s == 0){ // 对端关闭了连接logMessage(NORMAL, "%s:%d shutdown, metoo", clientip.c_str(), clientport);break;}else{ // 错误logMessage(ERROR, "read socket error, %d:%s", errno, strerror(errno));break;}// 读取成功 将buffer内容写入sock(回显)write(sock, buffer, strlen(buffer));}close(sock);
}

③ initServer()(初始化服务器)

下面是 初始化服务器: 的代码,简单描述其步骤:

  1. socket()创建监听套接字
  2. bind()绑定客户端端口与ip
  3. listen()设置监听状态(因为tcp是面向连接的,需要先建立连接才能进行通信)
void initServer(){// 1. 创建socket —— 进程、文件方面listensock = socket(AF_INET, SOCK_STREAM, 0); // ipv4协议,套接字类型,协议类型if(listensock < 0) // 创建套接字失败{logMessage(FATAL, "%d : %s", errno, strerror(errno));exit(2); // 退出进程}logMessage(NORMAL, "create socket success, sock: %d", listensock); // 创建成功,输出信息// 2. bind —— 网络、文件方面struct sockaddr_in local; // 表示ipv4地址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());// 3. 设置监听状态// TCP面向连接,正式通信前,应先建立连接if(listen(listensock, gbacklog) < 0){logMessage(FATAL, "listen error: %d:%s", errno, strerror(errno));exit(4);}logMessage(NORMAL, "init server success.");}

④ startServer()(启动服务器)

下面是启动服务器的代码,简述其步骤:
首先下面代码分为 单进程与多进程 版本,在注释有标出

  1. 通过accept() 获取连接
  2. 获取连接成功后 进行双方的通信:
    • 对于单进程,提取客户端的ip与port后直接调用之前的service()即可
    • 对于多进程,首先通过fork创建子进程,进行功能分配。
  3. 从功能上讲:
    • 单进程版本
      • 单个进程负责监听套接字并处理连接请求,在处理连接的过程中,
      • 单个进程可能会阻塞在读取或写入数据的操作上,导致无法及时处理其他连接请求。
    • 多进程版本
      • 在父进程中负责监听套接字,并循环接收连接请求。
      • 每当有新的连接请求到来时,父进程会创建一个新的子进程来处理该连接。
      • 子进程独立于父进程,负责与客户端进行通信,父进程则继续监听新的连接请求。
      • 每个子进程都有自己的资源空间,因此可以独立处理连接,避免了单进程版本中可能出现的阻塞问题,提高了并发处理能力。
void startServer()
{//此signal : 多线程代码signal(SIGCHLD, SIG_IGN); // 忽略SIGCHLD信号while(true){// 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);string client_ip = inet_ntoa(src.sin_addr);logMessage(NORMAL, "link success, servicesock: %d| %s : %d |\n", servicesock, client_ip.c_str(), client_port);// 开始通信:// v1: 单进程循环// service(servicesock, client_ip, client_port);// v2: 多进程// 子进程用于给新的连接提供服务(service),父进程继续执行循环接收连接pid_t id = fork();assert(id != -1);if(id == 0){// 子进程: 用于提供服务,无需监听socketclose(listensock);service(servicesock, client_ip, client_port);exit(0); // 此时僵尸状态}close(servicesock); // 父进程不提供服务}
}

3.4 tcp_server.cc

  • tcp_server.cc 用于 形成最后的可执行程序 ,在main函数中 初始化服务器并启动服务器即可
  • 对于TcpServer对象的创建,我们可以使用unique_ptr智能指针进行创建对象,用于在动态内存中分配对象,可以在不需要时自动释放内存。
  • 通过获取到的参数,创建对象
using std::cout;
using std::endl;static void usage(string proc)
{cout << "\nUsage:" << proc << " port" << endl;
}int main(int argc, char* argv[])
{if(argc != 2) { // 参数数量错误usage(argv[0]); // 输出正确使用方法exit(1); // 退出}uint16_t port = atoi(argv[1]); // 获取端口号// 智能指针创建 TcpServer对象std::unique_ptr<TcpServer> svr(new TcpServer(port));svr->initServer();svr->startServer();return 0; 
}

3.5 tcp_client.cc

  • 同理对于tcp_client.cc,首先获取传来的ip与端口号
  • 循环内:
    • 如果还未建立连接 创建套接字socksockaddr_in 结构体,后进行connect() 连接
    • 建立连接后 :持续读取用户输入的内容,并接收来自客户端的回显信息。
// 打印正确的程序使用方法
void Usage(std::string proc)
{std::cout << "\nUsage: " << proc << " <ip> <port>" << std::endl;
}// ./tcp_client 192.168.1.100 8080
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 connectAlive = false; // 是否已经建立了连接int sock = 0;while(true){if(!connectAlive){sock = socket(AF_INET, SOCK_STREAM, 0);if(sock < 0) { // 创建套接字失败std::cerr << "socket error" << std::endl;exit(2);}struct sockaddr_in server;memset(&server, 0, sizeof(server));server.sin_port = htons(serverPort);server.sin_family = AF_INET;server.sin_addr.s_addr = inet_addr(serverIp.c_str());if(connect(sock, (struct sockaddr*)&server, sizeof(server)) < 0){ // 连接失败std::cerr << "connect error: " << errno << " " << strerror(errno) << std::endl;exit(3);}// 建立连接成功,讲connectALive 设为truestd::cout << "connect success." << std::endl;connectAlive = true;}std::cout << "请输入# " << std::endl;std::string line;std::getline(std::cin, line);if(line == "quit") break;ssize_t s = send(sock, line.c_str(), line.size(), 0);if(s > 0) { // send 成功char buffer[1024];ssize_t r = recv(sock, buffer, sizeof(buffer), 0);if(r > 0) { // recv 成功buffer[r] = '\0';std::cout << "server response: " << buffer << std::endl;} else { // recv 失败std::cerr << "recv() error: client receiving message failed." << std::endl;close(sock);connectAlive = false;}} else {std::cerr << "send() error: client sending message failed." << std::endl;close(sock);}}return 0;
}

3.6 结果演示

如下图所示,当启动客户端后,客户端会有一个监听套接字

此时我们可以有多个客户端同时进行连接通信,并且能正确的接收到来自服务器的回显信息。
在这里插入图片描述


4. 线程池版本(实现英汉互译)

4.1 线程池ThreadPool的实现

对于线程池版本,首先我们需要自实现一个线程池,有关线程池的详细内容/ 代码在下面👇:

ThreadPool代码实例 与 理解

  • 而线程池版本的套接字实现,我们需要 对上面链接代码部分进行修改
    • 需要根据要求(在这里即英汉互译)对Task.hpp(任务类)进行更改:

Task.hpp

对于任务类,由于我们通信时需要:套接字,对方的ip、端口号,以及处理方法,所以 对Task类的变量和成员函数进行修改 即可:

#pragma once#include <iostream>
#include <string>
#include <functional>
#include "log.hpp"// typedef std::function<int(int, int)> func_t;
using func_t = std::function<void(int, const std::string& ip, const uint16_t& port, const std::string& name)>;class Task
{
public:// 构造Task(){}Task(int sock, const std::string ip, const uint16_t port, func_t func):_sock(sock),_ip(ip),_port(port),_func(func){}void operator()(const std::string& name){_func(_sock, _ip, _port, name);}private:int _sock;uint16_t _port;std::string _ip;func_t _func;
};

4.2 对多进程代码的修改 - TcpServer

由于我们将改为线程池的版本,所以这里我们对服务器的逻辑tcp_server.hpp作修改:

tcp_server.hpp

框架

  • 框架用于展示整个文件中包含的函数,以及类的实现,省略具体实现:
#pragma once#include <iostream>
#include <string>
#include <string.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <unistd.h>
#include <assert.h>
#include <signal.h>
#include <memory>
#include <unordered_map>#include "ThreadPool/threadPool.hpp"
#include "ThreadPool/log.hpp"
#include "ThreadPool/Task.hpp"// 通信服务
static void service(int sock, const std::string& clientip, const uint16_t& clientport, const std::string& thread_name)
{}// 英汉互译
static void dictOnline(int sock, const std::string& clientip, const uint16_t& clientport, const std::string& thread_name)
{}class TcpServer
{
private:const static int gbacklog = 20;public: // 构造 + 析构TcpServer(const uint16_t& port, const std::string& ip = "0.0.0.0"): _ip(ip), _port(port),_listensock(-1), _threadpool_ptr(ThreadPool<Task>::getThreadPool()){}~TcpServer(){}// 功能函数// 启动服务器void initServer(){ }void startServer(){ }private:std::string _ip;uint16_t _port;int _listensock;std::unique_ptr<ThreadPool<Task>> _threadpool_ptr;
};

service() 通信服务

对于通信服务部分,总体和多进程版本无异,区别在于参数上 传入了线程名 ,可以在输出消息时加上线程名。

static void service(int sock, const std::string& clientip, const uint16_t& clientport, const std::string& thread_name)
{char buffer[1024];while(true){ssize_t s = read(sock, buffer, sizeof(buffer) - 1);if(s > 0){buffer[s] = 0;std::cout << thread_name << " | " << clientip << ":" << clientport << " | " << buffer << std::endl; }else if (s == 0){logMessage(DEBUG, "client quit, me too.");break;}else{logMessage(ERROR, "read error, me too.");break;}write(sock, buffer, strlen(buffer)); // 回写内容}close(sock);
}

dictOnline()(英汉互译/网络词典)

对于该部分,主要包含以下步骤:

  • 创建一个哈希类,作为词典用于对应中英文
  • 随后循环读取客户端的内容:
    • 如果可以在词典中找到,就记录该对应的词汇
    • 如果找不到,就打印错误信息
  • 最后将结果写回客户端
static void dictOnline(int sock, const std::string& clientip, const uint16_t& clientport, const std::string& thread_name)
{char buffer[1024];static std::unordered_map<std::string, std::string> dict = {{"hello", "你好"},{"world", "世界"},{"tcp", "传输控制协议"},{"udp", "用户数据报协议"}};// 添加4组数据dict["rpc"] = "远程过程调用";dict["ssh"] = "安全外壳";dict["http"] = "超文本传输协议";dict["https"] = "超文本传输协议安全";while(true){ssize_t s = read(sock, buffer, sizeof(buffer) - 1);if(s > 0){buffer[s] = 0;std::string message;auto iter = dict.find(buffer);if(iter == dict.end()) message = "not found:(";else message = iter->second;std::cout << thread_name << " | " << clientip << ":" << clientport << " | " << buffer << " | " << message << std::endl;write(sock, message.c_str(), message.size()); // 将结果写回}else if (s == 0){logMessage(DEBUG, "client quit, me too.");break;}else{logMessage(ERROR, "read error | %d : %s", errno, strerror(errno));break;} }close(sock);
}

initServer()(初始化服务器)

初始化服务器的代码与多进程版本一致:

void initServer(){// 创建listensock_listensock = socket(AF_INET, SOCK_STREAM, 0);if(_listensock < 0) {logMessage(ERROR, "socket error, %d : %s", errno, strerror(errno));exit(1);}// 绑定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(_listensock, (struct sockaddr*)&local, sizeof(local)) < 0){logMessage(ERROR, "bind error, %d : %s", errno, strerror(errno));exit(2);}// 监听if(listen(_listensock, gbacklog) < 0){logMessage(ERROR, "listen error, %d : %s", errno, strerror(errno));exit(3);}logMessage(NORMAL, "init server success.");}

startServer()(启动服务器)

  • 对于启动服务器,首先需要 运行线程池 ,在正确建立连接后,将客户端的ip端口号与英汉互译的功能一同 传入Task对象
  • 最后将任务类添加到线程池中
void startServer()
{// 使用线程池_threadpool_ptr->run();while(true){// 等待客户端连接struct sockaddr_in peer;socklen_t len = sizeof(peer);int sock = accept(_listensock, (struct sockaddr*)&peer, &len);if(sock < 0){logMessage(ERROR, "accept error, %d : %s", errno, strerror(errno));continue;}// 成功获取连接uint16_t client_port = ntohs(peer.sin_port);std::string client_ip = inet_ntoa(peer.sin_addr);logMessage(NORMAL, "get a new client, ip: %s, port: %d", client_ip.c_str(), client_port);// 创建线程Task t(sock, client_ip, client_port, dictOnline);_threadpool_ptr->pushTask(t); // 添加 任务}
}

结果演示

通过下图,可以看出,启动服务器后,服务器可以接收多个客户端的信息,并正确发出反馈。

在这里插入图片描述


5. 完整代码

上述关于Tcp套接字通信的完整代码在👇:

Tcp套接字编程 - 实例代码

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

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

相关文章

记录docker ps查找指定容器的几个命令

1.docker ps | grep registry 查询包含registry的容器 2.docker ps | grep -E "reigistry\s" 开启正则匹配模式&#xff0c;匹配registry后面为空格的容器&#xff0c;若是匹配一整行可以这样写docker ps | grep -E "^([0-9a-f]{12})\sregistry\s.*" 这…

电视机顶盒哪个牌子好?618畅销电视机顶盒排行榜

电视机顶盒是我们使用最多的&#xff0c;不管看直播、动画、追剧、上网课都少不了它的存在。大促期间很多朋友问我电视机顶盒哪个牌子好&#xff0c;小编按照各平台的销量情况整理了618畅销电视机顶盒排行榜&#xff0c;看看哪些品牌的电视机顶盒是最受欢迎的吧。 TOP 1&#x…

通过扩展指令增强基于覆盖引导的模糊测试

本文由Bruno Oliveira于2024年4月25日发表于IncludeSec的官方网站上。作为IncludeSec的安全研究人员&#xff0c;在他们日常的安全审计和渗透测试工作中&#xff0c;有时需要为客户开发一些模糊测试工具。在安全评估方法中使用模糊测试技术&#xff0c;可以有效地在复杂的现代化…

【Basic】BUU LFI COURSE

文章目录 前言一、BUU LFI COURSE二、知识点PHP的危险函数路径遍历攻击 解题感悟 前言 话不多说直接看题 一、BUU LFI COURSE emmm什么提示也没给啊&#xff0c;那只能点开看一看线索了 okok咱们先分析一下这段php代码 <?php /*** Created by PhpStorm.* User: jinzhao*…

【HCIP学习】RSTP和MSTP

一、RSTP&#xff08;Rapid Spanning Tree Protocol&#xff0c;快速生成树&#xff09; 1、背景&#xff1a;RSTP从STP发展而来&#xff0c;具备STP的所有功能&#xff0c;可以兼容stp运行 2、RSTP与STP不同点 &#xff08;1&#xff09;减少端口状态 STP:disabled\blockin…

线程的概念和控制

文章目录 线程概念线程的优点线程的缺点线程异常线程用途理解虚拟地址 线程控制线程的创建线程终止线程等待线程分离封装线程库 线程概念 什么是线程&#xff1f; 在一个程序里的一个执行路线就叫做线程&#xff08;thread&#xff09;。更准确的定义是&#xff1a;线程是“一…

2024中青杯数学建模C题:“X 疾病”在人群中的传播代码论文思路分析

2024中青杯数学建模C题论文和代码已完成&#xff0c;代码为C题全部问题的代码&#xff0c;论文包括摘要、问题重述、问题分析、模型假设、符号说明、模型的建立和求解&#xff08;问题1模型的建立和求解、问题2模型的建立和求解、问题3模型的建立和求解&#xff09;、模型的评价…

c++ queue容器

在C标准库中&#xff0c;std::queue 是一个容器适配器&#xff0c;它提供了队列&#xff08;FIFO - First In First Out&#xff09;的数据结构。队列是一种特殊的线性数据结构&#xff0c;只允许在表的前端&#xff08;front&#xff09;进行删除操作&#xff0c;而在表的后端…

nssctf(Web刷题)

[SWPUCTF 2021 新生赛]gift_F12 打开题目是一个时间页面&#xff0c;不过看了一会儿发现没有什么用 直接F12打开网页源代码 CtrlF搜索flag 找到了flag NSSCTF{We1c0me_t0_WLLMCTF_Th1s_1s_th3_G1ft} [第五空间 2021]签到题 NSSCTF{welcometo5space} [SWPUCTF 2021 新生赛…

钉钉算是在线办公系统的设计标杆,尽管它依然很难用

不吹不黑&#xff0c;钉钉界面谁的的确简洁&#xff0c;无奈它面向的是场景复杂的办公领域&#xff0c;导致其越来越臃肿难用&#xff0c;反正我是该研究研究&#xff0c;但绝对不会用的。 举报 评论 1

Invoking “make cmake_check_build_system“ failed

前言&#xff1a; 在看过站内其他的方法且试过之后没奏效之后&#xff0c;偶然&#xff0c;无意间&#xff0c;随手整对了&#xff0c;然后后续在老赵的文档也找到了原因&#xff0c;对的上号&#xff0c;那在此我提出一种新的方法&#xff0c;且很简单的小tips。首先先来看看…

数据挖掘与机器学习——机器学习概述

一、什么是机器学习 机器学习的英文名称叫Machine Learning&#xff0c;简称ML&#xff0c;该领域主要研究的是如何使计算机能够模拟人类的学习行为从而获得新的知识。 机器学习与数据挖掘的联系&#xff1a;简单来说&#xff0c;机器学习就是让计算机从大量 的数据中学习到相关…

yaml文件格式详解 及 k8s实战演示

目录 一 k8s 支持的语言格式 1&#xff0c;YAML 语法格式 2&#xff0c;查看 api 资源版本标签 二 k8s 运行nginx pod实例 yaml文件 具体讲解 1&#xff0c;写一个yaml文件demo 2&#xff0c;deployment 管理nginx 的yaml 文件 3&#xff0c;创建资源对象 4&#…

【四、性能测试】Linux stress 压力模拟测试工具

在做 CPU 问题解析之前&#xff0c;需要先了解一下压力模拟工具&#xff0c;可以将 CPU、MEM、IO 等进行压力模拟&#xff0c;可以在模拟压力的过程中进行问题解析 一、STRESS 模拟对CPU、Memory、IO、磁盘进行压力测试。可以使用 stress 工具&#xff0c;它是专门针对 linux…

mysql 多表关联查询性能优化-同一sql不同的执行计划

一、问题背景 相同的sql&#xff0c;不同的日期&#xff0c;执行的时间差异很大&#xff0c;执行计划不一样。执行快时&#xff0c;30ms左右。执行慢时&#xff0c;15s左右。 二、分析结论 1、经过分析&#xff0c;发现不同日期下&#xff0c;sql的执行计划不同&#xff0c;驱…

基于springboot+vue的学生考勤管理系统

开发语言&#xff1a;Java框架&#xff1a;springbootJDK版本&#xff1a;JDK1.8服务器&#xff1a;tomcat7数据库&#xff1a;mysql 5.7&#xff08;一定要5.7版本&#xff09;数据库工具&#xff1a;Navicat11开发软件&#xff1a;eclipse/myeclipse/ideaMaven包&#xff1a;…

实现mysql的主从复制、实现MySQL的读写分离与负载均衡

实验环境 &#xff08;注明&#xff09;以下的所有关于yum和rpm以及tar的软件需要自己准备&#xff0c;没有的话可以私信博主 实验目标&#xff1a; 1.实现mysql主从复制 2.实现mysql读写分离与负载均衡 实验一、搭建mysql主从复制 1.建立时间同步环境&#xff0c;在主节…

C++BuilderXE 如何让listView按文件名数字排序而非字母排序

int m_nDataColSort0; bool IsAsctrue; void __fastcall TForm1::RzListView4Compare(TObject *Sender, TListItem *Item1, TListItem *Item2, int Data, int &Compare) { if(m_nDataColSort0) { //按列表第二列排序 //CompareCompareText(Item1->SubItems-…

卷积神经网络(CNN)详细介绍及其原理详解

卷积神经网络&#xff08;Convolutional Neural Networks&#xff0c;简称CNN&#xff09;是深度学习中非常重要的一类神经网络&#xff0c;主要用于图像识别、图像分类、物体检测等计算机视觉任务。本文将详细介绍卷积神经网络的基本概念、结构组成及其工作原理&#xff0c;并…

BCD编码(8421)介绍

概念 BCD (Binary-Coded Decimal) 是一种二进制的数字编码形式&#xff0c;其特点每个十进制数位用4个二进制位来表示。 在网络IO中&#xff0c;你传输一个数字类型最少需要一字节&#xff0c;传输两个数字类型最少需要两字节&#xff0c;但是当你使用BCD编码后传输&#xff…