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

c++代码如何实现在win/linux下创建编译及部署后台服务,并管理其他服务

一、实现基础

        在win下,在#include <windows.h>提供了服务控制管理器相关操作函数,如OpenSCManager、CreateService、OpenService、DeleteService、CloseServiceHandle,可以实现服务的创建、打开、关闭、删除等操作。

        在linux下,系统启用后台应用服务是按照先后次序去扫描特定文件目录去加载这些服务配置文件来启动服务,对于服务的管理本质上就是对于管道(文件句柄)的管理,因此借用popen、pclose等实现对进程的启停操作。

二、源码示例

文件目录结构:

svr_test
    bin
        install_linux.sh
        uinstall_linux.sh
    build_linux
    build_win
    include
        appexitio.h
        svc.h
    src
        main.cpp
    svc
        appexitio.cpp
        linuxSVC.cpp
        WinSVC.cpp
    CMakeLists.txt

文件源码:

appexitio.h

#if _MSC_VER > 1000
#pragma once
#endif // _MSC_VER > 1000

#ifndef _APP_EXIT_IO_H_
#define _APP_EXIT_IO_H_

/***********************************************************************
  *Copyright 2020-03-06, pyfree
  *
  *File Name       : appexitio.h
  *File Mark       : 
  *Summary         : 
  *程序退出信号处理接口
  *
  *Current Version : 1.00
  *Author          : pyfree
  *FinishDate      :
  *
  *Replace Version :
  *Author          :
  *FinishDate      :

 ************************************************************************/

#ifdef WIN32
#include <windows.h> 
#else
#include <signal.h>
#include <unistd.h>
#include <stdio.h>
#endif

#ifdef WIN32
bool ctrlhandler(unsigned long fdwctrltype);
#else
class SignalHandler {
public:
	SignalHandler();

  void printf_out();
public:
	// 程序退出时的函数操作
	static void handle_signal(int n, siginfo_t *siginfo, void *myact);
};

#endif // WIN32

#endif

svc.h

#if _MSC_VER > 1000
#pragma once
#endif // _MSC_VER > 1000
/***********************************************************************
  *Copyright 2020-04-21, pyfree
  *
  *File Name       : svc.h
  *File Mark       : 
  *Summary         : 服务管理函数集
  *
  *Current Version : 1.00
  *Author          : pyfree
  *FinishDate      :
  *
  *Replace Version :
  *Author          :
  *FinishDate      :

 ************************************************************************/
#ifndef _SVC_H_
#define _SVC_H_

/**
 * 启动服务
 * @param svr {char*} 服务名
 * @retuan {void} 
 */
void SvcStart(char *svr);

/**
 * 停止服务
 * @param svr {char*} 服务名
 * @retuan {void} 
 */
void SvcStop(char *svr);
/**
 * 查询服务
 * @param svr {char*} 服务名
 * @param svc_state {int&} 服务状态
 * @retuan {void} 
 */
void SvcQuery(char* svr, int &svc_state);

/**
 * 重启服务
 * @param svr {char*} 服务名
 * @retuan {void} 
 */
void SvcRestart(char *svr);

#endif //_SVC_H_

appexitio.cpp

#include "appexitio.h"

#ifdef WIN32
#include <windows.h>
#else
#include <stdlib.h>
#endif
#include <string>

namespace GlobalVar {
	extern bool exitFlag;
};

#ifdef WIN32
bool ctrlhandler(unsigned long fdwctrltype)
{
	bool exitEvent = false;
	switch (fdwctrltype)
	{
		// handle the ctrl-c signal.
	case CTRL_C_EVENT:
		printf("ctrl-c event\n\n");
		exitEvent = true;
		break;
		// ctrl-close: confirm that the user wants to exit.
	case CTRL_CLOSE_EVENT:
		printf("ctrl-close event\n\n");
		exitEvent = true;
		break;
		// pass other signals to the next handler.
	case CTRL_BREAK_EVENT:
		printf("ctrl-break event\n\n");
		break;
	case CTRL_LOGOFF_EVENT:
		printf("ctrl-logoff event\n\n");
		break;
	case CTRL_SHUTDOWN_EVENT:
		printf("ctrl-shutdown event\n\n");
		exitEvent = true;
		//清理
		break;
	default:
		break;
	}
	if (exitEvent)
	{
		Sleep(10);
	}
	GlobalVar::exitFlag = exitEvent;
	return exitEvent;
}
#else
SignalHandler::SignalHandler()
{
	/** install signal use sigaction **/
	struct sigaction act;
	sigemptyset(&act.sa_mask);   /** 清空阻塞信号 **/
	act.sa_flags = SA_SIGINFO;     /** 设置SA_SIGINFO 表示传递附加信息到触发函数 **/
	act.sa_sigaction = handle_signal;
	if (sigaction(SIGHUP, &act, NULL) < 0      // 1
		|| sigaction(SIGINT, &act, NULL) < 0  // 2
		|| sigaction(SIGQUIT, &act, NULL) < 0 // 3
											  //|| sigaction(SIGKILL,&act,NULL) < 0 // 9
		|| sigaction(SIGTERM, &act, NULL) < 0 // 15
		)
	{
		printf("install signal handler error");
	}
};

void SignalHandler::printf_out()
{

}

void SignalHandler::handle_signal(int n, siginfo_t *siginfo, void *myact)
{
	printf("SIGNAL received: signo=%d errno=%d code=%d "
		, siginfo->si_signo, siginfo->si_errno, siginfo->si_code);
	if (siginfo->si_signo == 1
		|| siginfo->si_signo == 2
		|| siginfo->si_signo == 3
		|| siginfo->si_signo == 9
		|| siginfo->si_signo == 15)
	{
		//程序退出,进行退出处理操作
		//G_VALUE::watchDogRunning = false;
		usleep(10000);
		exit(0);
	}
};
#endif // WIN32

linuxSVC.cpp
 

#ifdef __linux__

#include <string.h>
#include <string>
#include <stdlib.h>
#include <stdio.h>
#include <signal.h>

#include "svc.h"

/**
 * 启动服务
 * @param svr {char*} 服务名
 * @retuan {void} 
 */
void SvcStart(char *svr)
{
	FILE* fp = NULL;
	char command[128] = { 0 };
	sprintf(command, "systemctl start %s", svr);
	if ((fp = popen(command, "r")) == NULL)
	{
		return;
	}
	char buf[512] = { 0 };
	if ((fgets(buf, 512, fp)) == NULL)

	{
		pclose(fp);
		return;
	}
	printf("start ret:%s\n",buf);
	pclose(fp);
};

