select I/O 多路复用实现服务器聊天室功能

2023-05-16

基本概念

 IO多路复用是指内核一旦发现进程指定的一个或者多个IO条件准备读取,它就通知该进程。IO多路复用适用如下场合:

  (1)当客户处理多个描述字时(一般是交互式输入和网络套接口),必须使用I/O复用。

  (2)当一个客户同时处理多个套接口时,而这种情况是可能的,但很少出现。

  (3)如果一个TCP服务器既要处理监听套接口,又要处理已连接套接口,一般也要用到I/O复用。

  (4)如果一个服务器即要处理TCP,又要处理UDP,一般要使用I/O复用。

  (5)如果一个服务器要处理多个服务或多个协议,一般要使用I/O复用。

  与多进程和多线程技术相比,I/O多路复用技术的最大优势是系统开销小,系统不必创建进程/线程,也不必维护这些进程/线程,从而大大减小了系统的开销。

select 函数  

int select(int maxfdp1,fd_set *readset,fd_set *writeset,fd_set *exceptset,const struct timeval *timeout)
  • 第一个参数maxfdp1指定待测试的描述字个数,它的值是待测试的最大描述字加1(因此把该参数命名为maxfdp1),描述字0、1、2…maxfdp1-1均将被测试。
  • 中间的三个参数readset、writeset和exceptset指定我们要让内核测试读、写和异常条件的描述字。如果对某一个的条件不感兴趣,就可以把它设为空指针。struct fd_set可以理解为一个集合,这个集合中存放的是文件描述符,可通过以下四个宏进行设置:

          void FD_ZERO(fd_set *fdset);           //清空集合

          void FD_SET(int fd, fd_set *fdset);   //将一个给定的文件描述符加入集合之中

          void FD_CLR(int fd, fd_set *fdset);   //将一个给定的文件描述符从集合中删除

          int FD_ISSET(int fd, fd_set *fdset);   // 检查集合中指定的文件描述符是否可以读写 
  • timeout告知内核等待所指定描述字中的任何一个就绪可花多少时间。其timeval结构用于指定这段时间的秒数和微秒数。

     struct timeval{
    
               long tv_sec;   //seconds
    
               long tv_usec;  //microseconds
    

    };
    这个参数有三种可能:

(1)永远等待下去:仅在有一个描述字准备好I/O时才返回。为此,把该参数设置为空指针NULL。

(2)等待一段固定时间:在有一个描述字准备好I/O时返回,但是不超过由该参数所指向的timeval结构中指定的秒数和微秒数。

(3)根本不等待:检查描述字后立即返回,这称为轮询。为此,该参数必须指向一个timeval结构,而且其中的定时器值必须为0。

select函数注意事项

  • 文件的描述符的监视范围与第一个函数参数有关
  • 函数的超时时间与select函数的最后一个结构体 timeval有关
  • select函数只有在监视的文件描述符发生变化才返回,如果未发生变化,就会发生阻塞,指定超时就是为了防止上述情况
  • 将准备好的fd_set变量reads的内容复制到temps变量,因为调用函数后除了发生变化的描述符对应的位为1,其他的都为0,也就是说客户端在线,但是没有发生通讯,这个时候要记住这个在线的客户端就必须要
    进行复制,这是使用select的通用方法。
  • timeval每次循环都会被初始化一次
  • select 的函数返回值只有标记作用,只表示发生变化的活跃的描述符数目,要想知道哪些描述符发生变化,还得必须遍历FD_SET,代码如下:
while(1)
{
    timeout.tv_sec=30;//设置系统延迟等待时间30s
    timeout.tv_usec=0;
    temp_set=read_set;
    result=select(fd_max+1,&temp_set,NULL,NULL,&timeout);
    if(result==-1)
    {
        std::cout<<"select error!"<<std::endl;
        break;
        }
        if(result==0)
        {
            std::cout<<"server waiting time out!"<<std::endl;
            continue;
        }
        for(int index=0;index!=fd_max+1;index++)
        {
            if(FD_ISSET(index,&temp_set))
            {
                if(index==fd_server)//处理上线
                {
                    handle_on();
                }
                else
                {
                    handle_task(index);//处理客户端上线以外其他的业务
                }
            }
        }
    }
}

