SIpcObject是一个基于Windows消息及共享内存的一个IPC(跨进程函数调用)的组件。

GITHUB上有很多IPC模块,我这里又造了一个轮子,不一定比现有的IPC更好,不过我觉得已经足够简单了。

老规矩,先看一下IPC模块的路径:

再看一下IPC模块的接口:

 #pragma once

 #include <unknown/obj-ref-i.h>

 #define UM_CALL_FUN (WM_USER+1000)

 namespace SOUI
{
enum {
FUN_ID_CONNECT = ,
FUN_ID_DISCONNECT,
FUN_ID_START,
}; struct IShareBuffer {
virtual void StartRead() = ;
virtual void StartWrite() = ;
virtual int Write(const void * data, UINT nLen) = ;
virtual int Read(void * buf, UINT nLen) = ;
}; class SParamStream
{
public:
SParamStream(IShareBuffer *pBuf, bool bOutStream) :m_pBuffer(pBuf)
{
m_pBuffer->StartRead();
if (bOutStream) m_pBuffer->StartWrite();
} IShareBuffer * GetBuffer() {
return m_pBuffer;
} template<typename T>
SParamStream & operator<<(const T & data)
{
Write((const void*)&data, sizeof(data));
return *this;
} template<typename T>
SParamStream & operator >> (T &data)
{
Read((void*)&data, sizeof(data));
return *this;
} public:
int Write(const void * data, int nLen)
{
return m_pBuffer->Write(data, nLen);
}
int Read(void * buf, int nLen)
{
return m_pBuffer->Read(buf, nLen);
} protected:
IShareBuffer * m_pBuffer;
}; struct IFunParams
{
virtual UINT GetID() = ;
virtual void ToStream4Input(SParamStream & ps) = ;
virtual void ToStream4Output(SParamStream & ps) = ;
virtual void FromStream4Input(SParamStream & ps) = ;
virtual void FromStream4Output(SParamStream & ps) = ;
}; struct IIpcConnection;
struct IIpcHandle : IObjRef
{
virtual void SetIpcConnection(IIpcConnection *pConn) = ; virtual IIpcConnection * GetIpcConnection() const = ; virtual LRESULT OnMessage(ULONG_PTR idLocal, UINT uMsg, WPARAM wp, LPARAM lp, BOOL &bHandled) = ; virtual HRESULT ConnectTo(ULONG_PTR idLocal, ULONG_PTR idRemote) = ; virtual HRESULT Disconnect() = ; virtual bool CallFun(IFunParams * pParam) const = ; virtual ULONG_PTR GetLocalId() const = ; virtual ULONG_PTR GetRemoteId() const = ; virtual IShareBuffer * GetSendBuffer() = ; virtual IShareBuffer * GetRecvBuffer() = ; virtual BOOL InitShareBuf(ULONG_PTR idLocal, ULONG_PTR idRemote, UINT nBufSize, void* pSa) = ;
}; struct IIpcConnection : IObjRef
{
virtual IIpcHandle * GetIpcHandle() = ;
virtual bool HandleFun(UINT uFunID, SParamStream & ps) = ;
virtual void BuildShareBufferName(ULONG_PTR idLocal, ULONG_PTR idRemote, TCHAR szBuf[MAX_PATH]) const = ;
}; struct IIpcSvrCallback
{
virtual void OnNewConnection(IIpcHandle * pIpcHandle, IIpcConnection ** ppConn) = ;
virtual int GetBufSize() const = ;
virtual void * GetSecurityAttr() const = ;
virtual void ReleaseSecurityAttr(void* psa) const = ;
}; struct IIpcServer : IObjRef
{
virtual HRESULT Init(ULONG_PTR idSvr, IIpcSvrCallback * pCallback) =;
virtual void CheckConnectivity() =;
virtual LRESULT OnMessage(ULONG_PTR idLocal, UINT uMsg, WPARAM wp, LPARAM lp,BOOL &bHandled) =;
}; struct IIpcFactory : IObjRef
{
virtual HRESULT CreateIpcServer(IIpcServer ** ppServer) =;
virtual HRESULT CreateIpcHandle(IIpcHandle ** ppHandle) =;
}; }

