如何使用net-snmp构建代理程序

这篇具有很好参考价值的文章主要介绍了如何使用net-snmp构建代理程序。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。


前言

本篇主要介绍如何使用开源代码net-snmp来构建自己的代理程序,实现自己的专属业务。net-snmp支持静态编译、动态库加载、子代理加载三种方式扩展MIB,构建代理。


一、下载编译net-snmp

1、从net-snmp官方网站http://www.net-snmp.org/download.html下载net-snmp-5.9.3源码。
2、配置编译环境

./configure --prefix=/usr/local/snmpd --enable-ipv6 --with-openssl

–prefix=/usr/local/snmpd 编译生成目标文件放置目录
–enable-ipv6 支持IPV6
–with-openssl 编译带openssl
中间过程全部选择默认,完成后显示配置信息如下:
net-snmp,C++,服务器,linux,开源,后端,网络协议
3、编译

make -j4

-j是多线程编译,提高编译速度
4、拷贝生成文件到/usr/local/snmpd目录

make install

二、创建MIB文件

1.创建MIB

我们先创建一个简单的MIB文件myTest.mib,含有一个读写权限的节点testObject ,保存到 /usr/local/snmpd/share/snmp/mibs/ 目录。

--开始
TEST-MIB DEFINITIONS ::= BEGIN
--引入部分
IMPORTS
    enterprises
        FROM RFC1155-SMI            
    Integer32,OBJECT-TYPE
        FROM SNMPv2-SMI            
    DisplayString
        FROM SNMPv2-TC
    TEXTUAL-CONVENTION
        FROM SNMPv2-TC; --引用结束,用分号

--定义节点
--enterprises的OID是1.3.6.1.4.1
    test OBJECT IDENTIFIER ::= {enterprises 8888}

    testObject OBJECT IDENTIFIER ::= {test 1}  

    testObject OBJECT-TYPE --对象名称
    SYNTAX     Integer32  --类型
    MAX-ACCESS read-write       --访问方式
    STATUS      current     --状态
    DESCRIPTION "test write"    --描述
    ::= {test 1}                --父节点
--结束定义
END

2.使用 snmptranslate查看MIB加载

然后使用 snmptranslate 查看自定义的 TEST-MIB 是否被加载

/usr/local/snmpd/bin/snmptranslate -Tp -IR TEST-MIB::test
+--test(8888)
   |
   +-- -RW- Integer32 testObject(1)

3.查询OID

我们先来获取一下前面定义的 testObject 节点试试。 因为 enterprises 的OID是 1.3.6.1.4.1 ,而 test 是 enterprises 的叶子节点(8888),而 testObject 又是 test 的叶子节点(1)。所以其OID为 1.3.6.1.4.1.8888.1 。
下面使用snmpget来测试一下 ,找不到OID

./snmpget -v2c -c public localhost 1.3.6.1.4.1.8888.1
SNMPv2-SMI::enterprises.8888.1 = No Such Object available on this agent at this OID

三、使用MIB2C生成.c和.h文件

执行以下命令生成.c和.h文件,其中test表示要生成文件的MIB节点名称。

MIBS="+/usr/local/snmpd/share/snmp/mibs/myTest.mib" /usr/local/snmpd/bin/mib2c test

命令执行后显示如下:

writing to -
mib2c has multiple configuration files depending on the type of
code you need to write.  You must pick one depending on your need.

You requested mib2c to be run on the following part of the MIB tree:
  OID:                       	    test
  numeric translation:       	    .1.3.6.1.4.1.8888
  number of scalars within:         1
  number of tables within:          0
  number of notifications within:   0

First, do you want to generate code that is compatible with the
ucd-snmp 4.X line of code, or code for the newer Net-SNMP 5.X code
base (which provides a much greater choice of APIs to pick from).  You
can also generate C-header files containing define statements for
column numbers, enums, etc.  Or do you simply want to translate the
MIB structures into documentation?

  1) ucd-snmp style code
  2) Net-SNMP style code
  3) Generate header files
  4) Generate documentation

Select your choice : 2

**********************************************************************
		 GENERATING CODE FOR SCALAR OBJECTS:
**********************************************************************

  It looks like you have some scalars in the mib you requested, so I
  will now generate code for them if you wish.  You have two choices
  for scalar API styles currently.  Pick between them, or choose not
  to generate any code for the scalars:

  1) If you're writing code for some generic scalars
     (by hand use: "mib2c -c mib2c.scalar.conf test")

  2) If you want to magically "tie" integer variables to integer
     scalars
     (by hand use: "mib2c -c mib2c.int_watch.conf test")

  3) Don't generate any code for the scalars

Select your choice: 1
    using the mib2c.scalar.conf configuration file to generate your code.
writing to test.h
writing to test.c



**********************************************************************
* NOTE WELL: The code generated by mib2c is only a template.  *YOU*  *
* must fill in the code before it'll work most of the time.  In many *
* cases, spots that MUST be edited within the files are marked with  *
* /* XXX */ or /* TODO */ comments.                                  *
**********************************************************************
running indent on test.h
running indent on test.c

生成test.c文件如下:

/*
 * Note: this file originally auto-generated by mib2c
 * using mib2c.scalar.conf
 */

#include <net-snmp/net-snmp-config.h>
#include <net-snmp/net-snmp-includes.h>
#include <net-snmp/agent/net-snmp-agent-includes.h>
#include "test.h"

/** Initializes the test module */
void
init_test(void)
{
    const oid testObject_oid[] = { 1,3,6,1,4,1,8888,1 };

  DEBUGMSGTL(("test", "Initializing\n"));

    netsnmp_register_scalar(
        netsnmp_create_handler_registration("testObject", handle_testObject,
                               testObject_oid, OID_LENGTH(testObject_oid),
                               HANDLER_CAN_RWRITE
        ));
}