/**
 * 停止服务
 * @param svr {char*} 服务名
 * @retuan {void} 
 */
void SvcStop(char *svr)
{
	FILE* fp = NULL;
	char command[128] = { 0 };
	sprintf(command, "systemctl stop %s", svr);
	if ((fp = popen(command, "r")) == NULL)
	{
		return;
	}
	char buf[512] = { 0 };
	if ((fgets(buf, 512, fp)) == NULL)
	{
		pclose(fp);
		return;
	}
	printf("stop ret:%s\n",buf);
	pclose(fp);
};

/**
 * 查询服务
 * @param svr {char*} 服务名
 * @param svc_state {int&} 服务状态
 * @retuan {void} 
 */
void SvcQuery(char* svr, int &svc_state)
{
	svc_state = 0;
	FILE* fp = NULL;
	char command[128] = { 0 };
	sprintf(command, "systemctl status %s | grep Active", svr);
	if ((fp = popen(command, "r")) == NULL)
	{
		return;
	}
	char buf[512] = { 0 };
	if ((fgets(buf, 512, fp)) == NULL)
	{
		pclose(fp);
		return;
	}
	std::string comment = std::string(buf,strlen(buf));
	//std::string::size_type _pos = comment.find("running");
	//开机启动的状态,static不可被管理,disable未启动,enable启动
	//dead关闭,exited已读取完系统配置,闲置 waiting等待, running正在进行, mounted挂载, plugged加载插件, failed系统配置错误
	if(std::string::npos != comment.find("running"))
	{
		svc_state = 2;
	}else if(std::string::npos != comment.find("listening")){
		svc_state = 2;
	}else{
		svc_state = 1;
	}
	// printf("ret:%s,state:%d\n",buf,svc_state);
	pclose(fp);	
};

/**
 * 重启服务
 * @param svr {char*} 服务名
 * @retuan {void} 
 */
void SvcRestart(char *svr)
{
	SvcStop(svr);
	SvcStart(svr);
}

#endif

WinSVC.cpp

#ifdef WIN32
#pragma warning(disable: 4995)  //for swprintf

#include <windows.h>
#include <tchar.h>
#include <strsafe.h>

#include "atlcomtime.h"
#pragma comment(lib, "advapi32.lib")

#include "svc.h"

int MyMain(int argc, char * argv[]);		//Actual Main function, should be defined
extern char SVCNAME[128];					//SVC Name, should be defined
extern char SVCDESC[256];					//SVC Desc, should be defined


namespace WINSVC
{
	SERVICE_STATUS          gSvcStatus; 
	SERVICE_STATUS_HANDLE   gSvcStatusHandle; 
	HANDLE                  ghSvcStopEvent = NULL;

	/**
	* 本服务安装
	* @param {void}
	* @retuan {void} 
	*/
	VOID SvcInstall(void);
	/**
	* 本服务卸载
	* @param {void}
	* @retuan {void} 
	*/
	VOID DoDeleteSvc(void);
	/**
	* 查询服务
	* @param svr {char*} 服务名
	* @param svc_state {int&} 服务状态
	* @retuan {void} 
	*/
	VOID SvcQuery(char* svr, int &svc_state);
	/**
	* 启动服务
	* @param svr {char*} 服务名
	* @retuan {void} 
	*/
	VOID SvcStart(char *svr);
	/**
	* 停止服务
	* @param svr {char*} 服务名
	* @retuan {void} 
	*/
	VOID SvcStop(char *svr);
	/**
	* 重启服务
	* @param svr {char*} 服务名
	* @retuan {void} 
	*/
	VOID SvcRestart(char *svr);

	VOID WINAPI SvcCtrlHandler( DWORD ); 
	VOID WINAPI SvcMain( DWORD, LPTSTR * ); 

	VOID ReportSvcStatus( DWORD, DWORD, DWORD );
	/**
	* 本服务初始化
	* @param svr {char*} 服务名
	* @retuan {void} 
	*/
	VOID SvcInit( DWORD, LPTSTR * ); 
	VOID SvcReportEvent( LPTSTR );
	VOID CALLBACK MainThread(HWND hwnd,UINT uMsg,UINT_PTR idEvent,DWORD dwTime);

	HANDLE m_HThread;
	DWORD m_dwThreadID;
	BOOL g_bRun = FALSE;
}

void __cdecl _tmain(int argc, TCHAR *argv[]) 
{ 
	if( lstrcmpi( argv[1], TEXT("install")) == 0 )
	{
		WINSVC::SvcInstall();
		return;
	}
	else if( lstrcmpi( argv[1], TEXT("uninstall")) == 0 )
	{
		WINSVC::DoDeleteSvc();
		return;
	}
	SERVICE_TABLE_ENTRY DispatchTable[] = 
	{ 
		{ SVCNAME, (LPSERVICE_MAIN_FUNCTION) WINSVC::SvcMain }, 
		{ NULL, NULL } 
	}; 

	WINSVC::g_bRun = TRUE;
	if (!StartServiceCtrlDispatcher( DispatchTable )) 
	{ 
		WINSVC::SvcReportEvent(TEXT("StartServiceCtrlDispatcher"));
		MyMain(argc, argv);
		MSG msg;
		while (GetMessage(&msg, 0, 0, 0))
			DispatchMessage(&msg);
	} 
};

void SvcQuery(char* svr, int &svc_state)
{
	WINSVC::SvcQuery(svr, svc_state);
};

void SvcStart(char *svr)
{
	WINSVC::SvcStart(svr);
};

void SvcStop(char *svr)
{
	WINSVC::SvcStop(svr);
};

void SvcRestart(char *svr)
{
	WINSVC::SvcRestart(svr);
};

