【网络】:网络套接字(TCP)

网络套接字(TCP)

  • 一.编写TCP服务器
  • 二.编写Tcp客户端
  • 三.多进程
  • 四.多线程版本
  • 五.线程池版+完整源代码
  • 六.使用示例

一.编写TCP服务器

1.先搭一个架子

在这里插入图片描述

2.创建sockfd

在这里插入图片描述

domain参数依然是AF_INET(因为是IPV4)

在这里插入图片描述

type方式选择SOCK_STREAM(提供可靠的连接方式)

在这里插入图片描述

protocol协议类型默认缺省0

当然创建套接字是有可能失败的,所以这里加了一个日志(lg)文件,如果失败就打印日志(下面也会有该日志的完整源代码),注意:也可以直接打印在显示器上,但用日志更规范。

在这里插入图片描述

3创建并填充结构体

这里需要注意的是,填port时要使用htons函数转换成网络字节序(也就是大端),同时ip也要从字符串类型转换成int类型(有多种函数,这里选择inet_aton)。具体原因可以看上一篇博客(Udp)。

在这里插入图片描述

4.绑定内核

在这里插入图片描述

参数:1.绑定的套接字,2.绑定的结构体,3.结构体大小。

在这里插入图片描述

5.进行监听

TCP与UDP不同的是,它是面向连接的,服务器一般是比较“被动”的,服务器一直要处于等待连接的状态,我们一般称为监听。

在这里插入图片描述

在这里插入图片描述

6.对main函数进行构建,使用命令行传参方式传入端口号

在这里插入图片描述

7.接着编写Start部分代码

主要分为两步:1.获取新连接,2.根据新连接来通信。

在这里插入图片描述

这里的三个参数不必多说,关键在它的返回值也是一个套接字。它与最开始创建的套接字的功能划分不同,返回的这个套接字才是主要提供服务的。而最开始的套接字是帮助创建监听的,所以一般称它为listensock_。

在这里插入图片描述

8.编写服务代码

由于Tcp是面向字节流的,所以我们可以直接使用文件操作函数进行操作。

在这里插入图片描述

9.进行阶段性测试

由于还没有写客户端,所以我们可以使用telnet,它可以指定服务远程登陆,ip:127.0.0.1表示本地环回,端口号8888。如果没有该命令,可以yum -y install telnet。注意:下面我将Main.cc文件编译成了Tcpserver,启动该文件,在后面跟上端口号8888.

在这里插入图片描述

接着ctrl+6+],然后回车,就可以发消息了。

在这里插入图片描述

退出:ctrl+6+],输入quit。

在这里插入图片描述

二.编写Tcp客户端

1.创建套接字

在这里插入图片描述

2.绑定

客户端需要进行绑定吗?答案是需要的,但是不需要像服务器一样显示的绑定。因为无论是Udp还是Tcp,它都是socket进行通信,一定需要ip+port来进行区别,这样多个主机间才能进行区分。对于服务器来说,这个端口号要确定,因为其它客户端要对服务器发消息,就需要知道服务器的端口号。对于客户端来说,这个端口号是多少不重要,重要的是唯一,因为客户端对服务器发消息后,服务器就能通过结构体知道你的端口号和ip,所以客户端不需要显示的绑定。

Udp是在第一次发消息时绑定,而Tcp在发起connect时进行绑定。

在这里插入图片描述

在生活中,我们通过各种广告推广或者商店来获取服务器的ip和port,在这里我们修改一下main函数来收到传入服务器的ip和port。

在这里插入图片描述

3.测试

在这里插入图片描述

在这里插入图片描述

这样算是完成了一个框架,但有一个问题是目前是单进程,也就是说同时只能有一个客户端,很明显不符合预期,接下来改成多进程版。

三.多进程

在这里插入图片描述

在这里插入图片描述

