当前位置: 首页 > news >正文

Socket 编程 TCP

Socket 编程 TCP

  • TCP socket API 详解
  • V1 - Echo Server
  • V2 - Echo Server 多进程版本
  • V3 - Echo Server 多线程版本
  • V4 - Echo Server 线程池版本
  • 多线程远程命令执行
  • v5 引入线程池版本翻译

TCP socket API 详解

socket():
在这里插入图片描述

  • socket()打开一个网络通讯端口,如果成功的话,就像 open()一样返回一个文件描述符;
  • 应用程序可以像读写文件一样用 read/write 在网络上收发数据; 如果 socket()调用出错则返回-1;
  • 对于 IPv4, family 参数指定为 AF_INET;
  • 对于 TCP 协议,type 参数指定为 SOCK_STREAM, 表示面向流的传输协议
  • protocol 参数的介绍从略,指定为 0 即可。
  • bind:
    在这里插入图片描述
  • 服务器程序所监听的网络地址和端口号通常是固定不变的,客户端程序得知服务器程序的地址和端口号后就可以向服务器发起连接; 服务器需要调用 bind 绑定一个固定的网络地址和端口号;
  • bind()成功返回 0,失败返回-1。
  • bind()的作用是将参数 sockfd 和 myaddr 绑定在一起, 使 sockfd 这个用于网络通讯的文件描述符监听 myaddr 所描述的地址和端口号;
  • 前面讲过,struct sockaddr *是一个通用指针类型,myaddr 参数实际上可以接受多种协议的 sockaddr 结构体,而它们的长度各不相同,所以需要第三个参数 addrlen
    指定结构体的长度;

我们的程序中对myaddr参数是这样初始化的:
在这里插入图片描述

  1. 将整个结构体清零;
  2. 设置地址类型为 AF_INET;
  3. 网络地址为 INADDR_ANY, 这个宏表示本地的任意 IP 地址,因为服务器可能有
    多个网卡,每个网卡也可能绑定多个 IP 地址, 这样设置可以在所有的 IP 地址上监听, 直到与某个客户端建立了连接时才确定下来到底用哪个 IP 地址;
  4. 端口号为 SERV_PORT, 我们定义为 9999;

listen:
在这里插入图片描述

  • listen()声明 sockfd 处于监听状态, 并且最多允许有 backlog 个客户端处于连接等待状态, 如果接收到更多的连接请求就忽略, 这里设置不会太大(一般是 5);
  • listen()成功返回 0,失败返回-1;
    accept:
    在这里插入图片描述

connect:
在这里插入图片描述

  • 客户端需要调用 connect()连接服务器;
  • connect 和 bind 的参数形式一致, 区别在于 bind 的参数是自己的地址, 而connect 的参数是对方的地址;
  • connect()成功返回 0,出错返回-1;

V1 - Echo Server

nocopy.hpp

class nocopy
{public:nocopy(){}~nocopy(){}nocopy operator=(const nocopy& n)=delete;nocopy(const nocopy&n) =delete;
};

TcpServer.hpp