VOID WINSVC::SvcInstall()
{
	SC_HANDLE schSCManager;
	SC_HANDLE schService;
	TCHAR szPath[MAX_PATH];

	if( !GetModuleFileName( NULL, szPath, MAX_PATH ) )
	{
		printf("Install service fail (%d), %s %s %d!"
			, GetLastError(), __FILE__, __FUNCTION__, __LINE__);
		return;
	}

	// Get a handle to the SCM database. 

	schSCManager = OpenSCManager( 
		NULL,                    // local computer
		NULL,                    // ServicesActive database 
		SC_MANAGER_ALL_ACCESS);  // full access rights 

	if (NULL == schSCManager) 
	{
		printf("Failed to open service manager (%d), %s %s %d!"
			, GetLastError(), __FILE__, __FUNCTION__, __LINE__);
		return;
	}

	// Create the service

	schService = CreateService( 
		schSCManager,              // SCM database 
		SVCNAME,                   // name of service 
		SVCNAME,                   // service name to display 
		SERVICE_ALL_ACCESS,        // desired access 
		SERVICE_WIN32_OWN_PROCESS, // service type 
		SERVICE_AUTO_START,      // start type 
		SERVICE_ERROR_NORMAL,      // error control type 
		szPath,                    // path to service's binary 
		NULL,                      // no load ordering group 
		NULL,                      // no tag identifier 
		NULL,                      // no dependencies 
		NULL,                      // LocalSystem account 
		NULL);                     // no password 

	if (schService == NULL) 
	{
		printf("Failed to create service (%d), %s %s %d!"
			, GetLastError(), __FILE__, __FUNCTION__, __LINE__);
		CloseServiceHandle(schSCManager);
		return;
	}
	else{
		printf("%s\n",SVCDESC);
	}
	CloseServiceHandle(schService); 
	CloseServiceHandle(schSCManager);
}

VOID WINSVC::DoDeleteSvc()
{
	SC_HANDLE schSCManager;
	SC_HANDLE schService;
	//SERVICE_STATUS ssStatus; 

	// Get a handle to the SCM database. 

	schSCManager = OpenSCManager( 
		NULL,                    // local computer
		NULL,                    // ServicesActive database 
		SC_MANAGER_ALL_ACCESS);  // full access rights 

	if (NULL == schSCManager) 
	{
		printf("Failed to open service manager (%d), %s %s %d!"
			, GetLastError(), __FILE__, __FUNCTION__, __LINE__);
		return;
	}

	// Get a handle to the service.

	schService = OpenService( 
		schSCManager,       // SCM database 
		SVCNAME,          // name of service 
		DELETE);            // need delete access 

	if (schService == NULL)
	{ 
		printf("Failed to get service (%d), %s %s %d!"
			, GetLastError(), __FILE__, __FUNCTION__, __LINE__);
		CloseServiceHandle(schSCManager);
		return;
	}

	// Delete the service.

	if (! DeleteService(schService) ) 
	{
		printf("Failed to delete service (%d), %s %s %d!"
			, GetLastError(), __FILE__, __FUNCTION__, __LINE__);
	}
	else{ 
		printf("Service successfully removed !\n");//log输出不及时造成无显示
		printf("Service successfully removed !"); 
	}
	CloseServiceHandle(schService); 
	CloseServiceHandle(schSCManager);
}

VOID WINSVC::SvcQuery(char *svr, int &svc_state)
{
	SC_HANDLE schSCManager;
	SC_HANDLE schService;
	// Get a handle to the SCM database. 
	schSCManager = OpenSCManager(
		NULL,                    // local computer
		NULL,                    // ServicesActive database 
		SC_MANAGER_ALL_ACCESS);  // full access rights 

	if (NULL == schSCManager)
	{
		printf("Failed to open service manager (%d), %s %s %d!"
			, GetLastError(), __FILE__, __FUNCTION__, __LINE__);
		return;
	}
	// Get a handle to the service.
	schService = OpenService(
		schSCManager,       // SCM database 
		svr,				// name of service 
		SERVICE_QUERY_STATUS);     // need query access 

	if (schService == NULL)
	{
		printf("Failed to get service(%s) and error(%d), %s %s %d!"
			, svr, GetLastError(), __FILE__, __FUNCTION__, __LINE__);
		CloseServiceHandle(schSCManager);
		return;
	}
	//调用QueryServiceStatus函数
	SERVICE_STATUS sStatus = { 0 };
	if (!QueryServiceStatus(schService, &sStatus))
	{
		CloseServiceHandle(schService);
		CloseServiceHandle(schSCManager);
		return;
	}
	switch (sStatus.dwCurrentState)
	{
	case SERVICE_STOP_PENDING: case SERVICE_STOPPED:
		svc_state = 1;
		break;
	case SERVICE_START_PENDING: case SERVICE_RUNNING: case SERVICE_CONTINUE_PENDING:
		svc_state = 2;
		break;
	case SERVICE_PAUSE_PENDING: case SERVICE_PAUSED:
		svc_state = 3;
		break;
	default:
		svc_state = 0;
		break;
	}
	CloseServiceHandle(schService);
	CloseServiceHandle(schSCManager);
};

VOID WINSVC::SvcStart(char *svr)
{
	SC_HANDLE schSCManager;
	SC_HANDLE schService;
	// Get a handle to the SCM database. 
	schSCManager = OpenSCManager(
		NULL,                    // local computer
		NULL,                    // ServicesActive database 
		SC_MANAGER_ALL_ACCESS);  // full access rights 

	if (NULL == schSCManager)
	{
		printf("Failed to open service manager (%d), %s %s %d!"
			, GetLastError(), __FILE__, __FUNCTION__, __LINE__);
		return;
	}
	// Get a handle to the service.
	schService = OpenService(
		schSCManager,       // SCM database 
		svr,				// name of service 
		SERVICE_START | SERVICE_QUERY_STATUS);     // need start and query access 

	if (schService == NULL)
	{
		printf("Failed to get service(%s) and error(%d), %s %s %d!"
			, svr,GetLastError(), __FILE__, __FUNCTION__, __LINE__);
		CloseServiceHandle(schSCManager);
		return;
	}
	StartService(schService, 0, NULL);//开始Service
	//调用QueryServiceStatus函数
	SERVICE_STATUS sStatus = { 0 };
	if (!QueryServiceStatus(schService, &sStatus))
	{
		CloseServiceHandle(schService);
		CloseServiceHandle(schSCManager);
		return;
	}
	if (SERVICE_RUNNING == sStatus.dwCurrentState || SERVICE_START_PENDING == sStatus.dwCurrentState)
	{
		printf("start service(%s) success, %s %s %d!"
			, svr, __FILE__, __FUNCTION__, __LINE__);
	}
	CloseServiceHandle(schService);
	CloseServiceHandle(schSCManager);
};