和所有SOUI的组件一样,可以通过SOUI::IPC::SCreateInstance来创建IPC组件的IIpcFactory接口。

有了这个接口就可以用来创建IIpcServer和IIpcHandle这两个对象了。

IIpcServer是在IPC的服务端运行的接口,IIpcHandle是用来在服务端和客户端通讯的接口,在服务端,IIpcHandle由IIpcServer在客户端发起连接请求时自动创建,在客户端则直接使用IIpcFactory创建。

IIpcHandle是由SIpcObject实现的,在应用层中只需要直接使用。

应用层为了实现客户端与服务器的通讯还需要定义好协议。

SIpcObject的协议就是一个继承自IFunParam接口的定义的调用方法ID及方法参数。

下面看一下启程输入法使用IpcObject的协议定义。

 #pragma once
#include <string>
#include <sstream>
#include "sinstar-i.h"
#include "TextService-i.h"
#include <interface/SIpcObj-i.h>
#include <helper/sipcparamhelper.hpp> #define SINSTAR3_SERVER_HWND _T("sinstar3_server_wnd_{85B55CBC-7D48-4860-BA88-0BE4B073A94F}")
#define SINSTAR3_SHARE_BUF_NAME_FMT _T("sistart3_share_buffer_8085395F-E2FA-4F96-8BD0-FE5D7412CD22_%08x_2_%08x") //////////////////////////////////////////////////////////////////
namespace SOUI{ template<>
inline SParamStream & SParamStream::operator<<(const std::string & str)
{
int nSize = (int)str.size();
GetBuffer()->Write((const BYTE*)&nSize, sizeof(int));
GetBuffer()->Write((const BYTE*)str.c_str(), nSize);
return *this;
}
template<>
inline SParamStream & SParamStream::operator >> (std::string & str)
{
int nSize = ;
GetBuffer()->Read((BYTE*)&nSize, sizeof(int));
char *pBuf = new char[nSize];
GetBuffer()->Read((BYTE*)pBuf, nSize);
str = std::string(pBuf, nSize);
delete[]pBuf;
return *this;
} ////////////////////////////////////////////////////////////////////////
template<>
inline SParamStream & SParamStream::operator<<(const std::wstring & str)
{
int nSize = (int)str.size();
GetBuffer()->Write((const BYTE*)&nSize, sizeof(int));
GetBuffer()->Write((const BYTE*)str.c_str(), nSize*sizeof(wchar_t));
return *this;
}
template<>
inline SParamStream & SParamStream::operator >> (std::wstring & str)
{
int nSize = ;
GetBuffer()->Read((BYTE*)&nSize, sizeof(int));
wchar_t *pBuf = new wchar_t[nSize];
GetBuffer()->Read((BYTE*)pBuf, nSize*sizeof(wchar_t));
str = std::wstring(pBuf, nSize);
delete[]pBuf;
return *this;
} //////////////////////////////////////////////////////////////////////
template<>
inline SParamStream & SParamStream::operator<<(const POINT & pt)
{
GetBuffer()->Write((const BYTE*)&pt.x, sizeof(int));
GetBuffer()->Write((const BYTE*)&pt.y, sizeof(int));
return *this;
}
template<>
inline SParamStream & SParamStream::operator >> (POINT & pt)
{
int tmp = ;
GetBuffer()->Read((BYTE*)&tmp, sizeof(int));
pt.x = tmp;
GetBuffer()->Read((BYTE*)&tmp, sizeof(int));
pt.y = tmp;
return *this;
} } struct FunParams_Base : SOUI::IFunParams
{
virtual void ToStream4Input(SOUI::SParamStream & ps) {}
virtual void ToStream4Output(SOUI::SParamStream & ps) {}
virtual void FromStream4Input(SOUI::SParamStream & ps) {}
virtual void FromStream4Output(SOUI::SParamStream & ps) {}
}; enum {
ISinstar_Create = SOUI::FUN_ID_START,
ISinstar_Destroy,
ISinstar_OnImeSelect,
ISinstar_OnCompositionStarted,
ISinstar_OnCompositionChanged,
ISinstar_OnCompositionTerminated,
ISinstar_OnSetCaretPosition,
ISinstar_OnSetFocusSegmentPosition,
ISinstar_ProcessKeyStoke,
ISinstar_TranslateKey,
ISinstar_OnSetFocus,
ISinstar_GetCompositionSegments,
ISinstar_GetCompositionSegmentEnd,
ISinstar_GetCompositionSegmentAttr,
ISinstar_OnOpenStatusChanged,
ISinstar_OnConversionModeChanged,
ISinstar_ShowHelp,
ISinstar_GetDefInputMode, ITextService_InputStringW = ISinstar_GetDefInputMode + ,
ITextService_IsCompositing,
ITextService_StartComposition,
ITextService_ReplaceSelCompositionW,
ITextService_UpdateResultAndCompositionStringW,
ITextService_EndComposition,
ITextService_GetImeContext,
ITextService_ReleaseImeContext,
ITextService_SetConversionMode,
ITextService_GetConversionMode,
ITextService_SetOpenStatus,
ITextService_GetOpenStatus,
ITextService_GetActiveWnd,
}; struct Param_Create : FunParams_Base
{
bool bDpiAware;
std::string strHostPath;
DWORD dwVer;
FUNID(ISinstar_Create)
PARAMS3(Input, bDpiAware,strHostPath,dwVer)
}; struct Param_Destroy : FunParams_Base
{
FUNID(ISinstar_Destroy)
}; struct Param_OnImeSelect : FunParams_Base
{
BOOL bSelect;
FUNID(ISinstar_OnImeSelect)
PARAMS1(Input, bSelect)
}; struct Param_OnCompositionStarted : FunParams_Base
{
FUNID(ISinstar_OnCompositionStarted)
}; struct Param_OnCompositionTerminated : FunParams_Base
{
bool bClearCtx;
FUNID(ISinstar_OnCompositionTerminated)
PARAMS1(Input, bClearCtx)
}; struct Param_OnCompositionChanged : FunParams_Base
{
FUNID(ISinstar_OnCompositionChanged)
}; struct Param_OnSetCaretPosition : FunParams_Base
{
POINT pt;
int nHei;
FUNID(ISinstar_OnSetCaretPosition)
PARAMS2(Input, pt,nHei)
}; struct Param_OnSetFocusSegmentPosition : FunParams_Base
{
POINT pt; int nHei;
FUNID(ISinstar_OnSetFocusSegmentPosition)
PARAMS2(Input, pt, nHei)
}; struct Param_ProcessKeyStoke : FunParams_Base {
UINT64 lpImeContext; UINT vkCode; DWORD lParam; BOOL bKeyDown;
BYTE byKeyState[];
BOOL bEaten;
FUNID(ISinstar_ProcessKeyStoke)
PARAMS5(Input, lpImeContext, vkCode, lParam, bKeyDown, byKeyState)
PARAMS1(Output,bEaten)
}; struct Param_TranslateKey : FunParams_Base
{
UINT64 lpImeContext; UINT vkCode; UINT uScanCode; BOOL bKeyDown;
BYTE byKeyState[];
BOOL bEaten;
FUNID(ISinstar_TranslateKey)
PARAMS5(Input, lpImeContext, vkCode, uScanCode, bKeyDown, byKeyState)
PARAMS1(Output, bEaten)
}; struct Param_OnSetFocus : FunParams_Base
{
BOOL bFocus;
FUNID(ISinstar_OnSetFocus)
PARAMS1(Input, bFocus)
}; struct Param_GetCompositionSegments : FunParams_Base
{
int nSegs;
FUNID(ISinstar_GetCompositionSegments)
PARAMS1(Output, nSegs)
}; struct Param_GetCompositionSegmentEnd : FunParams_Base
{
int iSeg;
int iEnd;
FUNID(ISinstar_GetCompositionSegmentEnd)
PARAMS1(Input,iSeg)
PARAMS1(Output,iEnd)
}; struct Param_GetCompositionSegmentAttr : FunParams_Base
{
int iSeg;
int nAttr;
FUNID(ISinstar_GetCompositionSegmentAttr)
PARAMS1(Input, iSeg)
PARAMS1(Output, nAttr)
}; struct Param_OnOpenStatusChanged : FunParams_Base
{
BOOL bOpen;
FUNID(ISinstar_OnOpenStatusChanged)
PARAMS1(Input, bOpen)
}; struct Param_OnConversionModeChanged : FunParams_Base
{
EInputMethod uMode;
FUNID(ISinstar_OnConversionModeChanged)
PARAMS1(Input, uMode)
}; struct Param_ShowHelp : FunParams_Base
{
FUNID(ISinstar_ShowHelp)
}; struct Param_GetDefInputMode : FunParams_Base
{
EInputMethod uMode;
FUNID(ISinstar_GetDefInputMode)
PARAMS1(Output,uMode)
}; ////////////////////////////////////////////////////////////////////////////
struct Param_InputStringW : FunParams_Base
{
std::wstring buf;
BOOL bRet;
FUNID(ITextService_InputStringW)
PARAMS1(Input,buf)
PARAMS1(Output,bRet)
}; struct Param_IsCompositing : FunParams_Base
{
BOOL bRet;
FUNID(ITextService_IsCompositing)
PARAMS1(Output,bRet)
}; struct Param_StartComposition : FunParams_Base
{
UINT64 lpImeContext;
FUNID(ITextService_StartComposition)
PARAMS1(Input,lpImeContext)
}; struct Param_ReplaceSelCompositionW : FunParams_Base
{
UINT64 lpImeContext; int nLeft; int nRight; std::wstring buf;
FUNID(ITextService_ReplaceSelCompositionW)
PARAMS4(Input,lpImeContext,nLeft,nRight,buf)
}; struct Param_UpdateResultAndCompositionStringW : FunParams_Base
{
UINT64 lpImeContext; std::wstring resultStr; std::wstring compStr;
FUNID(ITextService_UpdateResultAndCompositionStringW)
PARAMS3(Input, lpImeContext, resultStr, compStr)
}; struct Param_EndComposition : FunParams_Base
{
UINT64 lpImeContext;
FUNID(ITextService_EndComposition)
PARAMS1(Input,lpImeContext)
}; struct Param_GetImeContext : FunParams_Base
{
UINT64 lpImeContext;
FUNID(ITextService_GetImeContext)
PARAMS1(Output,lpImeContext)
}; struct Param_ReleaseImeContext : FunParams_Base
{
UINT64 lpImeContext;
BOOL bRet;
FUNID(ITextService_ReleaseImeContext)
PARAMS1(Input, lpImeContext)
PARAMS1(Output,bRet)
}; struct Param_SetConversionMode : FunParams_Base
{
EInputMethod mode;
FUNID(ITextService_SetConversionMode)
PARAMS1(Input,mode)
}; struct Param_GetConversionMode : FunParams_Base
{
EInputMethod mode;
FUNID(ITextService_GetConversionMode)
PARAMS1(Output, mode)
}; struct Param_SetOpenStatus : FunParams_Base
{
UINT64 lpImeContext;
BOOL bOpen;
BOOL bRet;
FUNID(ITextService_SetOpenStatus)
PARAMS2(Input,lpImeContext,bOpen)
PARAMS1(Output,bRet)
}; struct Param_GetOpenStatus : FunParams_Base
{
UINT64 lpImeContext;
BOOL bOpen;
FUNID(ITextService_GetOpenStatus)
PARAMS1(Input, lpImeContext)
PARAMS1(Output, bOpen)
}; struct Param_GetActiveWnd : FunParams_Base
{
DWORD hActive;
FUNID(ITextService_GetActiveWnd)
PARAMS1(Output, hActive)
}
首先我们通过一组枚举值定义所有调用的函数ID。