上面的例子result知识起一个判断作用,要想获取详细信息,还得遍历FD_SET,从0到fd_max+1;
当管道一端被关闭时候,遵循下面两条规则:
(1) 当读一个写端已经被关闭的管道,在所有的数据都被读取后,read返回0,表示文件结束
(2)如果写一个读端已经被关闭的管道,则产生信号SIGPIPE,该信号可以被捕捉。
下面的代码就是父进程通知服务器客户端已经下线,服务器端则关闭子进程的两个管道的读写描述符,子进程读到0后自动返回,退出循环。

void chat()
{
    fd_write=open(m_pipe_wpath.c_str(),O_WRONLY);
    fd_read=open(m_pipe_rpath.c_str(),O_RDONLY);
    if(fork()==0)
    {
        close(fd_server);
        close(fd_write);
        while(bzero(msg,1024),read(fd_read,msg,1024)!=0)
        {
            std::cout<<msg<<std::endl;
        }
        close(fd_read);
        exit(0);
    }
    close(fd_read);
    while(bzero(msg,1024),fgets(msg,1024,stdin)!=NULL)
    {
        write(fd_write,msg,strlen(msg));
    }
}

select多路复用服务器流程图

服务器流程

代码区域

  • 服务器端代码:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <sys/select.h>
#include <sys/time.h>
#include <signal.h>
#include <json/json.h>
#include <stdexcept>
#include <iostream>
#include <sstream>
#define FD_MAX 1023
namespace SELECT
{
    struct Client_info
    {
        Client_info():m_next(NULL){}
        int m_pid;
        int m_read;
        int m_write;
        std::string m_name;
        Client_info* m_next;
    };
    void sighandle(int signum)
    {
        std::cout<<"Capture signal is "<<signum<<std::endl;
    }
    class Select
    {
        public:
            Select(const std::string pipe_path):m_head(NULL)
        {
            fd_server=open(pipe_path.c_str(),O_RDONLY);
            if(fd_server==-1)
            {
                throw std::runtime_error("open pipe failed");
            }
            fd_max=fd_server;//可以避免扫描整个集合
            signal(SIGPIPE,sighandle);
        }
            void init()
            {
                FD_ZERO(&read_set);
                FD_ZERO(&temp_set);
                FD_SET(fd_server,&read_set);
            }
            void handle_on()//处理上线消息
            {
                char recv_msg[1024];
                read(fd_server,recv_msg,1024);
                std::string msg(recv_msg);
                if(msg.find("on")!=std::string::npos)
                {
                    online(msg);
                }
            }
            void online(const std::string& msg)
            {
                Json::Value value;
                reader.parse(msg,value,false);//解析客户端上线消息
                Client_info* inode=new Client_info();
                inode->m_pid=value["pid"].asInt();//进程ID
                inode->m_name=value["name"].asString();//客户端名字
                inode->m_read=open(value["pipe_w"].asString().c_str(),O_RDONLY);//打开读写管道
                inode->m_write=open(value["pipe_r"].asString().c_str(),O_WRONLY);
                if(inode->m_read>fd_max)
                    fd_max=inode->m_read;
                inode->m_next=m_head;
                m_head=inode;
                FD_SET(inode->m_read,&read_set);//将读描述符放入监听集合
                std::stringstream ss;
                ss<<inode->m_name<<" is on line !"<<std::endl;
                std::string onmsg=ss.str();
                std::cout<<onmsg;
                dispatch(onmsg,inode->m_pid);/*群发客户端上线消息*/
            }
            void offline(const std::string& msg)
            {
                Json::Value value;
                reader.parse(msg,value,false);//解析客户端的下线消息,提取客户端的进程ID
                Client_info* search=m_head;
                Client_info* pre=NULL;
                while(search)
                {
                    if(search->m_pid==value["pid"].asInt())//找到下线的客户端
                    {
                        break;
                    }
                    pre=search;
                    search=search->m_next;
                }
                FD_CLR(search->m_read,&read_set);//将下线客户端描述符从监听集删除
                std::stringstream ss;
                ss<<search->m_name<<" off line normal"<<std::endl;
                std::string offmsg=ss.str();
                std::cout<<offmsg;
                dispatch(offmsg,search->m_pid);/*群发该客户端下线的消息*/
                close(search->m_read);
                close(search->m_write);//关闭下线客户端的读写描述符
                if(pre==NULL)/*将下线的客户端从链表中移除同时释放空间*/
                {
                    m_head=search->m_next;
                    search->m_next=NULL;
                    delete search;
                }
                else
                {
                    pre->m_next=search->m_next;
                    delete search;
                }
            }
            void dropoff(const int& read)
            {
                Client_info* search=m_head;
                Client_info* pre=NULL;
                while(search)
                {
                    if(search->m_read==read)//找到掉线的客户端
                    {
                        break;
                    }
                    pre=search;
                    search=search->m_next;
                }
                FD_CLR(search->m_read,&read_set);//将掉线客户端描述符从监听集删除
                std::stringstream ss;
                ss<<search->m_name<<" drop off line !"<<std::endl;
                std::string offmsg=ss.str();
                std::cout<<offmsg;
                dispatch(offmsg,search->m_pid);/*群发该客户端掉线的消息*/
                close(search->m_read);
                close(search->m_write);//关闭掉线客户端的读写描述符
                if(pre==NULL)/*将线的客户端从链表中移除同时释放空间*/
                {
                    m_head=search->m_next;
                    search->m_next=NULL;
                    delete search;
                }
                else
                {
                    pre->m_next=search->m_next;
                    delete search;
                }
            }
            void handle_task(const int& index)
            {
                char recv_buf[1024]="";
                int recv_len=read(index,recv_buf,1024);
                if(recv_len==0)
                {
                    dropoff(index);       //处理掉线
                }
                else if(recv_len>0)
                {
                    std::string msg(recv_buf);
                    if(msg.find("off")!=std::string::npos)//客户端正常下线
                    {
                        offline(msg);        
                    }
                    else                                    
                    {
                        sendmsg(msg);      //客户端正常发消息
                    }
                }
            }
            void sendmsg(const std::string& msg)
            {
                Json::Value msginfo;
                reader.parse(msg,msginfo,false);    
                int msg_pid=msginfo["pid"].asInt();
                std::string clientname=msginfo["name"].asString();
                std::string clientmsg=msginfo["msg"].asString();
                std::stringstream ss;
                ss<<clientmsg<<"from client :"<<clientname<<std::endl;
                std::string msgmsg=ss.str();
                dispatch(msgmsg,msg_pid);
            }
            void dispatch(const std::string& msg,const int& pid)
            {
                Client_info* broadcast=m_head;
                while(broadcast)
                {
                    if(broadcast->m_pid!=pid)//不能加入FD_ISSET来判断,否则未活动的描述符就收不到消息了
                    {
                        write(broadcast->m_write,msg.c_str(),msg.size());
                    }
                    broadcast=broadcast->m_next;
                }
            }
            void wait()
            {
                init();
                std::cout<<"server waiting for connecting"<<std::endl;
                while(1)
                {
                    timeout.tv_sec=30;//设置系统延迟等待时间30s
                    timeout.tv_usec=0; 
                    temp_set=read_set;
                    result=select(fd_max+1,&temp_set,NULL,NULL,&timeout);
                    if(result==-1)
                    {
                        std::cout<<"select error!"<<std::endl;
                        break;
                    }
                    if(result==0)
                    {
                        std::cout<<"server waiting time out!"<<std::endl;
                        continue;
                    }
                    for(int index=0;index!=fd_max+1;index++)
                    {
                        if(FD_ISSET(index,&temp_set))
                        {
                            if(index==fd_server)//处理上线
                            {
                                handle_on(); 
                            }
                            else
                            {
                                handle_task(index);//处理客户端上线以外其他的业务
                            }
                        }
                    }
                }
            }
        private:
            Client_info* m_head;
            fd_set read_set;
            fd_set temp_set;
            struct timeval timeout;
            int result;
            int fd_server;
            int fd_max;
            Json::FastWriter writer;
            Json::Reader reader;
    };
}
  • 服务器端测试代码:
#include"select_pro.hpp"
int main(int argc,char** argv)
{
    SELECT::Select* server=new SELECT::Select(argv[1]);
    server->init();
    server->wait();
}
  • 客户端代码
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <strings.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/wait.h>
#include <fcntl.h>
#include <sys/select.h>
#include <sys/time.h>
#include <json/json.h>
#include <stdexcept>
#include <iostream>
#include <sstream>
namespace CLIENT
{
    class Client
    {
        public:
            Client(const std::string& server_path,const std::string& pipe_rpath,const std::string& pipe_wpath,const std::string& name):m_pipe_rpath(pipe_rpath),m_pipe_wpath(pipe_wpath),m_name(name)
        {
            fd_server=open(server_path.c_str(),O_WRONLY);//打开服务器端描述符
            if(fd_server==-1)
            {
                perror("open fd_server");
            }
        }
            void connect()
            {
                std::cout<<"connect server"<<std::endl;
                Json::Value info;
                info["name"]=m_name;
                info["pid"]=getpid();
                info["pipe_r"]=m_pipe_rpath;
                info["pipe_w"]=m_pipe_wpath;
                info["type"]="on";
                std::string jsonfile=writer.write(info);
                write(fd_server,jsonfile.c_str(),jsonfile.size());
            }
            void chat()
            {
                fd_write=open(m_pipe_wpath.c_str(),O_WRONLY);
                fd_read=open(m_pipe_rpath.c_str(),O_RDONLY);
                if(fork()==0)
                {
                    close(fd_server);
                    close(fd_write);
                    while(bzero(msg,1024),read(fd_read,msg,1024)!=0)
                    {
                        std::cout<<msg<<std::endl;
                    }
                    close(fd_read);
                    exit(0);
                }
                close(fd_read);
                while(bzero(msg,1024),fgets(msg,1024,stdin)!=NULL)
                {
                    write(fd_write,msg,strlen(msg));
                }
            }
            void offline()
            {
                Json::Value info;
                info["pid"]=getpid();
                info["type"]="off";
                std::string jsonfile=writer.write(info);
                write(fd_write,jsonfile.c_str(),jsonfile.size());
                std::cout<<"waiting........"<<std::endl;
                wait(NULL);
                std::cout<<"waited !"<<std::endl;
                close(fd_server);
                close(fd_write);
            }
        private:
            int fd_write;
            int fd_read;
            int fd_server;
            std::string m_name;
            std::string m_pipe_rpath;
            std::string m_pipe_wpath;
            Json::FastWriter writer;
            Json::Reader reader;
            char msg[1024];
    };
}
  • 客户端测试代码