int
handle_testObject(netsnmp_mib_handler *handler,
                          netsnmp_handler_registration *reginfo,
                          netsnmp_agent_request_info   *reqinfo,
                          netsnmp_request_info         *requests)
{
    int ret;
    /* We are never called for a GETNEXT if it's registered as a
       "instance", as it's "magically" handled for us.  */

    /* a instance handler also only hands us one request at a time, so
       we don't need to loop over a list of requests; we'll only get one. */
    
    switch(reqinfo->mode) {

        case MODE_GET:
            snmp_set_var_typed_value(requests->requestvb, ASN_INTEGER,
                                     /* XXX: a pointer to the scalar's data */,
                                     /* XXX: the length of the data in bytes */);
            break;

        /*
         * SET REQUEST
         *
         * multiple states in the transaction.  See:
         * http://www.net-snmp.org/tutorial-5/toolkit/mib_module/set-actions.jpg
         */
        case MODE_SET_RESERVE1:
                /* or you could use netsnmp_check_vb_type_and_size instead */
            ret = netsnmp_check_vb_type(requests->requestvb, ASN_INTEGER);
            if ( ret != SNMP_ERR_NOERROR ) {
                netsnmp_set_request_error(reqinfo, requests, ret );
            }
            break;

        case MODE_SET_RESERVE2:
            /* XXX malloc "undo" storage buffer */
            if (/* XXX if malloc, or whatever, failed: */) {
                netsnmp_set_request_error(reqinfo, requests, SNMP_ERR_RESOURCEUNAVAILABLE);
            }
            break;

        case MODE_SET_FREE:
            /* XXX: free resources allocated in RESERVE1 and/or
               RESERVE2.  Something failed somewhere, and the states
               below won't be called. */
            break;

        case MODE_SET_ACTION:
            /* XXX: perform the value change here */
            if (/* XXX: error? */) {
                netsnmp_set_request_error(reqinfo, requests, /* some error */);
            }
            break;

        case MODE_SET_COMMIT:
            /* XXX: delete temporary storage */
            if (/* XXX: error? */) {
                /* try _really_really_ hard to never get to this point */
                netsnmp_set_request_error(reqinfo, requests, SNMP_ERR_COMMITFAILED);
            }
            break;

        case MODE_SET_UNDO:
            /* XXX: UNDO and return to previous value for the object */
            if (/* XXX: error? */) {
                /* try _really_really_ hard to never get to this point */
                netsnmp_set_request_error(reqinfo, requests, SNMP_ERR_UNDOFAILED);
            }
            break;

        default:
            /* we should never get here, so this is a really bad error */
            snmp_log(LOG_ERR, "unknown mode (%d) in handle_testObject\n", reqinfo->mode );
            return SNMP_ERR_GENERR;
    }

    return SNMP_ERR_NOERROR;

生成的.c文件是一个模板,我们需要手动修改几个地方:

/*
 * Note: this file originally auto-generated by mib2c
 * using mib2c.scalar.conf
 */

#include <net-snmp/net-snmp-config.h>
#include <net-snmp/net-snmp-includes.h>
#include <net-snmp/agent/net-snmp-agent-includes.h>
#include "test.h"

//add
int value = 10;

/** Initializes the test module */
void
init_test(void)
{
    const oid testObject_oid[] = { 1,3,6,1,4,1,8888,1 };

  DEBUGMSGTL(("test", "Initializing\n"));

    netsnmp_register_scalar(
        netsnmp_create_handler_registration("testObject", handle_testObject,
                               testObject_oid, OID_LENGTH(testObject_oid),
                               HANDLER_CAN_RWRITE
        ));
}

int
handle_testObject(netsnmp_mib_handler *handler,
                          netsnmp_handler_registration *reginfo,
                          netsnmp_agent_request_info   *reqinfo,
                          netsnmp_request_info         *requests)
{
    int ret;
    /* We are never called for a GETNEXT if it's registered as a
       "instance", as it's "magically" handled for us.  */

    /* a instance handler also only hands us one request at a time, so
       we don't need to loop over a list of requests; we'll only get one. */
    switch(reqinfo->mode) {

        case MODE_GET:
            //add
            snmp_set_var_typed_value(requests->requestvb, ASN_INTEGER,
                                     &value/* XXX: a pointer to the scalar's data */,
                                     4/* XXX: the length of the data in bytes */);
            break;

        /*
         * SET REQUEST
         *
         * multiple states in the transaction.  See:
         * http://www.net-snmp.org/tutorial-5/toolkit/mib_module/set-actions.jpg
         */
        case MODE_SET_RESERVE1:
                /* or you could use netsnmp_check_vb_type_and_size instead */
            ret = netsnmp_check_vb_type(requests->requestvb, ASN_INTEGER);
            if ( ret != SNMP_ERR_NOERROR ) {
                netsnmp_set_request_error(reqinfo, requests, ret );
            }
            break;

        case MODE_SET_RESERVE2:
            /* XXX malloc "undo" storage buffer */
            if (0/* XXX if malloc, or whatever, failed: */) {
                netsnmp_set_request_error(reqinfo, requests, SNMP_ERR_RESOURCEUNAVAILABLE);
            }
            break;

        case MODE_SET_FREE:
            /* XXX: free resources allocated in RESERVE1 and/or
               RESERVE2.  Something failed somewhere, and the states
               below won't be called. */
            break;

        case MODE_SET_ACTION:
            /* XXX: perform the value change here */
            //add
            value = *requests->requestvb->val.integer;
			printf("set: %d\n", value);
            if (0/* XXX: error? */) {
                netsnmp_set_request_error(reqinfo, requests, SNMP_ERR_BADVALUE/* some error */);
            }
            break;

        case MODE_SET_COMMIT:
            /* XXX: delete temporary storage */
            if (0/* XXX: error? */) {
                /* try _really_really_ hard to never get to this point */
                netsnmp_set_request_error(reqinfo, requests, SNMP_ERR_COMMITFAILED);
            }
            break;

        case MODE_SET_UNDO:
            /* XXX: UNDO and return to previous value for the object */
            if (0/* XXX: error? */) {
                /* try _really_really_ hard to never get to this point */
                netsnmp_set_request_error(reqinfo, requests, SNMP_ERR_UNDOFAILED);
            }
            break;

        default:
            /* we should never get here, so this is a really bad error */
            snmp_log(LOG_ERR, "unknown mode (%d) in handle_testObject\n", reqinfo->mode );
            return SNMP_ERR_GENERR;
    }

    return SNMP_ERR_NOERROR;
}

四、构建代理

1.静态编译

1)将test.c和test.h放到net-snmp源码的agent/mibgroup/目录下,进行编译:

./configure --prefix=/usr/local/snmpd --enable-ipv6 --with-openssl --with-mib-modules=test

–with-mib-modules=test是将test模块加载到snmpd中,这样编译出来的snmpd程序就支持test中的节点操作,假如要将test1、test2二个模块加载进去,则–with-mib-modules=test1 test2。生成配置信息如下:

---------------------------------------------------------
            Net-SNMP configuration summary:
---------------------------------------------------------

  SNMP Versions Supported:    1 2c 3
  Building for:               linux
  Net-SNMP Version:           5.9.3
  Network transport support:  Callback Unix Alias TCP UDP TCPIPv6 UDPIPv6 IPv4Base SocketBase TCPBase UDPIPv4Base UDPBase IPBase IPv6Base
  SNMPv3 Security Modules:     usm
  Agent MIB code:            default_modules test =>  snmpv3mibs mibII ucd_snmp notification notification-log-mib target agent_mibs agentx disman/event disman/schedule utilities host
  MYSQL Trap Logging:         unavailable
  Embedded Perl support:      enabled
  SNMP Perl modules:          building -- embeddable
  SNMP Python modules:        disabled
  Crypto support from:        use_pkg_config_for_openssl
  Authentication support:     MD5 SHA1 SHA224 SHA256 SHA384 SHA512
  Encryption support:         DES AES AES128 AES192 AES192C AES256 AES256C
  Local DNSSEC validation:    disabled

---------------------------------------------------------

重新编译生成snmpd

make -j4
make install

2)创建snmpd.conf配置文件:

sudo cat ../share/snmp/snmpd.conf 
rwcommunity public

3)启动snmpd

./snmpd -c ../share/snmp/snmpd.conf -C -Lo
 NET-SNMP version 5.9.3

4)查询、设置OID的值

./snmpget -v2c -c public localhost 1.3.6.1.4.1.8888.1.0
SNMPv2-SMI::enterprises.8888.1.0 = INTEGER: 10
./snmpset -v2c -c public localhost 1.3.6.1.4.1.8888.1.0 i 12
SNMPv2-SMI::enterprises.8888.1.0 = INTEGER: 12
./snmpget -v2c -c public localhost 1.3.6.1.4.1.8888.1.0
SNMPv2-SMI::enterprises.8888.1.0 = INTEGER: 12

2.动态库加载

1)编译动态库libtest.so

gcc -c -fpic test.c -o test.o -I/usr/local/snmpd/include
gcc -shared test.o -o libtest.so

2)修改snmpd.conf配置文件支持加载libtest.so动态库

cat ../share/snmp/snmpd.conf 
rwcommunity public
dlmod test /usr/local/snmpd/share/snmp/mibs/libtest.so

3)同上启动snmpd,查询设置OID的值。
注意动态库加载和子代理加载使用的snmpd为没有编译test.c的snmpd。

3.子代理加载

1)编译的时候需要用到 net-snmp-config工具 ,用来做两件事,一个是生成中间代码,然后用gcc来编译它。因为test.c文件中没有main函数, 所以要生成中间代码,其实就是主程序。
具体使用如下

net-snmp-config --compile-subagent test test.c

–compile-subagent的意思是编译为subagent程序。
test 是编译后输出程序名
test.c是要编译的文件
还可以加上 --norm 参数来阻止编译后删除生成的中间代码文件。我们可以试一下

net-snmp-config --compile-subagent --norm test ../share/snmp/mibs/test.c
generating the temporary code file: netsnmptmp.19614.c
checking for init_test in ../share/snmp/mibs/test.c
init_test(void)
checking for shutdown_test in ../share/snmp/mibs/test.c
running: gcc  -fno-strict-aliasing -g -O2 -Ulinux -Dlinux=linux  -I. -I/usr/include -o test netsnmptmp.19614.c  ../share/snmp/mibs/test.c  -L/usr/lib -lnetsnmpmibs -lnetsnmpagent -lnetsnmp -lnetsnmpmibs -lpci -ldl  -lnetsnmpagent   -lnetsnmp  
leaving the temporary code file: netsnmptmp.19614.c
subagent program test created