然后实现一个继承自IFunParams的对象FunParams_Base,以实现接口中的缺省方法。

然后从FunParams_Base继承出每一个IPC调用需要的参数。

我们以256行的Param_InputStringW为例来说明如何定义方法参数。

struct Param_InputStringW : FunParams_Base
{
std::wstring buf;
BOOL bRet;
FUNID(ITextService_InputStringW)
PARAMS1(Input,buf)
PARAMS1(Output,bRet)
};

这个IPC调用输入是一个wstring字符串,输出是一个BOOL类型返回值。

首先在对象中定义这两个成员变量。

定义好后通过宏FUNID来指定这个方法的函数调用ID。

再通过宏PARAM1(Input,buf)来指定这个方法的输入参数buf, 注意宏的第一个参数"input"。

第三步通过宏PARAM1(output,bRet)来定义这个方法的输出变量为bRet. PARAMX目前实现的X范围为1-5, 分别对应1-5个参数,如果在一次调用中有更多参数,可以参考PARAMX的实现多写几个宏就好了。

实际上这些宏就是为了组合IFunParams的几个虚方法。

这个对象在进行IPC调用的时候,先在请求端借助SParamStream对象序列化到共享内存中,SParamStream重载了输入"<<"及输出">>"操作符,默认操作是直接拷贝变量内存,这对于基本变量类型是适用的,但是对于string,wstring等对象就不适用了,对于那些不能通过简单的内存拷贝来传递的对象,我们需要像协议开头那样为这些类型的序列化做模板特化。对于比如POINT这样的对象也是可以直接通过内存拷贝就可以实现序列化的,因此这里对POINT的特化其实是多余的(最新的代码已经删除)。