#include"client_pro.hpp"
//#include"client.hpp" 
int main(int argc,char** argv)
{
    CLIENT::Client* client=new CLIENT::Client(argv[1],argv[2],argv[3],argv[4]);
    client->connect();
    client->chat();
    client->offline();
}
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

select I/O 多路复用实现服务器聊天室功能 的相关文章

  • Pyinstaller 打包 Tkinter 程序时引入图标解决方法

    Pyinstaller 打包 Tkinter 程序时引入图标解决方法 描述 在windows下开发python的gui程序时 多数使用pyinstaller py2exe等打包工具 在因为tkinter框架时 代码中使用iconbitmap
  • mysql基本操作

    修改字段属性 xff1a 修改字段属性 ALTER TABLE tb name MODIFY 字段名称 字段类型 完整性约束条件 将email字段 VARCHAR 50 修改成VARCHAR 200 注意 xff0c 修改时如果不带完整性约
  • <context:component-scan/>标签爆红

    lt xml version 61 34 1 0 34 encoding 61 34 UTF 8 34 gt lt beans xmlns 61 34 http www springframework org schema beans 34
  • iOS exit函数深入浅出

    1 exit函数 C C 43 43 函数exit用来终止当前程序 xff0c 函数定义如下 xff1a void exit int status 官方说明如下 xff1a Terminates the process normally p
  • 前端妹子如何在 sqlserver 2008 中如何用自定义函数 解析json数据

    导航 前言 xff1a 开始干活 xff1a 0 预告1 首先先建立一个 通用的json解析自定义函数 xff08 这个代码是网络上找到的成熟代码 xff09 2 重点讲解一下 函数 parseJSON 的用法3 学会了函数 parseJS
  • ECS架构的思考

    最近在整理Demo代码 xff0c 遇到一个设计问题 xff0c 这个问题是transform组件到底放到哪里比较合适 xff1f 我们都知道逻辑 xff0c 物理 xff0c 渲染模块都会用到transform组件 比如渲染模块会将tra
  • 外网如何访问内网/局域网网站【内网穿透】

    在本地内网 局域网环境下搭建的网站 xff0c 正常情况下只能在同个局域网下访问 xff0c 想要实现外网用户也能够正常访问 xff0c 可以通过内网穿透来实现 做内网穿透 xff0c 无需公网IP xff0c 也无需进入到路由器配置 xf
  • 禁用nouveau

    sudo vim etc modprobe d blacklist conf 在最后两行添加 xff1a blacklist nouveau options nouveau modeset 61 0 禁用nouveau第三方驱动 xff0c
  • 使用win10自带的微软远程桌面,远程控制不同局域网的电脑【无需公网IP、无需进入路由器】

    在Windows环境下 xff0c 要实现远程桌面控制 xff0c 首推系统自带的微软远程桌面mstsc xff0c 不需要另外去下载第三方远程软件 不管设备是否在同个网络下 xff0c 都可以使用mstsc来实现远程连接 在同个局域网内远
  • 推荐一款永久免费不限流量的内网穿透软件

    文章目录 前言1 安装cpolar内网穿透1 1 windows系统1 2 Linux系统 2 创建隧道穿透内网端口2 1 cpolar web ui2 2 命令行创建隧道 3 配置固定二级子域名3 1 保留二级子域名3 2 配置二级子域名
  • 群晖nas免费内网穿透,实现外网异地远程访问

    文章目录 1 安装cpolar群晖套件2 打开cpolar群晖套件3 登录cpolar Web UI管理界面4 创建新隧道映射5 获取公网地址6 配置固定二级子域名6 1 保留一个二级子域名6 2 配置二级子域名 7 使用固定二级子域名远程
  • 永久免费的内网端口映射工具推荐【无公网IP】

    搭建了个游戏服务器 xff0c 想要让在不同网段下的朋友也可以连接想要在家远程桌面公司电脑想要在外远程访问本地电脑的web服务器想要在外远程访问NAS 一切的一切 xff0c 都需要公网IP的支持 但是目前IPV4资源的稀缺 xff0c 很
  • 免费内网穿透教程【无公网IP】

    文章目录 前言1 安装cpolar内网穿透工具1 1 Windows系统1 2 Linux系统1 3 macOS系统 2 创建隧道映射内网端口3 获取公网地址4 配置固定二级子域名4 1 保留一个二级子域名4 2 配置二级子域名 5 公网测
  • 内网穿透SSH远程连接家里的树莓派

    随着科技的进步和信息技术的发展 xff0c 我们身边出现了各种新奇的科技产品 xff0c 其中既有轻便易用的消费类电子产品 xff0c 也有更轻更小的硬件设备 而树莓派作为计算机学习设备 xff0c 经过多年发展 xff0c 已经获得了不俗
  • 推荐10款简单好用的免费内网穿透工具

    前言 远程办公越来越普遍 xff0c 但是如何应对在外远程桌面控制公司电脑 远程公司内网办公系统 调阅公司文件资料 远程公司内网服务器是个问题 而解决方案其实很简单 xff0c 做内网穿透就可以突破局域网的限制 xff0c 轻松实现公网访问
  • Java支付宝沙箱环境支付,官方Demo远程调试【内网穿透】

    文章目录 1 下载当面付demo2 修改配置文件3 打包成web服务4 局域网测试5 内网穿透6 测试公网访问7 配置二级子域名8 测试使用固定二级子域名访问 在沙箱环境调试支付SDK的时候 xff0c 往往沙箱环境部署在本地 xff0c
  • opencv内存不足问题(OpenCV Error: Insufficient memory)

    最近在用opencv自带的函数haartraining训练分类器 xff0c 之前用的图片是20 20 xff0c 能训练出分类器 xff0c 后来换成了80 86 xff0c 就报错了 xff0c 报的错误是内存不足 xff0c 于是 x
  • ffmpeg 4.2.2 实现mp4转avi(修改官方remuxing例子)

    最近想把ffmpeg官方例子过一遍 xff0c 达到初步了解ffmpeg的目的 xff0c 本文只是给自己一个记录 xff0c 也是在网上没有找到一样的文章 xff0c 发出来供大家指点 直接使用官方demo xff0c 把mp4转换成av
  • 12.bss段的初始化

    12 bss段的初始化 在C代码 xff1a 有初始化全局的数据段 xff0c 局部的栈 xff0c malloc部分的堆 xff0c 未初始化的全局的bss段 从上面的编译的信息知道 xff1a Bss段的起始地址 xff1a 00010
  • pandas学习之df.rename()

    pandas学习之df rename df rename 用于更改行列的标签 xff0c 即行列的索引 可以传入一个字典或者一个函数 在数据预处理中 xff0c 比较常用 官方文档 xff1a DataFrame rename self m