编译后我们可以看到,当前目录下出现了编译后的程序 test 和一个 netsnmptmp.19614.c 的文件。我们打开netsnmptmp.19614.c文件,就可以看到这里有一个 main 函数。netsnmptmp.19614.c的代码如下,有兴趣的可以看一下

/* generated from net-snmp-config */
#include <net-snmp/net-snmp-config.h>
#ifdef HAVE_SIGNAL
#include <signal.h>
#endif /* HAVE_SIGNAL */
#include <net-snmp/net-snmp-includes.h>
#include <net-snmp/agent/net-snmp-agent-includes.h>
const char *app_name = "test";
static int reconfig = 0;

extern int netsnmp_running;

#ifdef __GNUC__
#define UNUSED __attribute__((unused))
#else
#define UNUSED
#endif

RETSIGTYPE
stop_server(UNUSED int a) {
    netsnmp_running = 0;
}

#ifdef SIGHUP
RETSIGTYPE
hup_handler(int sig)
{
    reconfig = 1;
    signal(SIGHUP, hup_handler);
}
#endif

static void
usage(const char *prog)
{
    fprintf(stderr,
            "USAGE: %s [OPTIONS]\n"
            "\n"
            "OPTIONS:\n", prog);

    fprintf(stderr,
            "  -d\t\t\tdump all traffic\n"
            "  -D TOKEN[,...]\tturn on debugging output for the specified "
            "TOKENs\n"
            "\t\t\t   (ALL gives extremely verbose debugging output)\n"
            "  -f\t\t\tDo not fork() from the calling shell.\n"
            "  -h\t\t\tdisplay this help message\n"
            "  -H\t\t\tdisplay a list of configuration file directives\n"
            "  -L LOGOPTS\t\tToggle various defaults controlling logging:\n");
    snmp_log_options_usage("\t\t\t  ", stderr);
#ifndef DISABLE_MIB_LOADING
    fprintf(stderr,
            "  -m MIB[" ENV_SEPARATOR "...]\t\tload given list of MIBs (ALL loads "
            "everything)\n"
            "  -M DIR[" ENV_SEPARATOR "...]\t\tlook in given list of directories for MIBs\n");
#endif /* DISABLE_MIB_LOADING */
#ifndef DISABLE_MIB_LOADING
    fprintf(stderr,
            "  -P MIBOPTS\t\tToggle various defaults controlling mib "
            "parsing:\n");
    snmp_mib_toggle_options_usage("\t\t\t  ", stderr);
#endif /* DISABLE_MIB_LOADING */
    fprintf(stderr,
            "  -v\t\t\tdisplay package version number\n"
            "  -x TRANSPORT\tconnect to master agent using TRANSPORT\n");
    exit(1);
}

static void
version(void)
{
    fprintf(stderr, "NET-SNMP version: %s\n", netsnmp_get_version());
    exit(0);
}