VOID WINSVC::SvcStop(char *svr)
{
	SC_HANDLE schSCManager;
	SC_HANDLE schService;
	// Get a handle to the SCM database. 
	schSCManager = OpenSCManager(
		NULL,                    // local computer
		NULL,                    // ServicesActive database 
		SC_MANAGER_ALL_ACCESS);  // full access rights 

	if (NULL == schSCManager)
	{
		printf("Failed to open service manager(%d), %s %s %d!"
			, GetLastError(), __FILE__, __FUNCTION__, __LINE__);
		return;
	}
	// Get a handle to the service.
	schService = OpenService(
		schSCManager,       // SCM database 
		svr,				// name of service 
		SERVICE_STOP | SERVICE_QUERY_STATUS);            // need stop or query access 

	if (schService == NULL)
	{
		printf("Failed to get service(%s) and error(%d), %s %s %d!"
			, svr,GetLastError(), __FILE__, __FUNCTION__, __LINE__);
		CloseServiceHandle(schSCManager);
		return;
	}
	//调用QueryServiceStatus函数
	SERVICE_STATUS sStatus = { 0 };
	if (!QueryServiceStatus(schService, &sStatus))
	{
		CloseServiceHandle(schService);
		CloseServiceHandle(schSCManager);
		return;
	}
	if (SERVICE_RUNNING == sStatus.dwCurrentState || SERVICE_PAUSED == sStatus.dwCurrentState)
	{
		ControlService(schService, SERVICE_CONTROL_STOP, &sStatus);
	}
	if (!QueryServiceStatus(schService, &sStatus))
	{
		CloseServiceHandle(schService);
		CloseServiceHandle(schSCManager);
		return;
	}
	if (SERVICE_STOPPED == sStatus.dwCurrentState || SERVICE_STOP_PENDING == sStatus.dwCurrentState)
	{
		printf("stop service(%s) success, %s %s %d!"
			, svr, __FILE__, __FUNCTION__, __LINE__);
	}
	CloseServiceHandle(schService);
	CloseServiceHandle(schSCManager);
};

VOID WINSVC::SvcRestart(char *svr)
{
	SC_HANDLE schSCManager;
	SC_HANDLE schService;
	// Get a handle to the SCM database. 
	schSCManager = OpenSCManager(
		NULL,                    // local computer
		NULL,                    // ServicesActive database 
		SC_MANAGER_ALL_ACCESS);  // full access rights 

	if (NULL == schSCManager)
	{
		printf("Failed to open service manager and error(%d), %s %s %d!"
			, GetLastError(), __FILE__, __FUNCTION__, __LINE__);
		return;
	}
	// Get a handle to the service.
	schService = OpenService(
		schSCManager,       // SCM database 
		svr,				// name of service 
		SERVICE_START | SERVICE_STOP | SERVICE_QUERY_STATUS);     // need pause and query access 

	if (schService == NULL)
	{
		printf("Failed to get service(%s) and error(%d), %s %s %d!"
			, svr,GetLastError(), __FILE__, __FUNCTION__, __LINE__);
		CloseServiceHandle(schSCManager);
		return;
	}
	//调用QueryServiceStatus函数
	SERVICE_STATUS sStatus = { 0 };
	if (!QueryServiceStatus(schService, &sStatus))
	{
		CloseServiceHandle(schService);
		CloseServiceHandle(schSCManager);
		return;
	}
	if (SERVICE_RUNNING == sStatus.dwCurrentState)
	{
		ControlService(schService, SERVICE_CONTROL_STOP, &sStatus);
	}
	if (!QueryServiceStatus(schService, &sStatus))
	{
		CloseServiceHandle(schService);
		CloseServiceHandle(schSCManager);
		return;
	}
	if (SERVICE_STOPPED == sStatus.dwCurrentState || SERVICE_STOP_PENDING == sStatus.dwCurrentState)
	{
		printf("stop service (%s) success, %s %s %d!"
			, svr, __FILE__, __FUNCTION__, __LINE__);
		StartService(schService, 0, NULL);//开始Service
	}
	if (!QueryServiceStatus(schService, &sStatus))
	{
		CloseServiceHandle(schService);
		CloseServiceHandle(schSCManager);
		return;
	}
	if (SERVICE_RUNNING == sStatus.dwCurrentState || SERVICE_START_PENDING == sStatus.dwCurrentState)
	{
		printf("restart service (%s) success, %s %s %d!"
			, svr, __FILE__, __FUNCTION__, __LINE__);
	}
	CloseServiceHandle(schService);
	CloseServiceHandle(schSCManager);
};
//

VOID WINAPI WINSVC::SvcMain( DWORD dwArgc, LPTSTR *lpszArgv )
{
	// Register the handler function for the service

	gSvcStatusHandle = RegisterServiceCtrlHandler( 
		SVCNAME, 
		SvcCtrlHandler);

	if( !gSvcStatusHandle )
	{ 
		SvcReportEvent(TEXT("RegisterServiceCtrlHandler")); 
		return; 
	} 

	// These SERVICE_STATUS members remain as set here

	gSvcStatus.dwServiceType = SERVICE_WIN32_OWN_PROCESS; 
	gSvcStatus.dwServiceSpecificExitCode = 0;    

	// Report initial status to the SCM

	ReportSvcStatus( SERVICE_START_PENDING, NO_ERROR, 3000 );

	// Perform service-specific initialization and work.

	SvcInit( dwArgc, lpszArgv );
}

//

VOID WINSVC::SvcInit( DWORD dwArgc, LPTSTR *lpszArgv)
{
	// TO_DO: Declare and set any required variables.
	//   Be sure to periodically call ReportSvcStatus() with 
	//   SERVICE_START_PENDING. If initialization fails, call
	//   ReportSvcStatus with SERVICE_STOPPED.

	// Create an event. The control handler function, SvcCtrlHandler,
	// signals this event when it receives the stop control code.

	TCHAR szPath[MAX_PATH];
	GetModuleFileName( NULL, szPath, MAX_PATH );
	TCHAR drive[MAX_PATH],dir[MAX_PATH],fname[MAX_PATH],ext[MAX_PATH];
	_tsplitpath_s( szPath,drive,dir,fname,ext );
	strcpy_s( szPath, drive );
	strcat_s( szPath, dir );
	SetCurrentDirectory( szPath );

	ghSvcStopEvent = CreateEvent(
		NULL,    // default security attributes
		TRUE,    // manual reset event
		FALSE,   // not signaled
		NULL);   // no name

	if ( ghSvcStopEvent == NULL)
	{
		ReportSvcStatus( SERVICE_STOPPED, NO_ERROR, 0 );
		return;
	}

	m_HThread = CreateThread( (LPSECURITY_ATTRIBUTES)NULL, 0, (LPTHREAD_START_ROUTINE)MainThread,	0, 0,   &m_dwThreadID);
	if( m_HThread != NULL )
	{
		g_bRun = TRUE;
	}
	// Report running status when initialization is complete.
	ReportSvcStatus( SERVICE_RUNNING, NO_ERROR, 0 );
	bool bWriteFlag = false;
	while(1)
	{
		// Check whether to stop the service.

		WaitForSingleObject(ghSvcStopEvent, INFINITE);
		if(!bWriteFlag)
		{
			printf("Service normal stop: %s %s %d"
			, __FILE__, __FUNCTION__, __LINE__);
			bWriteFlag = true;
		}
		g_bRun = FALSE;
		ReportSvcStatus( SERVICE_STOPPED, NO_ERROR, 0 );
	}
}