首先为什么要close(listensockfd_)呢?因为我们只需要父进程一个提供监听功能就行了。为什么要close(sockfd)呢?因为我们将具体的服务功能交给了子进程,所以父进程就不需要这个fd了。当然这并没有完成多进程版,因为父进程会一直阻塞,有多种解决办法,这里选择再创建孙子进程,让子进程直接释放,孙子进程交给OS管理,这样父进程就不会阻塞了。(不了解的可以看看博客进程等待)

在这里插入图片描述

但是这种创建进程的成本实在太高了,一般连个三四十个就无了,所以为了高性能,我们可以搞一个多线程版本。

四.多线程版本

在创建线程后,我们使用pthread_detach,将主线程和其它线程分离,这样在其它线程提供服务时,主线程依然能自主运行。(不了解该函数的可以看博客线程(二))

在这里插入图片描述

在这里插入图片描述

对于线程创建不熟悉的可以看看博客线程(一),这里因为线程函数是在类内创建的,会自带一个this指针,不符合参数要求,所以改成静态的。接下来给线程函数进行传参。(由于线程函数是静态的,不能直接使用类内函数,所以我们可以将Tcpserver类一并当参数传入)

在这里插入图片描述
在这里插入图片描述

在这里插入图片描述

显然创建线程依然有代价,服务器在运行时一直有用户进入退出,所以会不断创建销毁,那么我们如何将线程多次利用呢?下面来线程池版。

五.线程池版+完整源代码

具体线程池代码如何写,可以看我的博客线程池,这里就直接使用了。简单来说就是向线程池里不断push任务,然后多个线程去竞争任务。 可以使用该命令检测线程个数ps -aL | head -1 && ps -aL | grep 程序名。

注意:为了保证服务器不被长时间连接,当前服务是只提供一次,使用客户端发消息时,每发一条就会重新建立一个连接。

在这里插入图片描述

makefile

.PHONY:all
all:Tcpserver TcpclientTcpserver:Main.ccg++ -o $@ $^ -std=c++11 -lpthread
Tcpclient:Tcpclient.ccg++ -o $@ $^ -std=c++11.PHONY:clean
clean:rm -f Tcpclient Tcpserver

log.hpp