#pragma once
#include "common.hpp"
#include"InetAddr.hpp"
using namespace LogModule;
using func_t = std::function<std::string(std::string message, InetAddr peer)>;
const static int backlog = 8;
class tcpserve:public nocopy
{
public:tcpserve(uint16_t port, func_t func): _port(port),_func(func),_isruning(false){}void Init(){// 1._listensockfd = socket(AF_INET, SOCK_STREAM, 0);if (_listensockfd < 0){LOG(LogLevel::FATAL) << "socket error";exit(SOCKET_ERR);}LOG(LogLevel::INFO) << "socket success: " << _listensockfd; // 3InetAddr local(_port);int n = bind(_listensockfd, local.NetAddrPtr(), local.NetAddrLen());if (n < 0){LOG(LogLevel::FATAL) << "bind error";exit(BIND_ERR);}LOG(LogLevel::INFO) << "bind success: " << _listensockfd; // 3// 3、第二个参数运行排队的最大申请链接数n = listen(_listensockfd, backlog);if (n < 0){LOG(LogLevel::FATAL) << "listen error";exit(LISTEN_ERR);}LOG(LogLevel::INFO) << "listen success: " << _listensockfd; // 3// listen成就代表链接上了}void Run(){// 先获得链接的sockfd_isruning = true;while (_isruning){struct sockaddr_in peer;socklen_t len = sizeof(sockaddr_in);// 没有listen到accept会堵塞int sockfd = accept(_listensockfd, CONV(peer), &len);if (sockfd < 0){// 再去申请LOG(LogLevel::WARNING) << "accept error";continue;}// 此时获取远端的地址信息成功InetAddr addr(peer);LOG(LogLevel::INFO) << "accept success, peer addr : " << addr.StringAddr();// 去调用我们的服务Service(sockfd, addr);}_isruning = false;}void Service(int sockfd, InetAddr &peer){char buffer[1024];while (true){// 我们不断读取我们的信息int n = read(sockfd, buffer, sizeof(buffer) - 1);if (n > 0){buffer[n] = 0; // 设置为C风格字符串, n<= sizeof(buffer)-1LOG(LogLevel::DEBUG) << peer.StringAddr() << " #" << buffer;std::string echo_string = _func(buffer, peer);//写回我们的消息write(sockfd,echo_string.c_str(),echo_string.size());}else if (n == 0){//相当于管道的写端已经关闭了LOG(LogLevel::DEBUG) << peer.StringAddr() << " 退出了...";close(sockfd);break;}else{LOG(LogLevel::DEBUG) << peer.StringAddr() << " 异常...";close(sockfd);break;}}}~tcpserve(){}private:int _listensockfd;uint16_t _port;func_t _func;bool _isruning;
};

Comm.hpp

#pragma once
#include<iostream>
#include<string.h>
#include<string>
#include<sys/types.h>
#include<sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include<memory>
#include<functional>
//#include"InetAddr.hpp"
#include"log.hpp"
#define CONV(addr) ((struct sockaddr*)&addr)
enum ExitCode
{OK=0,SOCKET_ERR,BIND_ERR,LISTEN_ERR,USAGE_ERR,CONNECT_ERR};
//用来防止拷贝的
class nocopy
{public:nocopy(){}~nocopy(){}nocopy operator=(const nocopy& n)=delete;nocopy(const nocopy&n) =delete;
};

tcpclient.cc

#include "common.hpp"
#include"InetAddr.hpp"
void Usage(std::string proc)
{std::cerr << "Usage: " << proc << " server_ip server_port" << std::endl;
}// ./tcpclient server_ip server_port
int main(int argc, char *argv[])
{if (argc != 3){Usage(argv[0]);exit(USAGE_ERR);}std::string serverip = argv[1];uint16_t serverport = std::stoi(argv[2]);//1. 创建socket int sockfd = socket(AF_INET, SOCK_STREAM, 0);if (sockfd < 0){std::cerr << "socket error" << std::endl;exit(SOCKET_ERR);}// 2. 直接向目标服务器发起建立连接的请求InetAddr serveraddr(serverip, serverport);int n = connect(sockfd, serveraddr.NetAddrPtr(), serveraddr.NetAddrLen());if (n < 0){std::cerr << "connect error" << std::endl;exit(CONNECT_ERR);}// 3. echo clientwhile (true){std::string line;std::cout << "Please Enter@ ";std::getline(std::cin, line);write(sockfd, line.c_str(), line.size());char buffer[1024];ssize_t size = read(sockfd, buffer, sizeof(buffer) - 1);if (size > 0){buffer[size] = 0;std::cout<< buffer << std::endl;}}close(sockfd);return 0;
}

V2 - Echo Server 多进程版本

修改的只有tcpserve.hpp的Run函数

#pragma once
#include "common.hpp"
#include "InetAddr.hpp"
using namespace LogModule;
using func_t = std::function<std::string(std::string message, InetAddr peer)>;
const static int backlog = 8;
class tcpserve : public nocopy
{
public:tcpserve(uint16_t port, func_t func): _port(port),_func(func),_isruning(false){}void Init(){// 1._listensockfd = socket(AF_INET, SOCK_STREAM, 0);if (_listensockfd < 0){LOG(LogLevel::FATAL) << "socket error";exit(SOCKET_ERR);}LOG(LogLevel::INFO) << "socket success: " << _listensockfd; // 3InetAddr local(_port);int n = bind(_listensockfd, local.NetAddrPtr(), local.NetAddrLen());if (n < 0){LOG(LogLevel::FATAL) << "bind error";exit(BIND_ERR);}LOG(LogLevel::INFO) << "bind success: " << _listensockfd; // 3// 3、第二个参数运行排队的最大申请链接数n = listen(_listensockfd, backlog);if (n < 0){LOG(LogLevel::FATAL) << "listen error";exit(LISTEN_ERR);}LOG(LogLevel::INFO) << "listen success: " << _listensockfd; // 3// listen成就代表链接上了}void Run(){// 先获得链接的sockfd_isruning = true;while (_isruning){struct sockaddr_in peer;socklen_t len = sizeof(sockaddr_in);// 没有listen到accept会堵塞int sockfd = accept(_listensockfd, CONV(peer), &len);if (sockfd < 0){// 再去申请LOG(LogLevel::WARNING) << "accept error";continue;}// 此时获取远端的地址信息成功InetAddr addr(peer);LOG(LogLevel::INFO) << "accept success, peer addr : " << addr.StringAddr();//v1// 去调用我们的服务// Service(sockfd, addr);//v2pid_t pid = fork();if (pid > 0){// 父进程close(sockfd);int n=waitpid(pid,nullptr,0);//这里我们不可以进行阻塞等待1.我们可以进行对信号的忽略2.创建孙子进程(void)n;}else if (pid == 0){// 子进程close(_listensockfd);if(fork()>0) exit(OK);//孙子进程去执行service,子进程什么也不做直接等待成功等待父进程回收孙子进程则成为孤儿进程最后被系统回收Service(sockfd,addr);exit(OK);}else{close(sockfd);LOG(LogLevel::FATAL) << "fork error";exit(FORK_ERR);}}_isruning = false;}void Service(int sockfd, InetAddr &peer){char buffer[1024];while (true){// 我们不断读取我们的信息int n = read(sockfd, buffer, sizeof(buffer) - 1);if (n > 0){buffer[n] = 0; // 设置为C风格字符串, n<= sizeof(buffer)-1LOG(LogLevel::DEBUG) << peer.StringAddr() << " #" << buffer;std::string echo_string = _func(buffer, peer);// 写回我们的消息write(sockfd, echo_string.c_str(), echo_string.size());}else if (n == 0){// 相当于管道的写端已经关闭了LOG(LogLevel::DEBUG) << peer.StringAddr() << " 退出了...";close(sockfd);break;}else{LOG(LogLevel::DEBUG) << peer.StringAddr() << " 异常...";close(sockfd);break;}}}~tcpserve(){}private:int _listensockfd;uint16_t _port;func_t _func;bool _isruning;
};

V3 - Echo Server 多线程版本

void Run(){// 先获得链接的sockfd_isruning = true;while (_isruning){struct sockaddr_in peer;socklen_t len = sizeof(sockaddr_in);// 没有listen到accept会堵塞int sockfd = accept(_listensockfd, CONV(peer), &len);if (sockfd < 0){// 再去申请LOG(LogLevel::WARNING) << "accept error";continue;}// 此时获取远端的地址信息成功InetAddr addr(peer);LOG(LogLevel::INFO) << "accept success, peer addr : " << addr.StringAddr();// v1//  去调用我们的服务//  Service(sockfd, addr);// v2//  pid_t pid = fork();//  if (pid > 0)//  {//      // 父进程//      close(sockfd);//      int n=waitpid(pid,nullptr,0);//这里我们不可以进行阻塞等待1.我们可以进行对信号的忽略2.创建孙子进程//      (void)n;//  }//  else if (pid == 0)//  {//      // 子进程//      close(_listensockfd);//      if(fork()>0) exit(OK);//      //孙子进程去执行service,子进程什么也不做直接等待成功等待父进程回收孙子进程则成为孤儿进程最后被系统回收//      Service(sockfd,addr);//      exit(OK);//  }//  else//  {//      close(sockfd);//      LOG(LogLevel::FATAL) << "fork error";//      exit(FORK_ERR);// }// v3多线程版本pthread_t pid;ThreadData *ptr = new ThreadData(sockfd, addr, this);int n = pthread_create(&pid, nullptr, Rountinue, ptr);}_isruning = false;}void Run(){// 先获得链接的sockfd_isruning = true;while (_isruning){struct sockaddr_in peer;socklen_t len = sizeof(sockaddr_in);// 没有listen到accept会堵塞int sockfd = accept(_listensockfd, CONV(peer), &len);if (sockfd < 0){// 再去申请LOG(LogLevel::WARNING) << "accept error";continue;}// 此时获取远端的地址信息成功InetAddr addr(peer);LOG(LogLevel::INFO) << "accept success, peer addr : " << addr.StringAddr();// v1//  去调用我们的服务//  Service(sockfd, addr);// v2//  pid_t pid = fork();//  if (pid > 0)//  {//      // 父进程//      close(sockfd);//      int n=waitpid(pid,nullptr,0);//这里我们不可以进行阻塞等待1.我们可以进行对信号的忽略2.创建孙子进程//      (void)n;//  }//  else if (pid == 0)//  {//      // 子进程//      close(_listensockfd);//      if(fork()>0) exit(OK);//      //孙子进程去执行service,子进程什么也不做直接等待成功等待父进程回收孙子进程则成为孤儿进程最后被系统回收//      Service(sockfd,addr);//      exit(OK);//  }//  else//  {//      close(sockfd);//      LOG(LogLevel::FATAL) << "fork error";//      exit(FORK_ERR);// }// v3多线程版本pthread_t pid;ThreadData *ptr = new ThreadData(sockfd, addr, this);int n = pthread_create(&pid, nullptr, Rountinue, ptr);}_isruning = false;}class ThreadData{public:ThreadData(int sockd, InetAddr peer, tcpserve *tp) : _sockfd(sockd),_peer(peer),_tp(tp){}int _sockfd;InetAddr _peer;tcpserve *_tp;};static void *Rountinue(void *args){pthread_detach(pthread_self());ThreadData *td = static_cast<ThreadData *>(args);td->_tp->Service(td->_sockfd, td->_peer);delete td;return nullptr;}

V4 - Echo Server 线程池版本

#pragma once
#include "Mutex.hpp"
#include "cond.hpp"
#include "log.hpp"
#include "pthread.hpp"
#include <vector>
#include <queue>
namespace PthreadPoolModule
{using namespace MutexMoudle;using namespace CondModule;using namespace LogModule;using namespace PthreadModlue;template <typename T>class PthreadPool{private:void WakeUpAllThread(){LockGuard lockguard(_mutex);if (_sleepnum)_cond.Broadcast();LOG(LogLevel::INFO) << "唤醒所有的休眠线程";}void WakeUpOne(){_cond.Signal();LOG(LogLevel::INFO) << "唤醒一个休眠线程";}PthreadPool(int num = 17): _num(num),_isrunning(false),_sleepnum(0){for (int i = 0; i < _num; i++){_threads.emplace_back([this](){ HandlerTask(); });}}public:// 创建单例static PthreadPool<T> *GetInstance(){// 加锁防止多消费者if (_ins == nullptr){LockGuard lock(_insmutex);LOG(LogLevel::DEBUG) << "获取单例....";if (_ins == nullptr){LOG(LogLevel::DEBUG) << "首次使用单例, 创建之....";_ins = new PthreadPool<T>();_ins->Start();}}return _ins;}void Start(){if (_isrunning)return;_isrunning = true;LOG(LogLevel::INFO) << "线程池开始启动···";for (auto &ch : _threads){if (ch.Start()){LOG(LogLevel::INFO) << "线程 " << ch.Name() << " 启动成功";}else{LOG(LogLevel::ERROR) << "线程 " << ch.Name() << " 启动失败";}}}void Stop(){if (!_isrunning)return;_isrunning = false;// 进程停止// 可能有的在休眠// 有的在处理任务// 我们要把休眠的全部唤醒// 并且把任务队列的任务全部执行完// 才可以退出线程池//  唤醒所有的线层WakeUpAllThread();}void Join(){for (auto &ch : _threads){ch.Join();}}bool Enqueue(const T &in){if (_isrunning){LockGuard lock(_mutex);{_taskq.push(in);//if (_threads.size() == _sleepnum)if(_sleepnum>0)WakeUpOne();return true;}}return false;}// 该函数结束就会进行线程就运行完毕了void HandlerTask(){char name[128];pthread_getname_np(pthread_self(), name, sizeof(name));LOG(LogLevel::INFO) << name << " 处理任务...";// 消费者// 多个线程就是消费者while (true){T task;{LockGuard lock(_mutex);while (_taskq.empty() && _isrunning){_sleepnum++;_cond.Wait(_mutex);_sleepnum--;}if (!_isrunning && _taskq.empty())return;task = _taskq.front();_taskq.pop();}// 确保任务处理无阻塞,快速返回循环task(); // 任务执行在锁外}}private:std::vector<Pthread> _threads;int _num;Mutex _mutex;Cond _cond;std::queue<T> _taskq;bool _isrunning; // 用来标记线程退出int _sleepnum;   // 睡眠的线程数static PthreadPool<T> *_ins; // 单例指针// 我们用的单例在开始时没有我们的锁因此我们这把锁要定义为静态的static Mutex _insmutex;};// 静态成员的初始化template <typename T>PthreadPool<T> *PthreadPool<T>::_ins = nullptr;template <typename T>MutexMoudle::Mutex PthreadPool<T>::_insmutex;
}
#pragma once
#include "common.hpp"
#include "InetAddr.hpp"
#include"PthreadPool.hpp"
#include "log.hpp"
using task_t = std::function<void()>;
using namespace PthreadPoolModule;
using namespace LogModule;
using func_t = std::function<std::string(std::string message, InetAddr peer)>;
const static int backlog = 8;
class tcpserve : public nocopy
{
public:tcpserve(uint16_t port, func_t func): _port(port),_func(func),_isruning(false){}void Init(){// 1._listensockfd = socket(AF_INET, SOCK_STREAM, 0);if (_listensockfd < 0){LOG(LogLevel::FATAL) << "socket error";exit(SOCKET_ERR);}LOG(LogLevel::INFO) << "socket success: " << _listensockfd; // 3InetAddr local(_port);int n = bind(_listensockfd, local.NetAddrPtr(), local.NetAddrLen());if (n < 0){LOG(LogLevel::FATAL) << "bind error";exit(BIND_ERR);}LOG(LogLevel::INFO) << "bind success: " << _listensockfd; // 3// 3、第二个参数运行排队的最大申请链接数n = listen(_listensockfd, backlog);if (n < 0){LOG(LogLevel::FATAL) << "listen error";exit(LISTEN_ERR);}LOG(LogLevel::INFO) << "listen success: " << _listensockfd; // 3// listen成就代表链接上了}void Run(){// 先获得链接的sockfd_isruning = true;while (_isruning){struct sockaddr_in peer;socklen_t len = sizeof(sockaddr_in);// 没有listen到accept会堵塞int sockfd = accept(_listensockfd, CONV(peer), &len);if (sockfd < 0){// 再去申请LOG(LogLevel::WARNING) << "accept error";continue;}// 此时获取远端的地址信息成功InetAddr addr(peer);LOG(LogLevel::INFO) << "accept success, peer addr : " << addr.StringAddr();// v1//  去调用我们的服务//  Service(sockfd, addr);// v2//  pid_t pid = fork();//  if (pid > 0)//  {//      // 父进程//      close(sockfd);//      int n=waitpid(pid,nullptr,0);//这里我们不可以进行阻塞等待1.我们可以进行对信号的忽略2.创建孙子进程//      (void)n;//  }//  else if (pid == 0)//  {//      // 子进程//      close(_listensockfd);//      if(fork()>0) exit(OK);//      //孙子进程去执行service,子进程什么也不做直接等待成功等待父进程回收孙子进程则成为孤儿进程最后被系统回收//      Service(sockfd,addr);//      exit(OK);//  }//  else//  {//      close(sockfd);//      LOG(LogLevel::FATAL) << "fork error";//      exit(FORK_ERR);// }// v3多线程版本// pthread_t pid;// ThreadData *ptr = new ThreadData(sockfd, addr, this);// int n = pthread_create(&pid, nullptr, Rountinue, ptr);//v4多线程版本适合短服务PthreadPool<task_t>::GetInstance()->Enqueue([this,sockfd,addr](){this->Service(sockfd,addr);});}_isruning = false;}class ThreadData{public:ThreadData(int sockd, InetAddr peer, tcpserve *tp) : _sockfd(sockd),_peer(peer),_tp(tp){}int _sockfd;InetAddr _peer;tcpserve *_tp;};static void *Rountinue(void *args){pthread_detach(pthread_self());ThreadData *td = static_cast<ThreadData *>(args);td->_tp->Service(td->_sockfd, td->_peer);delete td;return nullptr;}void Service(int sockfd,  InetAddr peer){char buffer[1024];while (true){// 我们不断读取我们的信息int n = read(sockfd, buffer, sizeof(buffer) - 1);if (n > 0){buffer[n] = 0; // 设置为C风格字符串, n<= sizeof(buffer)-1LOG(LogLevel::DEBUG) << peer.StringAddr() << " #" << buffer;std::string echo_string = _func(buffer, peer);// 写回我们的消息write(sockfd, echo_string.c_str(), echo_string.size());}else if (n == 0){// 相当于管道的写端已经关闭了LOG(LogLevel::DEBUG) << peer.StringAddr() << " 退出了...";close(sockfd);break;}else{LOG(LogLevel::DEBUG) << peer.StringAddr() << " 异常...";close(sockfd);break;}}}~tcpserve(){}private:int _listensockfd;uint16_t _port;func_t _func;bool _isruning;
};

多线程远程命令执行

tcpserve.hpp

#pragma once
#include "common.hpp"
#include "InetAddr.hpp"
//#include"PthreadPool.hpp"
#include "log.hpp"
using task_t = std::function<void()>;
//using namespace PthreadPoolModule;
using namespace LogModule;
using func_t = std::function<std::string(std::string message, InetAddr peer)>;
const static int backlog = 8;
class tcpserve : public nocopy
{
public:tcpserve(uint16_t port, func_t func): _port(port),_func(func),_isruning(false){}void Init(){// 1._listensockfd = socket(AF_INET, SOCK_STREAM, 0);if (_listensockfd < 0){LOG(LogLevel::FATAL) << "socket error";exit(SOCKET_ERR);}LOG(LogLevel::INFO) << "socket success: " << _listensockfd; // 3InetAddr local(_port);int n = bind(_listensockfd, local.NetAddrPtr(), local.NetAddrLen());if (n < 0){LOG(LogLevel::FATAL) << "bind error";exit(BIND_ERR);}LOG(LogLevel::INFO) << "bind success: " << _listensockfd; // 3// 3、第二个参数运行排队的最大申请链接数n = listen(_listensockfd, backlog);if (n < 0){LOG(LogLevel::FATAL) << "listen error";exit(LISTEN_ERR);}LOG(LogLevel::INFO) << "listen success: " << _listensockfd; // 3// listen成就代表链接上了}void Run(){// 先获得链接的sockfd_isruning = true;while (_isruning){struct sockaddr_in peer;socklen_t len = sizeof(sockaddr_in);// 没有listen到accept会堵塞int sockfd = accept(_listensockfd, CONV(peer), &len);if (sockfd < 0){// 再去申请LOG(LogLevel::WARNING) << "accept error";continue;}// 此时获取远端的地址信息成功InetAddr addr(peer);LOG(LogLevel::INFO) << "accept success, peer addr : " << addr.StringAddr();// v1//  去调用我们的服务//  Service(sockfd, addr);// v2//  pid_t pid = fork();//  if (pid > 0)//  {//      // 父进程//      close(sockfd);//      int n=waitpid(pid,nullptr,0);//这里我们不可以进行阻塞等待1.我们可以进行对信号的忽略2.创建孙子进程//      (void)n;//  }//  else if (pid == 0)//  {//      // 子进程//      close(_listensockfd);//      if(fork()>0) exit(OK);//      //孙子进程去执行service,子进程什么也不做直接等待成功等待父进程回收孙子进程则成为孤儿进程最后被系统回收//      Service(sockfd,addr);//      exit(OK);//  }//  else//  {//      close(sockfd);//      LOG(LogLevel::FATAL) << "fork error";//      exit(FORK_ERR);// }//v3多线程版本pthread_t pid;ThreadData *ptr = new ThreadData(sockfd, addr, this);int n = pthread_create(&pid, nullptr, Rountinue, ptr);//v4多线程版本适合短服务// PthreadPool<task_t>::GetInstance()->Enqueue([this,sockfd,addr](){//     this->Service(sockfd,addr);// });}_isruning = false;}class ThreadData{public:ThreadData(int sockd, InetAddr peer, tcpserve *tp) : _sockfd(sockd),_peer(peer),_tp(tp){}int _sockfd;InetAddr _peer;tcpserve *_tp;};static void *Rountinue(void *args){pthread_detach(pthread_self());ThreadData *td = static_cast<ThreadData *>(args);td->_tp->Service(td->_sockfd, td->_peer);delete td;return nullptr;}void Service(int sockfd,  InetAddr peer){char buffer[1024];while (true){// 我们不断读取我们的信息int n = read(sockfd, buffer, sizeof(buffer) - 1);if (n > 0){buffer[n] = 0; // 设置为C风格字符串, n<= sizeof(buffer)-1LOG(LogLevel::DEBUG) << peer.StringAddr() << " #" << buffer;std::string echo_string = _func(buffer, peer);// 写回我们的消息write(sockfd, echo_string.c_str(), echo_string.size());}else if (n == 0){// 相当于管道的写端已经关闭了LOG(LogLevel::DEBUG) << peer.StringAddr() << " 退出了...";close(sockfd);break;}else{LOG(LogLevel::DEBUG) << peer.StringAddr() << " 异常...";close(sockfd);break;}}}~tcpserve(){}private:int _listensockfd;uint16_t _port;func_t _func;bool _isruning;
};

tcpserve.cc

#include"tcpserve.hpp"
#include"log.hpp"
#include"command.hpp"
using namespace LogModule;std::string defaultfunc(std::string mes,InetAddr peer)
{return "sreve say@:"+mes;
}
void Usage(std::string proc)
{std::cerr << "Usage: " << proc << " port" << std::endl;
}
int main(int argc, char *argv[])
{if (argc != 2){Usage(argv[0]);exit(USAGE_ERR);}uint16_t port = std::stoi(argv[1]);ENABLE_CONSOLE_LOG_STRATEGY();Command comd;std::unique_ptr<tcpserve> tsvr = std::make_unique<tcpserve>(port,[&comd](std::string cmd,InetAddr peer)->std::string{return comd.Excute(cmd,peer);});tsvr->Init();tsvr->Run();return 0;
}

command.hpp

#pragma once#include <iostream>
#include <string>
#include <set>
#include <unistd.h>
#include "InetAddr.hpp"
class Command
{
public:Command(){_WhiteListCommands.insert("ls");_WhiteListCommands.insert("pwd");_WhiteListCommands.insert("ls -l");_WhiteListCommands.insert("touch haha.txt");_WhiteListCommands.insert("who");_WhiteListCommands.insert("whoami");}std::string Excute(std::string mes, InetAddr who){if (!_WhiteListCommands.count(mes))return "None";FILE *fp = popen(mes.c_str(), "r");if (nullptr == fp){return std::string("你要执行的命令不存在: ") + mes;}std::string res;char buffer[1024];while (fgets(buffer, sizeof(buffer), fp)){res += buffer;}pclose(fp);std::string result = who.StringAddr() + "execute done, result is: \n" + res;return res;}~Command(){}private:std::set<std::string> _WhiteListCommands;
};

v5 引入线程池版本翻译

PthreadPool.hpp

#pragma once
#include "Mutex.hpp"
#include "cond.hpp"
#include "log.hpp"
#include "pthread.hpp"
#include <vector>
#include <queue>
namespace PthreadPoolModule
{using namespace MutexMoudle;using namespace CondModule;using namespace LogModule;using namespace PthreadModlue;template <typename T>class PthreadPool{private:void WakeUpAllThread(){LockGuard lockguard(_mutex);if (_sleepnum)_cond.Broadcast();LOG(LogLevel::INFO) << "唤醒所有的休眠线程";}void WakeUpOne(){_cond.Signal();LOG(LogLevel::INFO) << "唤醒一个休眠线程";}PthreadPool(int num = 5): _num(num),_isrunning(false),_sleepnum(0){for (int i = 0; i < _num; i++){_threads.emplace_back([this](){ HandlerTask(); });}}public:// 创建单例static PthreadPool<T> *GetInstance(){// 加锁防止多消费者if (_ins == nullptr){LockGuard lock(_insmutex);LOG(LogLevel::DEBUG) << "获取单例....";if (_ins == nullptr){LOG(LogLevel::DEBUG) << "首次使用单例, 创建之....";_ins = new PthreadPool<T>();_ins->Start();}}return _ins;}void Start(){if (_isrunning)return;_isrunning = true;LOG(LogLevel::INFO) << "线程池开始启动···";for (auto &ch : _threads){if (ch.Start()){LOG(LogLevel::INFO) << "线程 " << ch.Name() << " 启动成功";}else{LOG(LogLevel::ERROR) << "线程 " << ch.Name() << " 启动失败";}}}void Stop(){if (!_isrunning)return;_isrunning = false;// 进程停止// 可能有的在休眠// 有的在处理任务// 我们要把休眠的全部唤醒// 并且把任务队列的任务全部执行完// 才可以退出线程池//  唤醒所有的线层WakeUpAllThread();}void Join(){for (auto &ch : _threads){ch.Join();}}bool Enqueue(const T &in){if (_isrunning){LockGuard lock(_mutex);{_taskq.push(in);if(_sleepnum>0)WakeUpOne();return true;}}return false;}// 该函数结束就会进行线程就运行完毕了void HandlerTask(){char name[128];pthread_getname_np(pthread_self(), name, sizeof(name));LOG(LogLevel::INFO) << name << " 处理任务...";// 消费者// 多个线程就是消费者while (true){T task;{LockGuard lock(_mutex);while (_taskq.empty() && _isrunning){_sleepnum++;_cond.Wait(_mutex);_sleepnum--;}if (!_isrunning && _taskq.empty())return;task = _taskq.front();_taskq.pop();}// 确保任务处理无阻塞,快速返回循环task(); // 任务执行在锁外}}private:std::vector<Pthread> _threads;int _num;Mutex _mutex;Cond _cond;std::queue<T> _taskq;bool _isrunning; // 用来标记线程退出int _sleepnum;   // 睡眠的线程数static PthreadPool<T> *_ins; // 单例指针// 我们用的单例在开始时没有我们的锁因此我们这把锁要定义为静态的static Mutex _insmutex;};// 静态成员的初始化template <typename T>PthreadPool<T> *PthreadPool<T>::_ins = nullptr;template <typename T>MutexMoudle::Mutex PthreadPool<T>::_insmutex;
}

tcpserve.hpp

#pragma once
#include "common.hpp"
#include "InetAddr.hpp"
#include"PthreadPool.hpp"
#include "log.hpp"
using task_t = std::function<void()>;
using namespace PthreadPoolModule;
using namespace LogModule;
using func_t = std::function<std::string(std::string message, InetAddr peer)>;
const static int backlog = 8;
class tcpserve : public nocopy
{
public:tcpserve(uint16_t port, func_t func): _port(port),_func(func),_isruning(false){}void Init(){// 1._listensockfd = socket(AF_INET, SOCK_STREAM, 0);if (_listensockfd < 0){LOG(LogLevel::FATAL) << "socket error";exit(SOCKET_ERR);}LOG(LogLevel::INFO) << "socket success: " << _listensockfd; // 3InetAddr local(_port);int n = bind(_listensockfd, local.NetAddrPtr(), local.NetAddrLen());if (n < 0){LOG(LogLevel::FATAL) << "bind error";exit(BIND_ERR);}LOG(LogLevel::INFO) << "bind success: " << _listensockfd; // 3// 3、第二个参数运行排队的最大申请链接数n = listen(_listensockfd, backlog);if (n < 0){LOG(LogLevel::FATAL) << "listen error";exit(LISTEN_ERR);}LOG(LogLevel::INFO) << "listen success: " << _listensockfd; // 3// listen成就代表链接上了}void Run(){// 先获得链接的sockfd_isruning = true;while (_isruning){struct sockaddr_in peer;socklen_t len = sizeof(sockaddr_in);// 没有listen到accept会堵塞int sockfd = accept(_listensockfd, CONV(peer), &len);if (sockfd < 0){// 再去申请LOG(LogLevel::WARNING) << "accept error";continue;}// 此时获取远端的地址信息成功InetAddr addr(peer);LOG(LogLevel::INFO) << "accept success, peer addr : " << addr.StringAddr();// v1//  去调用我们的服务//  Service(sockfd, addr);// v2//  pid_t pid = fork();//  if (pid > 0)//  {//      // 父进程//      close(sockfd);//      int n=waitpid(pid,nullptr,0);//这里我们不可以进行阻塞等待1.我们可以进行对信号的忽略2.创建孙子进程//      (void)n;//  }//  else if (pid == 0)//  {//      // 子进程//      close(_listensockfd);//      if(fork()>0) exit(OK);//      //孙子进程去执行service,子进程什么也不做直接等待成功等待父进程回收孙子进程则成为孤儿进程最后被系统回收//      Service(sockfd,addr);//      exit(OK);//  }//  else//  {//      close(sockfd);//      LOG(LogLevel::FATAL) << "fork error";//      exit(FORK_ERR);// }//v3多线程版本// pthread_t pid;// ThreadData *ptr = new ThreadData(sockfd, addr, this);// int n = pthread_create(&pid, nullptr, Rountinue, ptr);//v4多线程版本适合短服务PthreadPool<task_t>::GetInstance()->Enqueue([this,sockfd,addr](){this->Service(sockfd,addr);});}_isruning = false;}class ThreadData{public:ThreadData(int sockd, InetAddr peer, tcpserve *tp) : _sockfd(sockd),_peer(peer),_tp(tp){}int _sockfd;InetAddr _peer;tcpserve *_tp;};static void *Rountinue(void *args){pthread_detach(pthread_self());ThreadData *td = static_cast<ThreadData *>(args);td->_tp->Service(td->_sockfd, td->_peer);delete td;return nullptr;}void Service(int sockfd,  InetAddr peer){char buffer[1024];while (true){// 我们不断读取我们的信息int n = read(sockfd, buffer, sizeof(buffer) - 1);if (n > 0){buffer[n] = 0; // 设置为C风格字符串, n<= sizeof(buffer)-1LOG(LogLevel::DEBUG) << peer.StringAddr() << " #" << buffer;std::string echo_string = _func(buffer, peer);// 写回我们的消息write(sockfd, echo_string.c_str(), echo_string.size());}else if (n == 0){// 相当于管道的写端已经关闭了LOG(LogLevel::DEBUG) << peer.StringAddr() << " 退出了...";close(sockfd);break;}else{LOG(LogLevel::DEBUG) << peer.StringAddr() << " 异常...";close(sockfd);break;}}}~tcpserve(){}private:int _listensockfd;uint16_t _port;func_t _func;bool _isruning;
};

tbcserve.cc

#include"tcpserve.hpp"
#include"log.hpp"
#include"command.hpp"
#include"dict.hpp"
using namespace LogModule;std::string defaultfunc(std::string mes,InetAddr peer)
{return "sreve say@:"+mes;
}
void Usage(std::string proc)
{std::cerr << "Usage: " << proc << " port" << std::endl;
}
int main(int argc, char *argv[])
{if (argc != 2){Usage(argv[0]);exit(USAGE_ERR);}uint16_t port = std::stoi(argv[1]);ENABLE_CONSOLE_LOG_STRATEGY();//Command comd;Dict dit;dit.LoadDict();std::unique_ptr<tcpserve> tsvr = std::make_unique<tcpserve>(port,[&dit](std::string word,InetAddr peer)->std::string{return dit.Translate(word,peer);});tsvr->Init();tsvr->Run();return 0;
}

dict.txt dict.hpp

#pragma oce
#include <map>
#include <unordered_map>
#include <string>
#include <fstream>
#include "log.hpp"
#include "InetAddr.hpp"
using namespace LogModule;
const std::string defaultdictpath = "./dict.txt";
class Dict
{
public:Dict(std::string path = defaultdictpath){_path = path;}bool LoadDict(){// 进行读取文件std::ifstream in(_path);if (!in.is_open()){LOG(LogLevel::DEBUG) << "打开字典: " << _path << " 错误";return false;}std::string sep(": ");std::string tem;while (getline(in, tem)){int pos = tem.find(sep);if (pos == std::string::npos){LOG(LogLevel::WARNING) << "解析: " << tem << " 失败";continue;}std::string english = tem.substr(0, pos);std::string chinese = tem.substr(pos + sep.size());if (english.empty() || chinese.empty()){LOG(LogLevel::WARNING) << "没有有效内容: " << tem;continue;}//std::cout<<english<<std::endl<<chinese<<std::endl;_dict.insert(make_pair(english, chinese));LOG(LogLevel::DEBUG) << "加载: " << tem;}return true;}std::string Translate(const std::string &word, InetAddr &client){//std::cout<<word<<std::endl;auto iter = _dict.find(word);if (iter == _dict.end()){LOG(LogLevel::DEBUG) << "进入到了翻译模块, [" << client.Ip() << " : " << client.Port() << "]# " << word << "->None";return "None";}LOG(LogLevel::DEBUG) << "进入到了翻译模块, [" << client.Ip() << " : " << client.Port() << "]# " << word << "->" << iter->second;return iter->second;}~Dict(){}private:std::unordered_map<std::string, std::string> _dict;std::string _path;
};apple: 苹果  
banana: 香蕉  
cat: 猫  
dog: 狗  
book: 书  
pen: 笔  
happy: 快乐的  
sad: 悲伤的  
run: 跑  
jump: 跳  
teacher: 老师  
student: 学生  
car: 汽车  
bus: 公交车  
love: 爱  
hate: 恨  
hello: 你好  
goodbye: 再见  
summer: 夏天  
winter: 冬天  
red: 红色  
blue: 蓝色  
green: 绿色  
yellow: 黄色  
mother: 母亲  
father: 父亲  
brother: 兄弟  
sister: 姐妹  
eye: 眼睛  
hand: 手  
foot: 脚  
head: 头  
rice: 米饭  
bread: 面包  
water: 水  
milk: 牛奶  
tree: 树  
mountain: 山  
river: 河流  
sun: 太阳  
day: 天  
year: 年  
month: 月  
hour: 小时  
rain: 雨  
snow: 雪  
wind: 风  
cloud: 云  
school: 学校  
house: 房子  
door: 门  
window: 窗户  
purple: 紫色  
pink: 粉色  
orange: 橙色  
gray: 灰色  
doctor: 医生  
engineer: 工程师  
artist: 艺术家  
cook: 厨师  
tiger: 老虎  
lion: 狮子  
elephant: 大象  
rabbit: 兔子  
math: 数学  
science: 科学  
history: 历史  
music: 音乐  
morning: 早晨  
night: 夜晚  
spring: 春天  
autumn: 秋天  
sing: 唱歌  
dance: 跳舞  
read: 阅读  
write: 写作  
chair: 椅子  
table: 桌子  
bed: 床  
lamp: 台灯  
moon: 月亮  
star: 星星  
computer: 电脑  
phone: 手机  
angry: 生气的  
tired: 疲倦的  
hungry: 饥饿的  
excited: 兴奋的  
bicycle: 自行车  
train: 火车  
plane: 飞机  
ship: 轮船  
white: 白色  
black: 黑色  
gold: 金色  
silver: 银色  
shirt: 衬衫  
pants: 裤子  
shoes: 鞋子  
hat: 帽子  
basketball: 篮球  
football: 足球  
swim: 游泳  
climb: 爬山  
nose: 鼻子  
ear: 耳朵  
mouth: 嘴巴  
tooth: 牙齿  
coffee: 咖啡  
tea: 茶  
juice: 果汁  
cake: 蛋糕  
classroom: 教室  
library: 图书馆  
playground: 操场  
pencil: 铅笔  
one: 一  
two: 二  
three: 三  
four: 四  
sunny: 晴朗的  
cloudy: 多云的  
rainy: 下雨的  
storm: 风暴  
grandfather: 祖父  
grandmother: 祖母  
uncle: 叔叔  
aunt: 阿姨  
keyboard: 键盘  
mouse: 鼠标  
camera: 相机  
screen: 屏幕  
forest: 森林  
ocean: 海洋  
desert: 沙漠  
grass: 草地  
slow: 慢的  
fast: 快的  
hot: 热的  
cold: 冷的  
soft: 柔软的  
hard: 坚硬的  
light: 轻的  
heavy: 重的  
city: 城市  
village: 村庄  
street: 街道  
park: 公园  
Monday: 星期一  
Friday: 星期五  
January: 一月  
December: 十二月  
left: 左边  
right: 右边  
up: 向上  
down: 向下  
open: 打开  
close: 关闭  
buy: 购买  
sell: 出售  
begin: 开始  
finish: 结束  
question: 问题  
answer: 答案  
friend: 朋友  
enemy: 敌人  
peace: 和平  
war: 战争  
success: 成功  
failure: 失败  
grape: 葡萄  
tomato: 西红柿  
potato: 土豆  
onion: 洋葱  
carrot: 胡萝卜  
noodle: 面条  
egg: 鸡蛋  
beer: 啤酒  
salt: 盐  
sugar: 糖  
finger: 手指  
leg: 腿  
heart: 心脏  
blood: 血液  
hospital: 医院  
medicine: 药  
pain: 疼痛  
healthy: 健康的  
sick: 生病的  
exercise: 锻炼  
sleep: 睡觉  
breathe: 呼吸  
second: 秒  
minute: 分钟  
today: 今天  
yesterday: 昨天  
tomorrow: 明天  
Tuesday: 星期二  
Wednesday: 星期三  
Thursday: 星期四  
Saturday: 星期六  
Sunday: 星期日  
February: 二月  
sky: 天空  
fog: 雾  
lake: 湖  
earth: 地球  
fire: 火  
ice: 冰  
plant: 植物  
room: 房间  
wall: 墙  
floor: 地板  
roof: 屋顶  
kitchen: 厨房  
bedroom: 卧室  
bathroom: 浴室  
sofa: 沙发  
clock: 钟  
mirror: 镜子  
cup: 杯子  
plate: 盘子  
spoon: 勺子  
coat: 外套  
dress: 连衣裙  
skirt: 裙子  
jacket: 夹克  
socks: 袜子  
gloves: 手套  
scarf: 围巾  
bag: 包  
watch: 手表  
ring: 戒指  
necklace: 项链  
button: 纽扣  
pocket: 口袋  
belt: 腰带  
umbrella: 雨伞  
glasses: 眼镜  
motorcycle: 摩托车  
taxi: 出租车  
road: 道路  
bridge: 桥  
station: 车站  
airport: 机场  
ticket: 票  
passport: 护照  
map: 地图  
luggage: 行李  
hotel: 酒店  
tourist: 游客  
direction: 方向  
speed: 速度  
paper: 纸  
desk: 课桌  
blackboard: 黑板  
homework: 作业  
exam: 考试  
language: 语言  
art: 艺术  
sport: 体育  
job: 工作  
office: 办公室  
meeting: 会议  
colleague: 同事  
boss: 老板  
salary: 工资  
email: 电子邮件  
document: 文件  
family: 家人  
stranger: 陌生人  
party: 聚会  
gift: 礼物  
smile: 微笑  
calm: 平静的  
bored: 无聊的  
thirsty: 口渴的  
hope: 希望  
fear: 恐惧  
courage: 勇气  
pride: 骄傲  
shame: 羞愧  
patience: 耐心  
stress: 压力  
walk: 走  
sit: 坐  
stand: 站  
eat: 吃  
drink: 喝  
speak: 说话  
listen: 听  
see: 看见  
push: 推  
pull:throw:catch: 抓住  
time: 时间  
life: 生命  
death: 死亡  
freedom: 自由  
justice: 正义  
truth: 真相  
lie: 谎言  
beauty: 美丽  
ugliness: 丑陋  
rich: 富有的  
poor: 贫穷的  
clean: 干净的  
dirty: 肮脏的  
easy: 容易的  
difficult: 困难的  
China: 中国  
America: 美国  
Japan: 日本  
France: 法国  
Germany: 德国  
country: 国家  
culture: 文化  
tradition: 传统  
festival: 节日  
food: 食物  
song: 歌曲  
movie: 电影  
game: 游戏  
money: 钱  
price: 价格  

相关文章:

Socket 编程 TCP

Socket 编程 TCP TCP socket API 详解V1 - Echo ServerV2 - Echo Server 多进程版本V3 - Echo Server 多线程版本V4 - Echo Server 线程池版本多线程远程命令执行v5 引入线程池版本翻译 TCP socket API 详解 socket(): socket()打开一个网络通讯端口,如果成功的话,就像 open…...

信息系统项目管理师-软考高级(软考高项)​​​​​​​​​​​2025最新(七)

个人笔记整理---仅供参考 项目立项管理 7.1项目建议与立项申请 项目建议书内容必背&#xff01; 7.2项目可行性研究 项目可行性研究必考 7.3项目的评估与决策...

Qt中的UIC

Qt中的UIC(User Interface Compiler, 用户界面编译器)&#xff1a;读取由Qt Widgets Designer生成的XML格式(.ui)文件并创建相应的C头文件或Python源文件。如将mainwindow.ui文件生成ui_mainwindow.h。 uic.exe位置在6.8.0\msvc2019_64\bin &#xff0c;其支持的输入参数如下所…...

【MATLAB例程】基于RSSI原理的Wi-Fi定位程序,N个锚点(数量可自适应)、三维空间,轨迹使用UKF进行滤波,附代码下载链接

本文所述程序实现了一种基于信号强度&#xff08;RSSI&#xff09;的Wi-Fi定位算法&#xff0c;并结合无迹卡尔曼滤波&#xff08;UKF&#xff09;对动态目标轨迹进行滤波优化。代码支持自适应锚点数量&#xff0c;适用于三维空间定位&#xff0c;可模拟目标运动、信号噪声及非…...

vulkanscenegraph显示倾斜模型(6.5)-vsg::DatabasePager

前言 上章深入分析了帧循环过程中&#xff0c;多线程下的记录与提交机制。本章将分析vsg::DatabasePager在更新场景图过程中的作用&#xff0c;进一步揭露vsg中场景图管理机制&#xff0c;并通过分析代码&#xff0c;详细解释vsg中场景图管理机制中的节点添加、节点删除、节点加…...

利用 Python pyttsx3实现文字转语音(TTS)

今天&#xff0c;我想跟大家分享如何利用 Python 编程语言&#xff0c;来实现文字转换为语音的功能&#xff0c;也就是我们常说的 Text-to-Speech (TTS) 技术。 你可能会好奇&#xff0c;为什么学习这个&#xff1f;想象一下&#xff0c;如果你想把书本、文章、杂志的内容转换…...

【PostgreSQL数据分析实战:从数据清洗到可视化全流程】5.1 描述性统计分析(均值/方差/分位数计算)

&#x1f449; 点击关注不迷路 &#x1f449; 点击关注不迷路 &#x1f449; 点击关注不迷路 文章大纲 5.1 描述性统计分析&#xff1a;均值、方差与分位数计算实战5.1.1 数据准备与分析目标数据集介绍分析目标 5.1.2 均值计算&#xff1a;从整体到分组分析总体均值计算加权均值…...

【PostgreSQL数据分析实战:从数据清洗到可视化全流程】5.4 数据抽样(简单随机抽样/分层抽样)

&#x1f449; 点击关注不迷路 &#x1f449; 点击关注不迷路 &#x1f449; 点击关注不迷路 文章大纲 PostgreSQL数据分析实战&#xff1a;数据抽样核心技术解析5.4 数据抽样&#xff1a;从简单随机到分层策略的深度实践5.4.1 简单随机抽样&#xff1a;概率均等的基础抽样方法…...

时间同步服务核心知识笔记:原理、配置

一、时间同步服务 在 Linux 系统中&#xff0c;准确的时间至关重要。对于服务器集群&#xff0c;时间同步确保各节点间数据处理和交互的一致性&#xff0c;避免因时间差异导致的事务处理错误、日志记录混乱等问题。在分布式系统中&#xff0c;时间同步有助于协调任务调度、数据…...

Leetcode刷题记录32——搜索二维矩阵 II

题源&#xff1a;https://leetcode.cn/problems/search-a-2d-matrix-ii/description/?envTypestudy-plan-v2&envIdtop-100-liked 题目描述&#xff1a; 思路一&#xff1a; &#x1f4a1; 解题思路&#xff1a;利用矩阵有序特性 双指针法&#xff08;Z 字形搜索&…...

【最新Python包管理工具UV的介绍和安装】

介绍 uv是一个非常快的 Python 包安装程序和 pip 解析器&#xff0c;用 Rust 编写&#xff0c;设计为pip-tools的直接替代品。 以下是官网给出的UV与其他包管理工具解决依赖&#xff08;左&#xff09;和安装包&#xff08;右&#xff09;的对比图。 可以看出UV是一个极快的 P…...

第二章-猜数游戏

猜数游戏 纸上得来终觉浅&#xff0c;绝知此事要躬行。实践才能出真知&#xff0c;因此本文内容将通过一个小项目快速帮我们上手Rust语言。其中可能会出现一些目前还不是很了解的知识&#xff0c;但没事&#xff0c;后续通过学习我们会慢慢了解的&#xff0c;现在我们先体会一…...

Go小技巧易错点100例(二十九)

随着 Go 语言的不断迭代&#xff0c;新版本带来了许多实用的标准库函数&#xff0c;使得代码更加简洁、可读性更强。本篇文章主要介绍 Go 1.21 版本中的一些新特性&#xff0c;涵盖 可变类型比较、slice 最大值与最小值、map 转换为 slice 以及 map 合并 等常见场景&#xff0c…...

游戏开发的TypeScript(5)TypeScript的类型转换

TypeScript的类型转换 游戏开发中&#xff0c;事件经常会携带一些数据&#xff0c;而这些数据会做类型上的转化&#xff0c;在 这种情况下&#xff0c;类型转换&#xff08;Type Assertion&#xff09;能够让你手动把某个值指定为特定类型。这在 TypeScript 无法自动推断出正确…...

旋转图像(中等)

借助辅助矩阵来翻转&#xff1a; 第i行第j列的元素会出现在新矩阵的第j行倒数第i列。 class Solution {public void rotate(int[][] matrix) {int n matrix.length;int[][] matrix_new new int[n][n];for (int i 0; i < n; i) {for (int j 0; j < n; j) {matrix_ne…...

慢sql处理流程和常见案例

思维导图: 在 MySQL 数据库管理中&#xff0c;慢查询是影响系统性能的常见痛点。随着 MySQL 8 版本的普及&#xff0c;其新增特性&#xff08;如 CTE、隐藏索引、JSON 格式执行计划等&#xff09;为慢查询优化提供了更强大的工具。本文结合 MySQL 8 的特性&#xff0c;通过代码…...

Kubernetes控制平面组件:Controller Manager 之 内置Controller详解

云原生学习路线导航页&#xff08;持续更新中&#xff09; kubernetes学习系列快捷链接 Kubernetes架构原则和对象设计&#xff08;一&#xff09;Kubernetes架构原则和对象设计&#xff08;二&#xff09;Kubernetes架构原则和对象设计&#xff08;三&#xff09;Kubernetes控…...

E-R图作业

1.一个图书馆借阅管理数据库要求提供下述服务&#xff1a; &#xff08;&#xff11;&#xff09;可随时查询书库中现有书籍的品种、数量与存放位置。所有各类书籍均可由书号惟一标识。 &#xff08;&#xff12;&#xff09;可随时查询书籍借还情况&#xff0c;包括借书人单位…...

debuginfo详解

debuginfo 是 Linux 系统中存储调试符号和源代码信息的特殊软件包&#xff0c;用于分析内核或用户态程序的崩溃转储文件&#xff08;如 vmcore、coredump&#xff09;。它在调试复杂问题&#xff08;如内核崩溃、程序段错误&#xff09;时至关重要。以下是其核心作用、安装方法…...

Android学习总结之GetX库篇(场景运用)

状态管理 在一个复杂的 Flutter 应用里&#xff0c;怎样借助 GetX 管理多个相互关联的状态&#xff0c;并且保证代码的可维护性和性能&#xff1f; 考察点&#xff1a;对 GetX 状态管理的深入理解&#xff0c;以及在复杂场景下运用它的能力。 解答思路&#xff1a; 采用模块…...

android-ndk开发(5): 编译运行 hello-world

android-ndk开发(5): 编译运行 hello-world 2025/05/05 1. 概要 hello-world 是每一门语言的第一个样例程序&#xff0c; 跑通它&#xff0c; 在一段时间内你会相当顺畅&#xff1a; 可以边学边实验&#xff0c; 根据运行结果得到反馈。 而对于 android-ndk 开发而言&#…...

【PostgreSQL数据分析实战:从数据清洗到可视化全流程】6.1 客户分群分析(RFM模型构建)

&#x1f449; 点击关注不迷路 &#x1f449; 点击关注不迷路 &#x1f449; 点击关注不迷路 文章大纲 PostgreSQL数据分析实战&#xff1a;RFM模型构建实现客户分群分析6.1 客户分群分析——RFM模型构建6.1.1 RFM模型核心指标解析6.1.2 数据准备与清洗规范数据表结构设计数据清…...

stm32之TIM定时中断详解

目录 1.引入1.1 简介1.2 类型1.2.1 基本定时器1.2.2 通用定时器1. 触发控制单元 (Trigger Control Unit)2. 输入捕获单元 (Input Capture Unit)3. 输出比较单元 (Output Compare Unit)4. CNT 计数器5. 自动重装载寄存器 (ARR)6. 预分频器 (PSC)7. 中断与 DMA 事件8. 刹车功能 (…...

【Hive入门】Hive安全管理与权限控制:用户认证与权限管理深度解析

目录 引言 1 Hive安全管理体系概述 2 Hive用户认证机制 2.1 Kerberos集成认证 2.1.1 Kerberos基本原理 2.1.2 Hive集成Kerberos配置步骤 2.1.3 Kerberos认证常见问题排查 2.2 LDAP用户同步 2.2.1 LDAP协议概述 2.2.2 Hive集成LDAP配置 2.2.3 LDAP与Hive用户同步架构…...

解决DNS劫持问题

什么是DNS劫持&#xff1f; DNS劫持&#xff08;DNS Hijacking&#xff09;是指通过篡改DNS配置&#xff0c;将用户的域名解析请求引导到恶意服务器的攻击方式。这种攻击常见于恶意软件、路由器漏洞或DNS配置被修改的情况下。攻击者通过这种方式控制了用户访问的网站&#xff…...

android-ndk开发(1): 搭建环境

android-ndk开发(1): 搭建环境 2025/05/05 1. 目的 写一些 C/C 代码&#xff0c; 例如 cv 基础算法&#xff0c; 并交叉编译到 android 平台。 不涉及 JNI、 Java、 Kotlin&#xff0c; 暂不涉及 rust。 基本上能适用于华为鸿蒙的 ohos ndk。 那么&#xff0c; 为了完成交叉…...

力扣面试150题-- 翻转二叉树

Day 41 题目描述 做法 /*** Definition for a binary tree node.* public class TreeNode {* int val;* TreeNode left;* TreeNode right;* TreeNode() {}* TreeNode(int val) { this.val val; }* TreeNode(int val, TreeNode left, TreeNode right…...

开源模型应用落地-qwen模型小试-Qwen3-8B-推理加速-vLLM(一)

一、前言 随着大语言模型的参数规模持续膨胀,如何在有限算力下实现高效推理成为行业焦点。阿里云推出的Qwen3-8B,凭借其卓越的语言理解与生成能力,已在多个基准测试中展现竞争力。而vLLM框架作为大模型部署的“加速器”,通过PagedAttention实现内存的高效管理,并支持连续批…...

brep2seq kaggle安装 micromamba conda环境

https://github.com/zhangshuming0668/Brep2Seq Micromamba Installation — documentation !curl -Ls https://micro.mamba.pm/api/micromamba/linux-64/latest | tar -xvj bin/micromamba A Synthetic CAD Models Dataset for Deep Learning kaggle只有20g&#xff0c;等我有…...

钩子函数和参数:Vue组件生命周期中的自定义逻辑

&#x1f90d; 前端开发工程师、技术日更博主、已过CET6 &#x1f368; 阿珊和她的猫_CSDN博客专家、23年度博客之星前端领域TOP1 &#x1f560; 牛客高级专题作者、打造专栏《前端面试必备》 、《2024面试高频手撕题》、《前端求职突破计划》 &#x1f35a; 蓝桥云课签约作者、…...

SpringBoot企业级开发之【文件上传】

看一下我们的需求&#xff1a; 接口文档&#xff1a; 分析思路&#xff1a; 现在我们先实现后端先&#xff1a; 实操&#xff1a; 一.存储到本地磁盘&#xff1a; 1.Controller 创建一个FileUploadController类 package org.huangyingyuan.controller;import org.huangyingyu…...

Linux系统安装PaddleDetection

一、安装cuda 1. 查看设备 先输入nvidia-smi&#xff0c;查看设备支持的最大cuda版本&#xff0c;选择官网中支持的cuda版本 https://www.paddlepaddle.org.cn/install/quick?docurl/documentation/docs/zh/install/conda/linux-conda.html 2. 下载CUDA并安装 使用快捷键…...

JVM 内存结构全解析

带你深入 JVM 内存结构,搞懂运行时数据区到底是怎么回事 JVM 的内存结构到底长什么样?程序计数器、虚拟机栈、堆、方法区、直接内存到底有什么用?这篇文章将从实际运行角度出发, 用一篇文章彻底讲透 JVM 的运行时数据区。一、为什么你必须搞懂 JVM 内存结构? 在一次线上…...

K8S node ARP 表爆满 如何优化

当 Kubernetes 节点的 ARP 表爆满时&#xff0c;可能会导致网络通信故障。以下是针对该问题的优化策略和解决方案&#xff1a; 一、ARP 表爆满的危害 网络不通&#xff1a;新的 ARP 请求无法被处理&#xff0c;导致数据包无法转发。性能下降&#xff1a;ARP 表查找效率降低&a…...

SpringMVC——第7章:HttpMessageConverter

一、HttpMessageConverter HttpMessageConverter是Spring MVC中非常重要的一个接口。翻译为&#xff1a;HTTP消息转换器。该接口下提供了很多实现类&#xff0c;不同的实现类有不同的转换方式。 1.什么是HTTP消息 HTTP消息其实就是HTTP协议。HTTP协议包括请求协议和响应协议。…...

【PostgreSQL数据分析实战:从数据清洗到可视化全流程】4.4 异构数据源整合(CSV/JSON/Excel数据导入)

&#x1f449; 点击关注不迷路 &#x1f449; 点击关注不迷路 &#x1f449; 点击关注不迷路 文章大纲 PostgreSQL异构数据源整合&#xff1a;CSV/JSON/Excel数据导入全攻略4.4 异构数据源整合&#xff1a;多格式数据导入实战4.4.1 CSV数据导入&#xff1a;高效批量处理4.4.1.1…...

在macOS上安装windows系统

使用Boot Camp 1. 准备工作&#xff1a;确认Mac满足Boot Camp系统要求&#xff0c;准备好Windows安装光盘或ISO映像文件&#xff0c;以及一个至少8GB的空白USB闪存驱动器用于保存驱动程序。 2. 打开Boot Camp助理&#xff1a;在“应用程序”文件夹的“实用工具”中找到“Boot…...

最长回文子串(动规 + 中心拓展)

目录 [BM73 最长回文子串](https://www.nowcoder.com/practice/b4525d1d84934cf280439aeecc36f4af?tpId295&tags&title&difficulty0&judgeStatus0&rp0&sourceUrl/exam/oj?questionJobId10&subTabNameonline_coding_page)1. 动态规划(1)状态表示:…...

学习海康VisionMaster之亮度测量

一&#xff1a;进一步学习了 今天学习下VisionMaster中的亮度测量&#xff1a;这个和前面学习的都不一样了&#xff0c;这个是测量ROI区域内的平均亮度等 1&#xff1a;什么是亮度测量&#xff1f; 我们工业上用的相机里面有一个感光芯片&#xff08;CCD/CMOS&#xff09;&…...

LeetCode 238:除自身以外数组的乘积(Java实现)

文章目录 **题目描述**解决思路1. 两次遍历法&#xff08;左右乘积法&#xff09;2. 核心思想 Java代码实现复杂度分析示例说明步骤分解 注意事项总结 题目描述 给定一个整数数组 nums&#xff0c;返回一个数组 answer&#xff0c;其中 answer[i] 等于 nums 中除 nums[i] 之外…...

LintCode第23题-判断数字与字母字符 第145题-大小写转换 第283题-三数之中的最大值

思路: 直接使用包装类的方法来判断 比如: isLetter(char c)判断是否是字母&#xff08;包括大小写、非英语字母也行&#xff09; isDigit(char c)判断是否是数字&#xff08;0~9&#xff09; isLetterOrDigit(char c)是否是字母或数字&#xff08;等价于 isLetter isLower…...

Visual Studio 项目转Qt项目

1. 先确保qmake 和 minGW &#xff08;g&#xff09; 路径都在系统变量内&#xff1b;或者通过WinR -> cmd 来检测&#xff0c; 如果能够 显示qmake 的信息 &#xff0c; g 的信息 &#xff0c; 就说明设置环境变量成功。 2. 打开项目文件夹&#xff0c;在这里打开cmd, 换…...

判断字符是否唯一 --- 位运算

目录 一&#xff1a;题目 二&#xff1a;算法与原理 三&#xff1a;代码分析 一&#xff1a;题目 题目链接&#xff1a;面试题 01.01. 判定字符是否唯一 - 力扣&#xff08;LeetCode&#xff09; 二&#xff1a;算法与原理 三&#xff1a;代码分析 class Solution { publ…...

react路由使用方法

react路由常用方法 一、router安装与基础路由二、路由跳转三、路由参数四、路由嵌套无论是小程序端、web端还是移动端前端开发都需要使用到路由组件,学会了路由之后便可以灵活开发各种交互页面。可以说路由在前端开发中占有非常重要的位置。在React中,路由使用方式和Vue比较相…...

Wannier90文件与参数

Wannier90源码https://github.com/wannier-developers/wannier90/releases/tag/v3.1.0 用法 Wannier90 可以以两种模式运行&#xff1a; 后处理模式 Post-processing mode&#xff1a;从文件中读取第一性原理代码计算得到的重叠和投影。我们预计这是使用 wannier90 最常见的…...

学习黑客Nmap 原理

练气期第一重 — 神识探查术&#xff08;Nmap 原理&#xff09; 场景设定 诸位道友&#xff08;学生&#xff09;刚踏入信息安全修真界&#xff0c;手中只有一柄“网路灵剑”&#xff08;本地终端&#xff09;。想要探知远处服务器的灵脉&#xff08;端口&#xff09;、功法&am…...

VBA信息获取与处理专题五:VBA利用CDO发送电子邮件

《VBA信息获取与处理》教程(版权10178984)是我推出第六套教程&#xff0c;目前已经是第一版修订了。这套教程定位于最高级&#xff0c;是学完初级&#xff0c;中级后的教程。这部教程给大家讲解的内容有&#xff1a;跨应用程序信息获得、随机信息的利用、电子邮件的发送、VBA互…...

Git 第一讲---基础篇 git基础概念与操作

前言&#xff1a; Git&#xff0c;作为目前全球最流行的分布式版本控制系统&#xff0c;以其高效、灵活和强大的分支管理能力&#xff0c;成为开发者手中不可或缺的工具。从个人开源项目到企业级应用&#xff0c;Git的身影无处不在。然而&#xff0c;对初学者而言&#xff0c;…...

心衰生物标志物NT-ProBNP和BNP

B型利钠肽&#xff08;BNP&#xff09;和N末端B型利钠肽原&#xff08;NT-proBNP&#xff09;都属于利尿钠肽&#xff08;NP&#xff09;家族。当发生心衰时&#xff0c;NT-ProBNP和BNP的浓度会升高&#xff0c;它们是心衰&#xff08;HF&#xff09;和心功能障碍诊疗中应用最广…...

Winform(11.案例讲解1)

今天写两个案例,用于更好的理解控件的使用 在写之前先写一个类 using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; namespace _1.案例讲解 { internal class Student { public string …...