//
VOID WINSVC::ReportSvcStatus( DWORD dwCurrentState,
	DWORD dwWin32ExitCode,
	DWORD dwWaitHint)
{
	static DWORD dwCheckPoint = 1;

	// Fill in the SERVICE_STATUS structure.

	gSvcStatus.dwCurrentState = dwCurrentState;
	gSvcStatus.dwWin32ExitCode = dwWin32ExitCode;
	gSvcStatus.dwWaitHint = dwWaitHint;

	if (dwCurrentState == SERVICE_START_PENDING)
		gSvcStatus.dwControlsAccepted = 0;
	else gSvcStatus.dwControlsAccepted = SERVICE_ACCEPT_STOP;

	if ( (dwCurrentState == SERVICE_RUNNING) ||
		(dwCurrentState == SERVICE_STOPPED) )
		gSvcStatus.dwCheckPoint = 0;
	else gSvcStatus.dwCheckPoint = dwCheckPoint++;

	// Report the status of the service to the SCM.
	SetServiceStatus( gSvcStatusHandle, &gSvcStatus );
}

//
VOID WINAPI WINSVC::SvcCtrlHandler( DWORD dwCtrl )
{
	// Handle the requested control code. 

	switch(dwCtrl) 
	{  
	case SERVICE_CONTROL_STOP: 
		ReportSvcStatus(SERVICE_STOP_PENDING, NO_ERROR, 0);

		// Signal the service to stop.

		SetEvent(ghSvcStopEvent);
		ReportSvcStatus(gSvcStatus.dwCurrentState, NO_ERROR, 0);

		return;

	case SERVICE_CONTROL_INTERROGATE: 
		break; 

	default: 
		break;
	} 

}

//
VOID WINSVC::SvcReportEvent(LPTSTR szFunction) 
{ 
	HANDLE hEventSource;
	LPCTSTR lpszStrings[2];
	TCHAR Buffer[80];

	hEventSource = RegisterEventSource(NULL, SVCNAME);

	if( NULL != hEventSource )
	{
		StringCchPrintf(Buffer, 80, TEXT("%s failed with %d, %s %s %d"), szFunction
			, GetLastError(), __FILE__, __FUNCTION__, __LINE__);

		lpszStrings[0] = SVCNAME;
		lpszStrings[1] = Buffer;

		ReportEvent(hEventSource,        // event log handle
			EVENTLOG_ERROR_TYPE, // event type
			0,                   // event category
			0,           		 // event identifier
			NULL,                // no security identifier
			2,                   // size of lpszStrings array
			0,                   // no binary data
			lpszStrings,         // array of strings
			NULL);               // no binary data

		DeregisterEventSource(hEventSource);
	}
}

VOID CALLBACK WINSVC::MainThread(HWND hwnd,UINT uMsg,UINT_PTR idEvent,DWORD dwTime)
{	
	int argc = 0;
	char * pArgv = NULL;
	MyMain(argc, &pArgv);
	printf("Main thread quit successfully");
}
#endif

 main.cpp

//
#include <stdio.h>
#include <stdlib.h>
#include <string>

#ifdef __linux__
#include <memory>
#endif // __linux__

#include "appexitio.h"

namespace GlobalVar {
	bool exitFlag = false;
};

#ifdef WIN32
#include <windows.h>
//server conf
char SVCNAME[128] = "pyfreeMgr";
char SVCDESC[256] =
"\r\n pyfree Technology Ltd \r\n "
"https://www.pyfree.com \r\n "
"email:pyxxx@163.com \r\n "
"pyfree-xxx system service \r\n "
"Service installation success";
#endif // WIN32
//linux服务名依赖于安装脚本配置,详情参考demo下的脚本
#ifdef WIN32
int MyMain(int argc, char* argv[])
#else
int main(int argc, char *argv[])
#endif
{
#ifdef WIN32
	if (!SetConsoleCtrlHandler((PHANDLER_ROUTINE)ctrlhandler, true))
	{
		printf("install signal handler error!");
	}
#else
	SignalHandler * g_exit_handler = NULL;
	g_exit_handler = new SignalHandler();
	if(!g_exit_handler){
		printf("install signal handler error!");
	}
#endif // WIN32
	//这里做真正业务处理
	//...
	
	//
	while (!GlobalVar::exitFlag)
	{
		sleep(10);
	}
	//
	return 0;
}

三、编译及测试

本文编译采用win:cmake+Visual Studio,Linux:cmake+gcc

CMakeLists.txt

# CMake 最低版本号要求
cmake_minimum_required (VERSION 2.8)
# 项目信息
project (server_test)
#
if(WIN32)
    message(STATUS "windows compiling...")
    add_definitions(-D_PLATFORM_IS_WINDOWS_)
	set(CMAKE_CXX_FLAGS_RELEASE "${CMAKE_CXX_FLAGS_RELEASE} /MT")
  set(CMAKE_CXX_FLAGS_DEBUG "${CMAKE_CXX_FLAGS_DEBUG} /MTd")
  set(WIN_OS true)
else(WIN32)
    message(STATUS "linux compiling...")
    add_definitions( -D_PLATFORM_IS_LINUX_)
    add_definitions("-Wno-invalid-source-encoding")
	  # add_definitions("-O2")
    set(UNIX_OS true)
    set(_DEBUG true)
    
endif(WIN32)

#
set(EXECUTABLE_OUTPUT_PATH ${PROJECT_SOURCE_DIR}/bin)

# 指定源文件的目录,并将名称保存到变量
SET(source_h
    #
	${PROJECT_SOURCE_DIR}/include/appexitio.h
	${PROJECT_SOURCE_DIR}/include/svc.h
  )
  
SET(source_cpp
    #
	${PROJECT_SOURCE_DIR}/svc/appexitio.cpp
	${PROJECT_SOURCE_DIR}/src/main.cpp
  )
  