协议定义好后,我们来看看如何进行IPC调用及响应IPC调用。

 class CClientConnection : public SOUI::TObjRefImpl<SOUI::IIpcConnection>
{
public:
CClientConnection(ITextService * pTxtService); public:
// 通过 IIpcConnection 继承
virtual SOUI::IIpcHandle * GetIpcHandle() override;
virtual void BuildShareBufferName(ULONG_PTR idLocal, ULONG_PTR idRemote, TCHAR szName[MAX_PATH]) const override;
bool CallFun(SOUI::IFunParams *params) const;
protected:
void OnInputStringW( Param_InputStringW &param);
void OnIsCompositing( Param_IsCompositing &param);
void OnStartComposition( Param_StartComposition &param);
void OnReplaceSelCompositionW( Param_ReplaceSelCompositionW &param);
void OnUpdateResultAndCompositionStringW( Param_UpdateResultAndCompositionStringW &param);
void OnEndComposition( Param_EndComposition &param);
void OnGetImeContext( Param_GetImeContext &param);
void OnReleaseImeContext( Param_ReleaseImeContext &param);
void OnSetConversionMode( Param_SetConversionMode &param);
void OnGetConversionMode( Param_GetConversionMode &param);
void OnSetOpenStatus( Param_SetOpenStatus &param);
void OnGetOpenStatus( Param_GetOpenStatus &param);
void OnGetActiveWnd( Param_GetActiveWnd &param); FUN_BEGIN
FUN_HANDLER(Param_InputStringW, OnInputStringW)
FUN_HANDLER(Param_IsCompositing, OnIsCompositing)
FUN_HANDLER(Param_StartComposition, OnStartComposition)
FUN_HANDLER(Param_ReplaceSelCompositionW, OnReplaceSelCompositionW)
FUN_HANDLER(Param_UpdateResultAndCompositionStringW, OnUpdateResultAndCompositionStringW)
FUN_HANDLER(Param_EndComposition, OnEndComposition)
FUN_HANDLER(Param_GetImeContext, OnGetImeContext)
FUN_HANDLER(Param_ReleaseImeContext, OnReleaseImeContext)
FUN_HANDLER(Param_SetConversionMode, OnSetConversionMode)
FUN_HANDLER(Param_GetConversionMode, OnGetConversionMode)
FUN_HANDLER(Param_SetOpenStatus, OnSetOpenStatus)
FUN_HANDLER(Param_GetOpenStatus, OnGetOpenStatus)
FUN_HANDLER(Param_GetActiveWnd, OnGetActiveWnd)
FUN_END private:
ITextService * m_pTxtService;
SOUI::CAutoRefPtr<SOUI::IIpcHandle> m_ipcHandle;
};
 bool CClientConnection::CallFun(SOUI::IFunParams *params) const
{
SASSERT(m_ipcHandle);
return m_ipcHandle->CallFun(params);
}
 void CSinstarProxy::ProcessKeyStoke(UINT64 imeContext, UINT vkCode, LPARAM lParam, BOOL bKeyDown, BYTE byKeyState[], BOOL * pbEaten)
{
Param_ProcessKeyStoke param;
param.lpImeContext = imeContext;
param.vkCode = vkCode;
param.lParam = (DWORD)lParam;
param.bKeyDown = bKeyDown;
memcpy(param.byKeyState, byKeyState, );
param.bEaten = false;
m_conn.CallFun(&param);
*pbEaten = param.bEaten;
}
CSinstarProxy对象有一个CClientConnection对象:m_conn,它需要调用服务器的方法ProcessKeyStoke,我们需要把对应的函数参数包装到对象:Param_ProcessKeyStoke中,调用m_conn.CallFun(&param),再从参数中获取返回值。