#pragma once#include <iostream>
#include <time.h>
#include <stdarg.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include <stdlib.h>#define SIZE 1024#define Info 0
#define Debug 1
#define Warning 2
#define Error 3
#define Fatal 4#define Screen 1
#define Onefile 2
#define Classfile 3#define LogFile "log.txt"class Log
{
public:Log(){printMethod = Screen;path = "./log/";}void Enable(int method){printMethod = method;}std::string levelToString(int level){switch (level){case Info:return "Info";case Debug:return "Debug";case Warning:return "Warning";case Error:return "Error";case Fatal:return "Fatal";default:return "None";}}void printLog(int level, const std::string &logtxt){switch (printMethod){case Screen:std::cout << logtxt << std::endl;break;case Onefile:printOneFile(LogFile, logtxt);break;case Classfile:printClassFile(level, logtxt);break;default:break;}}void printOneFile(const std::string &logname, const std::string &logtxt){std::string _logname = path + logname;int fd = open(_logname.c_str(), O_WRONLY | O_CREAT | O_APPEND, 0666); // "log.txt"if (fd < 0)return;write(fd, logtxt.c_str(), logtxt.size());close(fd);}void printClassFile(int level, const std::string &logtxt){std::string filename = LogFile;filename += ".";filename += levelToString(level); // "log.txt.Debug/Warning/Fatal"printOneFile(filename, logtxt);}~Log(){}void operator()(int level, const char *format, ...){time_t t = time(nullptr);struct tm *ctime = localtime(&t);char leftbuffer[SIZE];snprintf(leftbuffer, sizeof(leftbuffer), "[%s][%d-%d-%d %d:%d:%d]", levelToString(level).c_str(),ctime->tm_year + 1900, ctime->tm_mon + 1, ctime->tm_mday,ctime->tm_hour, ctime->tm_min, ctime->tm_sec);va_list s;va_start(s, format);char rightbuffer[SIZE];vsnprintf(rightbuffer, sizeof(rightbuffer), format, s);va_end(s);// 格式:默认部分+自定义部分char logtxt[SIZE * 2];snprintf(logtxt, sizeof(logtxt), "%s %s", leftbuffer, rightbuffer);// printf("%s", logtxt); // 暂时打印printLog(level, logtxt);}private:int printMethod;std::string path;
};Log lg;

ThreadPool.hpp

#pragma once#include <iostream>
#include <vector>
#include <string>
#include <queue>
#include <pthread.h>
#include <unistd.h>struct ThreadInfo
{pthread_t tid;std::string name;
};static const int defalutnum = 5;template <class T>
class ThreadPool
{
public:void Lock(){pthread_mutex_lock(&mutex_);}void Unlock(){pthread_mutex_unlock(&mutex_);}void Wakeup(){pthread_cond_signal(&cond_);}void ThreadSleep(){pthread_cond_wait(&cond_, &mutex_);}bool IsQueueEmpty(){return tasks_.empty();}std::string GetThreadName(pthread_t tid){for (const auto &ti : threads_){if (ti.tid == tid)return ti.name;}return "None";}public:static void *HandlerTask(void *args){ThreadPool<T> *tp = static_cast<ThreadPool<T> *>(args);std::string name = tp->GetThreadName(pthread_self());while (true){tp->Lock();while (tp->IsQueueEmpty()){tp->ThreadSleep();}T t = tp->Pop();tp->Unlock();t();}}void Start(){int num = threads_.size();for (int i = 0; i < num; i++){threads_[i].name = "thread-" + std::to_string(i + 1);pthread_create(&(threads_[i].tid), nullptr, HandlerTask, this);}}T Pop(){T t = tasks_.front();tasks_.pop();return t;}void Push(const T &t){Lock();tasks_.push(t);Wakeup();Unlock();}static ThreadPool<T> *GetInstance(){if (nullptr == tp_) // ???{pthread_mutex_lock(&lock_);if (nullptr == tp_){std::cout << "log: singleton create done first!" << std::endl;tp_ = new ThreadPool<T>();}pthread_mutex_unlock(&lock_);}return tp_;}private:ThreadPool(int num = defalutnum) : threads_(num){pthread_mutex_init(&mutex_, nullptr);pthread_cond_init(&cond_, nullptr);}~ThreadPool(){pthread_mutex_destroy(&mutex_);pthread_cond_destroy(&cond_);}ThreadPool(const ThreadPool<T> &) = delete;const ThreadPool<T> &operator=(const ThreadPool<T> &) = delete; // a=b=c
private:std::vector<ThreadInfo> threads_;std::queue<T> tasks_;pthread_mutex_t mutex_;pthread_cond_t cond_;static ThreadPool<T> *tp_;static pthread_mutex_t lock_;
};template <class T>
ThreadPool<T> *ThreadPool<T>::tp_ = nullptr;template <class T>
pthread_mutex_t ThreadPool<T>::lock_ = PTHREAD_MUTEX_INITIALIZER;

Task.hpp

#pragma once
#include <iostream>
#include <string>
#include <string.h>
#include "log.hpp"extern Log lg;class Task
{
public:Task(int sockfd, const std::string &clientip, const uint16_t &clientport): sockfd_(sockfd), clientip_(clientip), clientport_(clientport){}Task(){}void run(){// 测试代码,收一个消息再发回去char buffer[4096];ssize_t n = read(sockfd_, buffer, sizeof(buffer));if (n > 0){buffer[n] = 0;std::cout << "client say#" << buffer << std::endl;std::string echo_string = "tcpserver echo#";echo_string += buffer;n=write(sockfd_, echo_string.c_str(), echo_string.size());if(n<0){lg(Warning, "write error,errorno:%d,errstring:%s", errno, strerror(errno));}}else if (n == 0) // 结束服务{lg(Info, "quit,sever close sockfd:%d", sockfd_);}else // 出错{lg(Warning, "read error,sockfd:%d,client port:%d,client ip:%s", sockfd_, clientport_, clientip_.c_str());}close(sockfd_);}void operator()(){run();}~Task(){}private:int sockfd_;std::string clientip_;uint16_t clientport_;
};

Tcpserver.hpp

#pragma once#include <iostream>
#include <string>
#include <cstdlib>
#include <cstring>
#include <unistd.h>
#include <sys/wait.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <netinet/in.h>
#include <pthread.h>
#include <signal.h>
#include <signal.h>
#include "Task.hpp"
#include "log.hpp"
#include "ThreadPool.hpp"
const int defaultfd = -1;
const std::string defaultip = "0.0.0.0";
const int backlog = 10; // 一般不要设置太大// 枚举错误代码,方便查阅
enum
{UseageError = 1,SocketError,BindError,ListenError,
};// //线程传参
// class ThreadData
// {
// public:
//   ThreadData(int fd,const std::string &ip,uint16_t &p,TcpServer*tp): sockfd(fd),clientip(ip),clientport(p),tsvr(tp)
//   {}
// public:
//   int sockfd;
//   std::string clientip;
//   uint16_t clientport;
//   TcpServer* tsvr;
// };class TcpServer
{
public:TcpServer(const uint16_t &port, const std::string &ip = defaultip) : listensockfd_(defaultfd), port_(port), ip_(ip){}void InitServer(){listensockfd_ = socket(AF_INET, SOCK_STREAM, 0);if (listensockfd_ < 0) // 创建失败{lg(Fatal, "create listensockfd,errno:%d,errstring:%s", errno, strerror(errno));exit(SocketError);}// 如果成功,打印成功信息lg(Info, "create listensockfd sucess:%d", listensockfd_);// 创建并填充结构体struct sockaddr_in local;                  // 创建结构体memset(&local, 0, sizeof(local));          // 将内部清零local.sin_family = AF_INET;                // 填充类型local.sin_port = htons(port_);             // 填充端口号inet_aton(ip_.c_str(), &(local.sin_addr)); // 填充ip地址// 绑定if (bind(listensockfd_, (struct sockaddr *)&local, sizeof(local))) // 如果绑定失败{lg(Fatal, "bind listensockfd,errno:%d,errstring:%s", errno, strerror(errno));exit(BindError);}// 监听if (listen(listensockfd_, backlog) < 0) // 如果失败{lg(Fatal, "listen error,errno:%d,errstring:%s", errno, strerror(errno));exit(ListenError);}lg(Info, "listen sucess,listensockfd:%d", listensockfd_);}// //线程函数// static void* Routine(void*args)// {//   pthread_detach(pthread_self());//分离线程//   ThreadData*td=static_cast<ThreadData*>(args);//   td->tsvr->Serverce(td->sockfd,td->clientip,td->clientport);//提供服务//   delete td;//   return nullptr;// }void Start(){signal(SIGPIPE,SIG_IGN);ThreadPool<Task>::GetInstance()->Start();//启动线程池lg(Info, "tcpServer if running.....");while (true){// 1.获取新连接struct sockaddr_in client;socklen_t len = sizeof(client);int sockfd = accept(listensockfd_, (struct sockaddr *)&client, &len);if (sockfd < 0){lg(Warning, "accept error,errorno:%d,errstring:%s", errno, strerror(errno));continue;}// 接收客户端的port和ipuint16_t clientport = ntohs(client.sin_port);char clientip[32];inet_ntop(AF_INET, &(client.sin_addr), clientip, sizeof(clientip));// 2.根据新连接进行通信lg(Info, "get a new link,sockfd:%d,client port:%d,client ip:%s", sockfd, clientport, clientip);// 3.服务//3.1 单进程版//Serverce(sockfd, clientip, clientport);//close(sockfd);//3.2 多进程版// pid_t id=fork();// if(id==0)//子进程// {//   close(listensockfd_);//   if(fork()>0) exit(0);//创建孙子进程,释放子进程//   Serverce(sockfd, clientip, clientport);//孙子进程提供服务//   close(sockfd);//服务完成,关闭服务//   exit(0);//退出进程// }// //父进程// close(sockfd);// pid_t rid=waitpid(id,nullptr,0);// (void)rid;//3.3多线程版本// ThreadData*td=new ThreadData(sockfd,clientip,clientport,this);// pthread_t tid;// pthread_create(&tid,nullptr,Routine,td);//3.4线程池版本Task t(sockfd, clientip, clientport);ThreadPool<Task>::GetInstance()->Push(t);}}// void Serverce(const int sockfd, const std::string &clientip, const uint16_t clientport)// {//   // 测试代码,收一个消息再发回去//   while (true)//   {//     char buffer[4096];//     ssize_t n = read(sockfd, buffer, sizeof(buffer));//     if (n > 0)//     {//       buffer[n] = 0;//       std::cout << "buffer say#" << buffer << std::endl;//       std::string echo_string = "tcpserver echo#";//       echo_string += buffer;//       write(sockfd, echo_string.c_str(), echo_string.size());//     }//     else if(n==0)//结束服务//     {//       lg(Info,"quit,sever close sockfd:%d",sockfd);//       break;//     }//     else//出错//     {//       lg(Warning, "read error,sockfd:%d,client port:%d,client ip:%s", sockfd, clientport, clientip.c_str());//       break;//     }//   }// }~TcpServer(){}private:int listensockfd_; // 监听套接字std::string ip_;   // ip地址uint16_t port_;    // 端口号
};

Tcpclient.cc

#include <iostream>
#include <unistd.h>
#include <string.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <netinet/in.h>void Usage(char *proc)
{std::cout << "\n\rUsage:" << proc << "severip serverport" << std::endl;
}int main(int argc, char *argv[])
{if (argc != 3){Usage(argv[0]);exit(1);}// 获取服务器的ip和portstd::string serverip = argv[1];uint16_t serverport = std::stoi(argv[2]);// 填充结构体struct sockaddr_in server;memset(&server, 0, sizeof(server));server.sin_family = AF_INET;server.sin_port = htons(serverport);inet_pton(AF_INET, serverip.c_str(), &(server.sin_addr));while (true){// 创建套接字int sockfd = socket(AF_INET, SOCK_STREAM, 0);if (sockfd < 0){std::cerr << "socket error" << std::endl;return 1;}// 进行连接int n = connect(sockfd, (struct sockaddr *)&server, sizeof(server));if (n < 0) // 如果连接失败{std::cerr << "connect error...." << std::endl;return 2;}// 测试代码std::string message;std::cout << "Please Enter#";getline(std::cin, message);n = write(sockfd, message.c_str(), message.size());if (n < 0){std::cerr << "write error....." << std::endl;continue;}char buffer[4096];n = read(sockfd, buffer, sizeof(buffer));if (n > 0){buffer[n] = 0;std::cout << buffer << std::endl;}close(sockfd);}return 0;
}

Main.cc

#include"Tcpserver.hpp"
#include<memory>void Usage(std::string proc)
{std::cout<<"\n\rUsage:"<<proc<<"port[1024+]\n"<<std::endl;
}int main(int argc,char*argv[])
{if(argc!=2)//如果传参数不对,提醒一下{Usage(argv[0]);exit(UseageError);}uint16_t port=std::stoi(argv[1]);std::unique_ptr<TcpServer> tcp_svr(new TcpServer(port));tcp_svr->InitServer();tcp_svr->Start();return 0;
}

六.使用示例

在这里插入图片描述

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

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

相关文章

从零开始学Linux之chkconfig命令

首先我们要知道Linux系统有以下7种运行级别&#xff0c;分别用0到6数字表示&#xff0c;这里使用红帽6系统做演示 0&#xff1a;关机 1&#xff1a;单用户模式&#xff0c;root用户免密登录&#xff0c;用于系统维护&#xff0c;例如忘记用户密码可以切换到该模式直接使用roo…

查看本地mysql的密码

1.导出链接 在数据库连接工具找到导出链接 导出到桌面即可&#xff0c;注意导出密码 2.复制密码 3.解密 复制如下代码到网址&#xff1a;代码在线运行 - 在线工具 (tool.lu) <?php namespace FatSmallTools; class NavicatPassword {protected $version 0;protected $a…

YOLOv5算法进阶改进(14)— 即插即用的动态卷积之ODConv | 助力涨点

​ 前言:Hello大家好,我是小哥谈。动态卷积(Dynamic Convolution)是一种用于目标检测的卷积神经网络模块,其中ODConv(Object Detection Convolution)是其一种具体实现。动态卷积在传统的卷积操作上引入了动态权重,以适应不同目标的形状和尺度变化。本文将YOLOv5的主干网…

Python实战:使用DrissionPage库爬取拉勾网职位信息

DrissionPage库&#xff0c;号称可以把Selenium按在地上摩擦&#xff01; 常规情况下&#xff0c;我们借助 requests 库爬取不加密的网站&#xff0c;使用 Selenium 库爬取加密的网站。 requests 效率高&#xff0c;但是解密难度大。Selenium 库可以实现网页自动化&#xff0c…

【星海随笔】RYU控制器(一)

Ryu is a component-based software defined networking framework.   Ryu是一个基于组件的软件定义网络框架 Ryu supports various protocols for managing network devices, such as OpenFlow, Netconf, OF-config, etc.   支持管理网络设备的多种协议 About OpenFlow, R…

Redis 布隆过滤器

布隆过滤器 这一篇文章主要是记录布隆过滤器的使用和认识 主要参考了如下的blog https://blog.csdn.net/weixin_42972832/article/details/131211665 他讲的还不错 简单的来说,布隆过滤器,实际上就像是一个集合,拿redis的key来举例来说,布隆过滤器的设置就是去过滤不属于redi…

视频融合平台EasyCVR推流成功但平台显示不在线是什么原因?

TSINGSEE青犀视频监控汇聚平台EasyCVR可拓展性强、视频能力灵活、部署轻快&#xff0c;可支持的主流标准协议有国标GB28181、RTSP/Onvif、RTMP等&#xff0c;以及支持厂家私有协议与SDK接入&#xff0c;包括海康Ehome、海大宇等设备的SDK等。平台既具备传统安防视频监控的能力&…

环形链表找入环点----链表OJ---三指针

https://leetcode.cn/problems/linked-list-cycle-ii/description/?envTypestudy-plan-v2&envIdtop-100-liked 首先&#xff0c;需要判断是否有环&#xff0c;而这里我们不单纯判断是否有环&#xff0c;还要为下一步做准备&#xff0c;需要让slow指针和fast都从头结点开始…

使用pygame建立一个简单的使用键盘方向键移动的方块小游戏

import pygame import sys# 初始化pygame pygame.init()# 设置窗口大小 screen_size (640, 480) # 创建窗口 screen pygame.display.set_mode(screen_size) # 设置窗口标题 pygame.display.set_caption("使用键盘方向键移动的方块的简单小游戏")# 设置颜色 bg_colo…

MQ回顾之rabbitmq速通

rabbitMQ相对来说功能比较完善&#xff0c;吞吐量会低一点。 持续更新…… 安装 docker 测试选择docker安装 官方安装操作 1、docker pull rabbitmq:latest 2、docker run -d --hostname my-rabbit --name some-rabbit -p 15672:15672 -p 5672:5672 rabbitmq 3、docker…

transformer_正余弦位置编码代码笔记

transformer_正余弦位置编码代码笔记 transformer输入的序列中&#xff0c;不同位置的相同词汇可能会表达不同的含义&#xff0c;通过考虑位置信息的不同来区分序列中不同位置的相同词汇。 位置编码有多种方式&#xff0c;此处仅记录正余弦位置编码 正余弦位置编码公式如下&…

idea+javafx的真正打包方式

概述&#xff1a;看到网上很多乱说javafx如何打包的&#xff0c;这里写一篇真正可以打包javafx的。 注&#xff1a;使用java17即使里面没有javafx&#xff0c;也是可以运行javafx项目的&#xff0c;只要你们按照下面一步一步来即可。 第一步&#xff1a;编写一个类去调用主类…

ArXiv| Graph-Toolformer: 基于ChatGPT增强提示以赋予大语言模型图数据推理能力

ArXiv| Graph-Toolformer: 基于ChatGPT增强提示以赋予大语言模型图数据推理能力. 来自加利福利亚大学戴维斯分校计算机科学系的IFM实验室发表在arXiv上的文章:“Graph-ToolFormer: To Empower LLMs with Graph Reasoning Ability via Prompt Augmented by ChatGPT”。 文章的…

2.1总结

还是一样水更一天&#xff0c;就随便做了几个题&#xff0c;有一个周期有点长&#xff0c;后面更一篇长的 随手刷的一道水题&#xff0c;就不往今天的行程单添了 问题&#xff1a;最大公约数 题解&#xff1a;题目太水了&#xff0c;就是求三个数&#xff0c;其中两组的最大公…

PyTorch识别验证码

## 一、生成测试集数据pip install captcha common.py import random import time captcha_array list("0123456789abcdefghijklmnopqrstuvwxyz") captcha_size 4from captcha.image import ImageCaptchaif __name__ __main__:for i in range(10):image ImageC…

this.$store undefined

报错&#xff1a;vuex报错 this.$store显示undefined&#xff0c;可能存在的问题&#xff0c;从以下几个方向排查 1、查看store文件中的vuex实例对象是否暴漏 2、main.js中是否注入store 3、如果上边均没问题&#xff0c;打开package.json&#xff0c;查看vue与vuex的版本&am…

el-upload子组件上传多张图片(上传为files或base64url)

场景&#xff1a; 在表单页&#xff0c;有图片需要上传&#xff0c;表单的操作行按钮中有上传按钮&#xff0c;点击上传按钮。 弹出el-dialog进行图片的上传&#xff0c;可以上传多张图片。 由于多个表单页都有上传多张图片的操作&#xff0c;因此将上传多图的el-upload定义…

Web中的转发与重定向

转发与重定向 一、转发和重定向的概念1.转发2.重定向 二、JavaWeb 中的转发和重定向三、SpringMVC 中的转发和重定向1.转发(1) 默认的方式(2) 完整的方式 2.重定向 四、总结 一、转发和重定向的概念 在 Web 应用中&#xff0c;转发和重定向都是用于将请求从一个页面传递到另一…

09-信息收集-APP及其他资产等

信息收集-APP及其他资产等 信息收集-APP及其他资产等一、APP提取季抓包及后续配合1、某APK一键提取反编译2、利用bp抓取更多URL 二、某IP无web框架下的第三方测试1、各种端口一顿乱扫 —— 思路2、各种接口一顿乱扫 —— 思路3、接口部分一顿测试 —— 思路 三、**案例演示**1、…

技术革新与市场需求:探索亚信安慧AntDB的发展之路

在这个信息爆炸的时代&#xff0c;企业对数据处理的需求日益增长&#xff0c;而传统的数据库系统往往难以应对海量数据的存储和处理。亚信安慧AntDB的出现&#xff0c;为解决这一难题提供了有力的工具。它不仅具备高吞吐、高并发、高性能的特点&#xff0c;还拥有极佳的扩展性和…