#头文件目录
include_directories(${PROJECT_SOURCE_DIR}/include)

if (${UNIX_OS})

#execute_process(COMMAND ${PROJECT_SOURCE_DIR}/build.sh)
SET(source_cpp_linux
  ${PROJECT_SOURCE_DIR}/svc/linuxSVC.cpp
)

add_definitions(
  "-W"
  "-fPIC"
  "-Wall"
  # "-Wall -g"
  "-Werror"
  "-Wshadow"
  "-Wformat"
  "-Wpointer-arith"
  "-D_REENTRANT"
  "-D_USE_FAST_MACRO"
  "-Wno-long-long"
  "-Wuninitialized"
  "-D_POSIX_PTHREAD_SEMANTICS"
  "-DACL_PREPARE_COMPILE"
  "-Wno-unused-parameter"
  "-fexceptions"
  )
  set(CMAKE_C_FLAGS_DEBUG "${CMAKE_C_FLAGS_DEBUG} -O0")

link_directories()
# 指定生成目标
add_executable(server_test ${source_h} ${source_cpp} ${source_cpp_linux})
#link
target_link_libraries(server_test 
  -lpthread -pthread -lz -lrt -ldl
)

endif(${UNIX_OS})

if (${WIN_OS})

SET(source_cpp_win
  ${PROJECT_SOURCE_DIR}/svc/WinSVC.cpp
)

set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} /wd4819")

add_definitions(
  "-D_CRT_SECURE_NO_WARNINGS"
  "-D_WINSOCK_DEPRECATED_NO_WARNINGS"
  "-DNO_WARN_MBCS_MFC_DEPRECATION"
  "-DWIN32_LEAN_AND_MEAN"
)

link_directories()

if (CMAKE_BUILD_TYPE STREQUAL "Debug")

set(CMAKE_RUNTIME_OUTPUT_DIRECTORY_DEBUG ${PROJECT_SOURCE_DIR}/bin)
# 指定生成目标
add_executable(server_testd ${source_h} ${source_cpp} ${source_cpp_win})

#target_link_libraries(server_testd *.lib)

else(CMAKE_BUILD_TYPE)

set(CMAKE_RUNTIME_OUTPUT_DIRECTORY_RELEASE ${PROJECT_SOURCE_DIR}/bin)
# 指定生成目标
add_executable(server_test ${source_h} ${source_cpp} ${source_cpp_win})

#target_link_libraries(server_test *.lib)

endif (CMAKE_BUILD_TYPE)

endif(${WIN_OS})

3.1 win下编译及实现:

【1】cmake编译,进入svr_test\build_win编译:

cmake -G "Visual Studio 10 2010 Win64" -DCMAKE_BUILD_TYPE=Release ..

msbuild server_test.sln /p:Configuration="Release" /p:Platform="x64"

Visual Studio不同版本写法如下:

  Visual Studio 15 2017 [arch] = Generates Visual Studio 2017 project files.
                                 Optional [arch] can be "Win64" or "ARM".
  Visual Studio 14 2015 [arch] = Generates Visual Studio 2015 project files.
                                 Optional [arch] can be "Win64" or "ARM".
  Visual Studio 12 2013 [arch] = Generates Visual Studio 2013 project files.
                                 Optional [arch] can be "Win64" or "ARM".
  Visual Studio 11 2012 [arch] = Generates Visual Studio 2012 project files.
                                 Optional [arch] can be "Win64" or "ARM".
  Visual Studio 10 2010 [arch] = Generates Visual Studio 2010 project files.
                                 Optional [arch] can be "Win64" or "IA64".
  Visual Studio 9 2008 [arch]  = Generates Visual Studio 2008 project files.
                                 Optional [arch] can be "Win64" or "IA64".
  Visual Studio 8 2005 [arch]  = Generates Visual Studio 2005 project files.
注:超出列表版本请自行查阅资料

本文编译release版本详细过程如下:

d:\workForMy\workspace\svr_test\build_win>cmake -G "Visual Studio 10 2010 Win64" -DCMAKE_BUILD_TYPE=Release ..
-- windows compiling...
CMake Warning (dev) in CMakeLists.txt:
  A logical block opening on the line

    D:/workForMy/workspace/svr_test/CMakeLists.txt:95 (if)

  closes on the line

    D:/workForMy/workspace/svr_test/CMakeLists.txt:111 (endif)

  with mis-matching arguments.
This warning is for project developers.  Use -Wno-dev to suppress it.

-- Configuring done
-- Generating done
-- Build files have been written to: D:/workForMy/workspace/svr_test/build_win

d:\workForMy\workspace\svr_test\build_win>msbuild server_test.sln /p:Configuration="Release" /p:Platform="x64"
Microsoft(R) 生成引擎版本 4.8.4084.0
[Microsoft .NET Framework 版本 4.0.30319.42000]
版权所有 (C) Microsoft Corporation。保留所有权利。

在此解决方案中一次生成一个项目。若要启用并行生成,请添加“/m”开关。
生成启动时间为 2022/9/1 13:47:15。
节点 1 上的项目“d:\workForMy\workspace\svr_test\build_win\server_test.sln”(默认目标)。
ValidateSolutionConfiguration:
  正在生成解决方案配置“Release|x64”。
ValidateProjects:
  在解决方案配置“Release|x64”中未选定生成项目“ALL_BUILD”。
项目“d:\workForMy\workspace\svr_test\build_win\server_test.sln”(1)正在节点 1 上生成“d:\workForMy\workspace\svr_test\build_win\ZERO_CHECK.vcxproj”(2) (默认目标)。
InitializeBuildStatus:
  正在创建“x64\Release\ZERO_CHECK\ZERO_CHECK.unsuccessfulbuild”,因为已指定“AlwaysCreate”。
CustomBuild:
  所有输出均为最新。
FinalizeBuildStatus:
  正在删除文件“x64\Release\ZERO_CHECK\ZERO_CHECK.unsuccessfulbuild”。
  正在对“x64\Release\ZERO_CHECK\ZERO_CHECK.lastbuildstate”执行 Touch 任务。
已完成生成项目“d:\workForMy\workspace\svr_test\build_win\ZERO_CHECK.vcxproj”(默认目标)的操作。

项目“d:\workForMy\workspace\svr_test\build_win\server_test.sln”(1)正在节点 1 上生成“d:\workForMy\workspace\svr_test\build_win\server_test.vcxproj.metaproj”(3) (默认目标)。
项目“d:\workForMy\workspace\svr_test\build_win\server_test.vcxproj.metaproj”(3)正在节点 1 上生成“d:\workForMy\workspace\svr_test\build_win\server_test.vcxproj”(4) (默认目标)。
InitializeBuildStatus:
  正在创建“server_test.dir\Release\server_test.unsuccessfulbuild”,因为已指定“AlwaysCreate”。