int
main (int argc, char **argv)
{
  int arg;
  char* cp = NULL;
  int dont_fork = 0, do_help = 0;

  while ((arg = getopt(argc, argv, "dD:fhHL:"
#ifndef DISABLE_MIB_LOADING
                       "m:M:"
#endif /* DISABLE_MIB_LOADING */
                       "n:"
#ifndef DISABLE_MIB_LOADING
                       "P:"
#endif /* DISABLE_MIB_LOADING */
                       "vx:")) != EOF) {
    switch (arg) {
    case 'd':
      netsnmp_ds_set_boolean(NETSNMP_DS_LIBRARY_ID,
                             NETSNMP_DS_LIB_DUMP_PACKET, 1);
      break;

    case 'D':
      debug_register_tokens(optarg);
      snmp_set_do_debugging(1);
      break;

    case 'f':
      dont_fork = 1;
      break;

    case 'h':
      usage(argv[0]);
      break;

    case 'H':
      do_help = 1;
      break;

    case 'L':
      if (snmp_log_options(optarg, argc, argv) < 0) {
        exit(1);
      }
      break;

#ifndef DISABLE_MIB_LOADING
    case 'm':
      if (optarg != NULL) {
        setenv("MIBS", optarg, 1);
      } else {
        usage(argv[0]);
      }
      break;

    case 'M':
      if (optarg != NULL) {
        setenv("MIBDIRS", optarg, 1);
      } else {
        usage(argv[0]);
      }
      break;
#endif /* DISABLE_MIB_LOADING */

    case 'n':
      if (optarg != NULL) {
        app_name = optarg;
        netsnmp_ds_set_string(NETSNMP_DS_LIBRARY_ID,
                              NETSNMP_DS_LIB_APPTYPE, app_name);
      } else {
        usage(argv[0]);
      }
      break;

#ifndef DISABLE_MIB_LOADING
    case 'P':
      cp = snmp_mib_toggle_options(optarg);
      if (cp != NULL) {
        fprintf(stderr, "Unknown parser option to -P: %c.\n", *cp);
        usage(argv[0]);
      }
      break;
#endif /* DISABLE_MIB_LOADING */

    case 'v':
      version();
      break;

    case 'x':
      if (optarg != NULL) {
        netsnmp_ds_set_string(NETSNMP_DS_APPLICATION_ID,
                              NETSNMP_DS_AGENT_X_SOCKET, optarg);
      } else {
        usage(argv[0]);
      }
      break;

    default:
      fprintf(stderr, "invalid option: -%c\n", arg);
      usage(argv[0]);
      break;
    }
  }

  if (do_help) {
    netsnmp_ds_set_boolean(NETSNMP_DS_APPLICATION_ID,
                           NETSNMP_DS_AGENT_NO_ROOT_ACCESS, 1);
  } else {
    /* we are a subagent */
    netsnmp_ds_set_boolean(NETSNMP_DS_APPLICATION_ID,
                           NETSNMP_DS_AGENT_ROLE, 1);

    if (!dont_fork) {
      if (netsnmp_daemonize(1, snmp_stderrlog_status()) != 0)
        exit(1);
    }

    /* initialize tcpip, if necessary */
    SOCK_STARTUP;
  }

  /* initialize the agent library */
  init_agent(app_name);

  /* initialize your mib code here */
  init_test();

  /* test will be used to read test.conf files. */
  init_snmp("test");

  if (do_help) {
    fprintf(stderr, "Configuration directives understood:\n");
    read_config_print_usage("  ");
    exit(0);
  }

  /* In case we received a request to stop (kill -TERM or kill -INT) */
  netsnmp_running = 1;
#ifdef SIGTERM
  signal(SIGTERM, stop_server);
#endif
#ifdef SIGINT
  signal(SIGINT, stop_server);
#endif
#ifdef SIGHUP
  signal(SIGHUP, hup_handler);
#endif

  /* main loop here... */
  while(netsnmp_running) {
    if (reconfig) {
      free_config();
      read_configs();
      reconfig = 0;
    }
    agent_check_and_process(1);
  }

  /* at shutdown time */
  snmp_shutdown(app_name);

  /* deinitialize your mib code here */

  /* shutdown the agent library */
  shutdown_agent();
  SOCK_CLEANUP;
  exit(0);
}

2)修改snmpd.conf配置文件支持子代理模式

sudo cat ../share/snmp/snmpd.conf 
rwcommunity public
master agentx

3)启动snmpd

./snmpd -c ../share/snmp/snmpd.conf -C -Lo
Turning on AgentX master support.
 NET-SNMP version 5.9.3

4)启动子代理

./test

5)同上,查询、设置OID的值


总结

本篇主要通过一个简单的MIB测试文件,演示了如何使用net-snmp来构建自己业务代理程序的过程,分别测试了三种方式: 静态编译、动态库加载、子代理加载。但是要真正构建一个业务代理程序,还涉及到表节点以及业务数据获取等内容,需要进一步深入的研究。文章来源地址https://www.toymoban.com/news/detail-762063.html