在CClientConnection对象中有一组FUN_BEGIN,FUN_END包装的处理函数映射表,分别用来处理服务端对客户端的函数调用。

如此,一个客户端服务器双向调用的IPC就完成了。

这个IPC核心就是用参数对象来包装参数列表并经过序列化,反序列化来实现跨进程函数调用,并通过实现一些宏简化开发,美化代码结构,目前在我的启程输入法3.0中工作很好。

启程输入法3.0 GIT仓库: https://gitee.com/setoutsoft/sinstar3

启程软件 2019-02-03

SOUI新组件SIpcObject介绍的更多相关文章

  1. Android新组件RecyclerView介绍,其效率更好

    今天我们首先来说为什么要介绍这个新组件RecyclerView,因为前几天我发布了一个常用面试题ListView的复用及如何优化的文章,介绍给一些开发者,但是我看到有关的反馈说:现在都不再用listv ...

  2. android新组件RecyclerView使用介绍和进阶使用,替用Gallery

    简介: RecyclerView是support-v7包中的新组件,是一个强大的滑动组件,与经典的ListView相比,同样拥有item回收复用的功能,但是直接把viewholder的实现封装起来,用 ...

  3. 【转】android新组件RecyclerView使用介绍和进阶使用,替用Gallery

    简介: RecyclerView是support-v7包中的新组件,是一个强大的滑动组件,与经典的ListView相比,同样拥有item回收复用的功能,但是直接把viewholder的实现封装起来,用 ...

  4. android开发3:四大基本组件的介绍与生命周期

    android开发3:四大基本组件的介绍与生命周期 Android四大基本组件分别是Activity,Service服务,Content Provider内容提供者,BroadcastReceiver ...

  5. 纯小白入手 vue3.0 CLI - 2.4 - 新组件 Forms.vue 中学习表单

    vue3.0 CLI 真小白一步一步入手全教程系列:https://www.cnblogs.com/ndos/category/1295752.html 我的 github 地址 - vue3.0St ...

  6. 读写Word的组件DocX介绍与入门

    本文为转载内容: 文章原地址:http://www.cnblogs.com/asxinyu/archive/2013/02/22/2921861.html 开源Word读写组件DocX介绍与入门 阅读 ...

  7. 第1节 kafka消息队列:2、kafka的架构介绍以及基本组件模型介绍

    3.kafka的架构模型 1.producer:消息的生产者,主要是用于生产消息的.主要是接入一些外部的数据源,从外部获取数据,比如说我们可以从flume获取数据,还可以通过ftp传入数据等,还可以通 ...

  8. 150多个Flutter组件详细介绍送给你

    迷茫是什么,迷茫就是大事干不了,小事不想干,能力配不上欲望,才华配不上梦想. 150+Flutter组件详细介绍地址:http://laomengit.com/ 前言 我在Flutter未正式发布之前 ...

  9. iNeuOS工业互联平台,图表与数据点组合成新组件,进行项目复用

    目       录 1.      概述... 1 2.      演示信息... 2 3.      应用过程... 2 1.   概述 针对有些行业的数据已经形成了标准化的建模或者有些公司专注于某 ...