随机推荐

  • java8操作两个集合List

    public static void main String args List lt String gt list1 61 new ArrayList lt String gt list1 add 34 1 34 list1 add 34
  • Atcoder AGC005 题解

    A STring 用类似括号匹配的方法搞一下即可 span class token macro property span class token directive keyword include span span class toke
  • CentOS-7安装桌面环境

    CentOS 7安装桌面环境 CentOS 7安装桌面环境 CentOS 7安装Server with GUI 设置为开机从桌面环境启动 yum y group install 39 Server with GUI 39 systemctl
  • [软件注册]Sublime 3 激活/注册码(key)

    偶然发现了一种sublime激活方式 使用的sublime3 1 1版本 亲试有效 Step1 配置 host文件 推荐使用 switchhost软件 可以快速变更host span class hljs number 127 0 span
  • 测试git能否连接github

    welcome to my blog 使用以下命令进行测试 ssh T git 64 github com 出现报错 ssh dispatch run fatal Connection to 13 250 177 223 port 22 S
  • vtk中实现3D模型(读取文件)

    xff08 xff09 VTK 坐标系统及空间变换 窗口 视图分割 mb5fed73533dfa9的技术博客 51CTO博客 VTK学习 xff08 三 xff09 VTK读取序列图像 灰信网 xff08 软件开发博客聚合 xff09 读取
  • centos中安装Python2.7

    转载于 xff1a 秋水逸冰 CentOS 6 8安装Python2 7 13 查看当前系统中的 Python 版本 python version 返回 Python 2 6 6 为正常 检查 CentOS 版本 cat etc redha
  • 安装tar.gz文件(无configure文件)

    如何安装tar gz文件 xff08 以webstorm为例 xff09 1 获取root权限并输入密码 xff1a su root 2 进入有该文件的目录下 xff08 以我的为例 xff0c 具体看你的文件在哪里 xff09 cd 下载
  • 游戏服务端框架之业务线程模型

    请求消息绑定线程策略的选择 在上一篇文章中 我们看到 消息是直接在网络框架的io线程中处理的 这样做有一个非常严重的缺陷 如果业务处理比较耗时 那么io线程接受消息的速度就会下降 严重影响io的吞吐量 典型的 我们应该另起线程池 专门用于异
  • 在WSL中使用GPU:WSL2 + Ubuntu 18.04 + CUDA + Gnome图形界面环境配置

    目录 引言1 确认Windows 10版本2 在Windows上安装WSL23 在Windows上安装CUDA on WSL驱动4 在WSL2中安装CUDA Toolkit3 测试CUDA是否能在WSL2中运作4 安装Gnome图形界面其他
  • Centos 开启路由转发实现全网互通

    只需在RouterSrv网关服务器上开启路由转发功能即可 root 64 RouterSrv vi etc sysctl conf net ipv4 ip forward 61 1 添加此行即可 root 64 localhost sysc
  • 虚拟机中配置外网环境

    文章目录 在虚拟机中配置外网环境 在虚拟机中配置外网环境 主机为 win10 xff0c 虚拟机中为 ubuntu 系统 xff0c 采用clash 1 xff0c 设置 Allow Lan xff0c 允许局域网访问 2 xff0c 虚拟
  • mysql 操作数据库(备份与恢复)

    一 直接把创建数据库的语句放到sql 文件中 xff1a php 写法 xff1a lt php mysql port 61 get mysql port cmd 61 US MYSQL BIN 34 mysql exe port 61 3
  • Go调用Python by go-python3

    确保python版本为3 7 conda create go python span class token assign left variable python span span class token operator 61 spa
  • linux下搭建maven私服

    maven私服我相信很多公司都有 xff0c 私服的好处有以下几点 xff1a 1 节省下载资源开销 jar包 xff08 不一定是jar xff0c 也可以是其他资源 xff09 都存在私服 xff0c 可以不必每次下载都去远程仓库去下载
  • git 安装包 最新 下载 快速 国内 镜像 地址

    下载git时 xff0c 先进官网看 https git scm com download win 然后发现几kb的网速 xff0c 这是要让我下一年么 xff0c 找了找网上有没有其他的镜像 xff0c 发现阿里有一个镜像 xff0c 下
  • docker笔记(四、docker部署beego打包后的二进制文件)

    在beego工程里 xff0c 使用go build可以将该工程打包成一个二进制文件 xff0c 那么这个二进制文件在docker里面该怎么部署呢 xff1f 先写一个简单的图片上传的demo xff0c 名字叫docker test 在工
  • LINUX服务器最简洁的HTTPS免费证书配置方法

    注意 xff1a 该方法已在多台服务器配置了免费的https证书 xff0c 无论是更新还是第一次配置都运行成功 xff1b 由于是免费版 xff0c 每个证书都只有三个月的有效期 xff0c 也无法保证安全和稳定性 xff0c 所以只建议
  • 【性能测试】获取性能系统指标之示例Python代码

    usr bin env python coding utf 8 import sys import datetime import time import psutil from ctypes import 34 34 34 性能测试示例P
  • select I/O 多路复用实现服务器聊天室功能

    基本概念 IO多路复用是指内核一旦发现进程指定的一个或者多个IO条件准备读取 xff0c 它就通知该进程 IO多路复用适用如下场合 xff1a xff08 1 xff09 当客户处理多个描述字时 xff08 一般是交互式输入和网络套接口 x