到了这里,关于如何使用net-snmp构建代理程序的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • 内网穿透代理服务器nps使用初探(三)微信小程序内网穿透

    已完成NPS 的部署,部署教程请参照:https://gaoming.blog.csdn.net/article/details/128203454 内网IP 系统 内核 192.168.1.113 win11 windows_amd64_client 公网IP 系统 内核 39.97.238.139 CentOS linux_amd64_server 内网网站(内网机器 192.168.1.113 上部署的 Web 服务,因微信小程序开发需要采用 https 协议, https 端

    2024年02月09日
    浏览(69)
  • 如何使用Docker将.Net6项目部署到Linux服务器(一)

    目录 一 配置服务器环境 1.1 配置yum                                                                                                                     1.1.1 更新yum包 1.1.2 yum命令 1.2 配置docker                                                           

    2024年02月04日
    浏览(36)
  • 如何在 Linux 中安装、设置和使用 SNMP?

    SNMP(Simple Network Management Protocol)是一种用于管理和监控网络设备的协议。它允许网络管理员通过远程方式收集设备的运行状态、性能数据和错误信息,以便进行故障排除和网络优化。在Linux系统中,我们可以安装、设置和使用SNMP来监控和管理服务器和网络设备。本文将详细

    2024年02月11日
    浏览(32)
  • 如何在 Linux 中安装、设置和使用 SNMP

    SNMP(Simple Network Management Protocol)是一种用于管理和监控网络设备的协议。它允许网络管理员通过远程方式收集设备的运行状态、性能数据和错误信息,以便进行故障排除和网络优化。在Linux系统中,我们可以安装、设置和使用SNMP来监控和管理服务器和网络设备。本文将详细

    2024年02月09日
    浏览(25)
  • 如何使用Vue.js构建桌面应用程序

    Vue.js是一个流行的JavaScript框架,可以用于构建Web应用程序。但是,Vue.js也可以用于构建桌面应用程序。本文将介绍如何使用Vue.js构建桌面应用程序,包括以下步骤: 1. 选择一个Vue.js框架:选择一个适合你的Vue.js框架。我们推荐使用Electron,因为它是最流行的桌面应用程序框架

    2024年02月15日
    浏览(38)
  • 如何使用Inno Setup制作Unity构建程序的Windows安装程序

    必须包括: Data文件夹(xxx_Data) Mono文件夹(MonoBleedingEdge) 打包的应用程序文件(xxx.exe) Unity播放器dll文件(UnityPlayer.dll) 可选包括: Unity程序崩溃处理(UnityCrashHandler64.exe) 下载地址:https://jrsoftware.org/isdl.php 找到Stable Release,点击Download Sites的“US”下载 运行安装程序

    2024年03月17日
    浏览(43)
  • 如何使用 MongoDB 构建采用 AI 技术的应用程序

    使用MongoDB构建采用AI技术的应用程序通常涉及以下步骤: 确定应用场景 : 首先,确定您的应用程序如何使用AI。这可以包括机器学习(ML)、深度学习(DL)、自然语言处理(NLP)等。 确定您需要收集和处理的数据类型以及这些数据如何支持AI模型。 设计数据模型 : 根据

    2024年02月19日
    浏览(45)
  • 腾讯WiFi码推广,扫码连WiFi小程序代理入驻后如何推广使用教程!

    针对小程序:WiFi大师至尊版,很多朋友入驻团长等级代理商以后不会使用,根据这个教程进行操作即可。 1.拓展员如何开通账号? 团长打开小程序,点击右下角“我的”进入个人页面,在页面最下方有邀请提示框,点击进入邀请页面。如图 进入到邀请入驻页面后,选择“邀

    2024年02月09日
    浏览(72)
  • .NET Emit 入门教程:第二部分:构建动态程序集(追加构建静态程序集教程)

    在本部分中,我们将深入探讨如何使用C# Emit 来创建动态程序集。 动态程序集是在运行时生成的,为我们提供了一种灵活、动态地构建和加载程序集的能力。 程序集是.NET中的基本部署单位,它包含了可执行代码、资源、元数据等信息,是.NET应用程序的基本组成单元之一。

    2024年03月21日
    浏览(35)
  • 解释 RESTful API,以及如何使用它构建 web 应用程序

             RESTful API stands for Representational State Transfer Application Programming Interface. It is a set of principles and guidelines for building web services that provide data in a standard format, typically JSON or XML. RESTful API emphasizes on uniformity, scalability, reliability, performance, and flexibility. It operates on HTTP and follo

    2024年02月14日
    浏览(51)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包