CustomBuild:
  所有输出均为最新。
ClCompile:
  所有输出均为最新。
Link:
  所有输出均为最新。
  d:\Program Files (x86)\Microsoft Visual Studio 10.0\VC\bin\x86_amd64\link.exe /ERRORREPORT:QUEUE /OUT:"D:\workForMy\workspace\svr_test\bin\server_test.exe" /INCREMENTAL:NO /NOLOGO /LIBPA
  TH:"D:\workForSoftware\Visual Leak Detector\lib\Win64" kernel32.lib user32.lib gdi32.lib winspool.lib shell32.lib ole32.lib oleaut32.lib uuid.lib comdlg32.lib advapi32.lib /MANIFEST /Man
  ifestFile:"server_test.dir\Release\server_test.exe.intermediate.manifest" /MANIFESTUAC:"level='asInvoker' uiAccess='false'" /PDB:"D:/workForMy/workspace/svr_test/bin/server_test.pdb" /SU
  BSYSTEM:CONSOLE /TLBID:1 /DYNAMICBASE /NXCOMPAT /IMPLIB:"D:/workForMy/workspace/svr_test/bin/Release/server_test.lib" /MACHINE:X64 server_test.dir\Release\appexitio.obj
  server_test.dir\Release\main.obj
  server_test.dir\Release\WinSVC.obj   /machine:x64
  server_test.vcxproj -> D:\workForMy\workspace\svr_test\bin\server_test.exe
Manifest:
  C:\Program Files (x86)\Microsoft SDKs\Windows\v7.0A\bin\mt.exe /nologo /verbose /outputresource:"D:\workForMy\workspace\svr_test\bin\server_test.exe;#1" /manifest server_test.dir\Release
  \server_test.exe.intermediate.manifest
FinalizeBuildStatus:
  正在删除文件“server_test.dir\Release\server_test.unsuccessfulbuild”。
  正在对“server_test.dir\Release\server_test.lastbuildstate”执行 Touch 任务。
已完成生成项目“d:\workForMy\workspace\svr_test\build_win\server_test.vcxproj”(默认目标)的操作。

已完成生成项目“d:\workForMy\workspace\svr_test\build_win\server_test.vcxproj.metaproj”(默认目标)的操作。

已完成生成项目“d:\workForMy\workspace\svr_test\build_win\server_test.sln”(默认目标)的操作。


已成功生成。
    0 个警告
    0 个错误

已用时间 00:00:01.07

d:\workForMy\workspace\svr_test\build_win>

win安装编译服务及测试:

进入D:\workForMy\workspace\svr_test\bin

本文安装及卸载服务示例:
D:\workForMy\workspace\svr_test\bin>server_test.exe install

 pyfree Technology Ltd
 https://www.pyfree.com
 email:pyxxx@163.com
 pyfree-xxx system service
 Service installation success

D:\workForMy\workspace\svr_test\bin>server_test.exe uninstall
Service successfully removed !
Service successfully removed !
D:\workForMy\workspace\svr_test\bin>

 安装成功示例:

 3.2 Linux下编译及实现

【1】本文是在centos7编译的,进入svr_test/build_linux,具体编译过程如下:

cmake ..  
make  
[***@pyfree build_linux]$ make
Scanning dependencies of target server_test
[ 25%] Building CXX object CMakeFiles/server_test.dir/svc/linuxSVC.cpp.o
[ 50%] Linking CXX executable ../bin/server_test
[100%] Built target server_test
[***@pyfree build_linux]$ cmake ..
-- The C compiler identification is GNU 4.8.5
-- The CXX compiler identification is GNU 4.8.5
-- Check for working C compiler: /usr/bin/cc
-- Check for working C compiler: /usr/bin/cc -- works
-- Detecting C compiler ABI info
-- Detecting C compiler ABI info - done
-- Detecting C compile features
-- Detecting C compile features - done
-- Check for working CXX compiler: /usr/bin/c++
-- Check for working CXX compiler: /usr/bin/c++ -- works
-- Detecting CXX compiler ABI info
-- Detecting CXX compiler ABI info - done
-- Detecting CXX compile features
-- Detecting CXX compile features - done
-- linux compiling...
-- Configuring done
-- Generating done
-- Build files have been written to: /mnt/hgfs/workForMy/workspace/svr_test/build_linux
[***@pyfree build_linux]$ make
Scanning dependencies of target server_test
[ 25%] Building CXX object CMakeFiles/server_test.dir/svc/appexitio.cpp.o
[ 50%] Building CXX object CMakeFiles/server_test.dir/src/main.cpp.o
[ 75%] Building CXX object CMakeFiles/server_test.dir/svc/linuxSVC.cpp.o
[100%] Linking CXX executable ../bin/server_test
[100%] Built target server_test
[***@pyfree build_linux]$ 

【2】服务安全及部署:

前面说过,在Linux下,所谓的内部服务启动就是将进程文件加载到特性文件目录并配置好这些文件,系统在启动过程中会自动加载启动,并linux有自己的服务管理器,就像win下的任务管理器一样,差别在于win提供界面工具,linux采用命令。

我们提供两个脚本实现本进程的服务安装及卸载:

install_linux.sh

#!/bin/bash
#@pyfree 2022-08-26,pyXXX@163.com
#创建程序目录
#run for root
#定义程序全局路径变量
#程序根目录
dir='/usr/local/sysmgr'
curdir=$(pwd)

if [ ! -d $dir ]; then
    mkdir -p $dir