随机推荐

  1. 洛谷P1032 字串变换-题解

    https://www.luogu.org/problemnew/show/P1032--(题目传送) 好在数据范围很小,暴力一点也能过.思路较简单,按照所有规则,从第一位开始广搜. 注意:1.str ...

  2. PHP 消息队列 详解

    前言:之前做过的一些项目中有时候会接触到消息队列,但是对消息队列并没有一个很清楚的认知,本篇文章将会详细分析和归纳一些笔记,以供后续学习. 一.消息对列概念 从本质上说消息对列就是一个队列结构的中间件 ...

  3. python实现猜字游戏

    import randomx = random.randint(0,99)while(True): num = input("please input a number\n") i ...

  4. JS设置Cookie过期时间

    //JS操作cookies方法! //写cookies function setCookie(name,value) { var Days = 30; var exp = new Date(); ex ...

  5. 001 Nibiru SDK 调试工具介绍

    为方便调试 Unity 项目,Nibiru 提供调试工具用于模拟手柄键值或直连主机手 柄 要求:Unity 开发环境和调试工具运行在同一台电脑上 Nibiru SDK 调试工具主要用于开发过程中对操控 ...

  6. idea创建springboot Web项目

    一.File —— New —— Project 二.next 三.选择你要的骨架,然后 next.个人觉的这些不用选,因为就是帮你建了几个文件夹,导入了几个jar包依赖而已. 四.Finish 五. ...

  7. macbook配置xdebug+vscode

    1.从xdebug官网下载xdebug 地址:https://xdebug.org/index.php,如果你不知道你要下载哪个版本的话可以下载它建议的版本.使用方法是在 https://xdebug ...

  8. 模仿OpenStack写自己的RPC

    在openstack中使用两种通信方式,一种是Restful API,另一种是远程过程调用RPC.本片文章主要讲解openstack中RPC的使用方式,以及如何在我们自己的架构中使用RPC. 在我前面 ...

  9. R猜拳游戏解释

    R猜拳游戏解释 作者:梁 蓉 猜拳游戏大概解释: 搜集齐数据框,for循环在三个随机数里抽俩个,抽出的数据放回,继续抽取剪刀石头布,机器人出剪刀石头布,我出对应压制机器人的方法来赢取胜利,我给机器人发 ...

  10. 四五月份:关键词是沟通、绘画和SQL

    例行总结一下四五月份的感受. 关键词有三个:沟通.绘画和SQL. 整体来说,这两个月在努力跟这三个关键词死磕,略有些进展,因此汇报一下. 虽然这三个关键词从重要度来说是从左到右的,但从叙述来讲,还是先 ...