fi
#
echo "appdir="$dir
echo "curdir="$curdir
#复制程序文件到程序目录
/bin/cp -f server_test $dir
#
ls $dir
#
if [ ! -f "$dir/sysmgrStart.sh" ];then
#创建程序启动脚本sysmgr
cat > $dir/sysmgrStart.sh << eof
#!/bin/bash
pid=\`ps -ef | grep "server_test*" | grep -v "grep" | wc -l\`
if [ \$pid -eq 0 ];then
    cd $dir
	echo "The sysmgr server will be start"
    nohup ./server_test & > /dev/null
else
    echo "The sysmgr server is alreadly running"
fi 
eof
fi

if [ ! -f "$dir/sysmgrStop.sh" ];then
#创建程序关闭脚本sysmgr
cat > $dir/sysmgrStop.sh << eof
#!/bin/bash
pid=\`ps -ef | grep "server_test*" | grep -v "grep" | wc -l\`
if [ \$pid -ne 0 ];then
	echo "The sysmgr server will be stop"
    killall -9 server_test
else
	echo "The sysmgr server is stoping, don't stop it"
fi 
eof
fi

#给服务启动脚本添加执行权限
chmod o+x $dir/{sysmgrStart.sh,sysmgrStop.sh}

if [ ! -f "/usr/lib/systemd/system/pyfreeMgr.service" ];then
#创建程序启动文件到centos7服务启动路径/usr/lib/systemd/system
cat > /usr/lib/systemd/system/pyfreeMgr.service <<eof
[Unit]
Description=server_test
After=syslog.target network.target remote-fs.target nss-lookup.target
[Service]
Type=forking
ExecStart=$dir/sysmgrStart.sh
ExecReload=
ExecStop=$dir/sysmgrStop.sh
PrivateTmp=true
[Install]
WantedBy=multi-user.target
eof
fi

cd $curdir

#设置开机启动gather服务
systemctl daemon-reload
chmod o-x /usr/lib/systemd/system/pyfreeMgr.service
systemctl enable pyfreeMgr.service
#查看服务是否开机启动:
#systemctl is-enabled pyfreeMgr.service
#设置开机时禁用服务
#systemctl disable pyfreeMgr.service
#启动服务
#systemctl start pyfreeMgr.service

#停止服务
#systemctl stop pyfreeMgr.service

uninstall_linux.sh

#!/bin/bash
#@pyfree 2022-08-26,pyXXX@163.com
#创建程序目录
#run for root
#定义程序全局路径变量
#程序根目录
dir='/usr/local/sysmgr'

systemctl stop pyfreeMgr.service
systemctl disable pyfreeMgr.service
rm -f /usr/lib/systemd/system/pyfreeMgr.service

rm -rf $dir

脚本运行过程如下:

[root@pyfree bin]# ./install_linux.sh 
appdir=/usr/local/sysmgr
curdir=/mnt/hgfs/workForMy/workspace/svr_test/bin
server_test  sysmgrStart.sh  sysmgrStop.sh
[root@pyfree bin]# systemctl is-enabled pyfreeMgr.service
enabled
[root@pyfree bin]# systemctl status pyfreeMgr.service
● pyfreeMgr.service - server_test
   Loaded: loaded (/usr/lib/systemd/system/pyfreeMgr.service; enabled; vendor preset: disabled)
   Active: inactive (dead)
[root@pyfree bin]# ./uinstall_linux.sh 
Removed symlink /etc/systemd/system/multi-user.target.wants/pyfreeMgr.service.
[root@pyfree bin]# 

四、服务管理函数使用说明

在svc.h中提供里四个服务管理函数,本服务程序可以通过这些函式实现对其他应有函数的启停、查询状态等管理,前提是自身服务具有相关权限。大家可以在main.cpp的业务应用代码段测试实现对系统内其他程序进行管理。

相关文章:

  • UI 自动化测试应不应该投入?有没有前途?怎样做最明智?
  • 股票量化交易有什么优势?
  • 元宇宙电商-NFG系统,是如何用数字藏品平台,促进新营销的?
  • thunderbird102编译-环境搭建(1)
  • curl用法:查看响应时间
  • 房地产基础知识!!!
  • 写一个简单食用的拦截器
  • 算法竞赛进阶指南 0x68 二分图的匹配
  • 【无标题】数字ic设计|ic芯片设计全流程
  • Stable Diffusion搭建全过程记录,生成自己的专属艺术照
  • 【iOS自动化测试】第二章:环境安装
  • 源码安装LAMT架构
  • 重要文件即时搞定,不用插电就能打印,汉印MT800移动便携打印机上手
  • MySQL数据库性能分析之explain使用
  • 猿创征文|Linux centos7下利用docker快速部署SQLserver测试学习环境
  • 【跃迁之路】【463天】刻意练习系列222(2018.05.14)
  • ES学习笔记(10)--ES6中的函数和数组补漏
  • Javascript Math对象和Date对象常用方法详解
  • JavaScript设计模式系列一:工厂模式
  • Java方法详解
  • laravel 用artisan创建自己的模板
  • python 学习笔记 - Queue Pipes,进程间通讯
  • vue2.0一起在懵逼的海洋里越陷越深(四)
  • webgl (原生)基础入门指南【一】
  • 对超线程几个不同角度的解释
  • 码农张的Bug人生 - 见面之礼
  • 使用iElevator.js模拟segmentfault的文章标题导航
  • 一起来学SpringBoot | 第三篇:SpringBoot日志配置
  • !!【OpenCV学习】计算两幅图像的重叠区域
  • (android 地图实战开发)3 在地图上显示当前位置和自定义银行位置
  • (C语言)编写程序将一个4×4的数组进行顺时针旋转90度后输出。
  • (k8s中)docker netty OOM问题记录
  • (ros//EnvironmentVariables)ros环境变量
  • (八十八)VFL语言初步 - 实现布局
  • (超详细)语音信号处理之特征提取
  • (二)c52学习之旅-简单了解单片机
  • (附源码)apringboot计算机专业大学生就业指南 毕业设计061355
  • (四) 虚拟摄像头vivi体验
  • (学习日记)2024.01.09
  • (学习日记)2024.04.04:UCOSIII第三十二节:计数信号量实验
  • (一)Mocha源码阅读: 项目结构及命令行启动
  • ..thread“main“ com.fasterxml.jackson.databind.JsonMappingException: Jackson version is too old 2.3.1
  • .NET6 命令行启动及发布单个Exe文件
  • .NET成年了,然后呢?
  • .net反混淆脱壳工具de4dot的使用
  • .NET下ASPX编程的几个小问题
  • [docker] Docker容器服务更新与发现之consul
  • [EFI]MSI GF63 Thin 9SCXR电脑 Hackintosh 黑苹果efi引导文件
  • [Go WebSocket] 多房间的聊天室(五)用多个小锁代替大锁,提高效率
  • [LeetCode]—Longest Palindromic Substring 最长回文子串
  • [LeetCode]Max Points on a Line
  • [LeetCode]-Spiral Matrix III 螺旋矩阵
  • [Linux]进程间通信(进程间通信介绍 | 匿名管道 | 命名管道)
  • [nginx] LEMP 架构随笔
  • [NYOJ 536] 开心的mdd