热门标签 | HotTags
当前位置:  开发笔记 > 运维 > 正文

DDNS的工作原理及其在Linux上的实现

DDNS(DynamicDNS)扩展了DNS将客户端IP与其域名进行静态映射的功能,它可以将同一域名实时地解析为不同的动态IP,而不需要额外的人工干预

DDNS 工作原理的分析
DDNS 的实现最根本的一点是当主机的 IP 地址发生变化的时候,实现 DNS 映射信息的及时更新,应用程序需要及时地获得这一信息,主要的方法可分为两大类:

一类是轮询机制,即:应用程序每隔一定的时间,去从查询主机当前的 IP 地址,并与之前的进行比较,从而判断网络地址是否发生了变化。显然,这种方法不仅效率低下,而且对每次查询 IP 地址的时间间隔很难得到一个折中的数值。
第二类方法是异步实现方式,即:每当主机的 IP 地址发生变化的时候,应用程序能够被及时地通知到。这的确是一个简单而又高效的方法,但与此同时,另一个问题又产生了,那就是:通知源又应该由谁来担当呢?显然,这是处于用户空间的应用程序无法胜任的。于是,我们想到了让内核来充当这一消息源。这样,在内核空间和用户空间之间就需要通过消息来进行通信了。
在 Linux 下用户空间与内核空间的信息交互方式有许多种,比如:软中断、系统调用、netlink 等等。关于这些通信方式的介绍以及其各自的优缺点并不在本文的讨论范围内,您可以自行查看参考资源。

在这许多种通信方式中,netlink 凭借其标准的 socket API、模块化实现、异步通信机制、多播机制等等多种优势,成为了内核与越来越多应用程序之间交互的主要方式。在 Linux 的内核中,已经为我们封装了使用 netlink 对特定网络状态变化进行消息通知的功能,这就是著名的 rtnetlink。有关 netlink 在内核空间实现的详细代码以及其 API 参数的介绍,您可以自行查看参考资源,本文在此不作过多的赘述。
本文讨论的重点是针对 DDNS 这一特定的应用,演示 rtnetlink 检测到 IP 地址发生了变化、并将消息告知用户空间的应用程序的整个过程,以及应用程序利用 netlink 套接字接收消息、并告知 DNS 服务器的实现方法。

DDNS 工作流程的简单介绍
结合上述对 DDNS 工作原理的分析,我们可以将 DDNS 的工作流程简单地用图 1 来表示:
图 1. DDNS 的工作流程图

从图 1 中可以看到,DDNS 的工作流程主要有三个部分:

应用程序实时感知到 IP 地址发生了变化,如上介绍,利用基于 netlink 的异步通知机制可以让应用程序及时得到内核空间对这些事件的“通知”,具体可以分为如下 5 个步骤:

1、内核空间初始化 rtnetlink 模块,创建 NETLINK_ROUTE 协议簇类型的 netlink 套接字;
2、用户空间创建 NETLINK_ROUTE 协议簇类型的 netlink 套接字,并且绑定到 RTMGRP_IPV4_IFADDR 组播 group 中;
3、用户空间接收从内核空间发来的消息,如果没有消息,则阻塞自身;
4、当主机被分配了新的 IPV4 地址,内核空间通过 netlink_broadcast,将 RTM_NEWADDR 消息发送到 RTNLGRP_IPV4_IFADDR 组播 group 中 ;
5、用户空间接收消息,进行验证、处理;

应用程序接收到“通知”后,把 DNS update 信息发送给 DNS 服务器,目的是将更新后的 IP 地址及时地通知 DNS 服务器,以便网络上的主机仍然能够通过原来的域名访问到自己,通用的做法是利用开源软件 nsupdate 发送 DNS update 信息给 DNS 服务器以实现 DNS 信息的动态更新。
最后,对应于第一部分 netlink 套接字的创建,用户空间和内核空间关闭所创建的 netlink 套接字。

下文将详细阐述其中的每一环节及其实现。

内核空间 rtnetlink 检测 IP 地址变化的实现与分析
在我们开始利用 netlink 套接字、实现与内核通信的应用程序之前,先来分析一下内核空间的 rtnetlink 模块是如何工作的。
内核空间 rtnetlink 的初始化
清单 1. rtnetlink 的初始化

 /* 
以下代码摘自 Linux kernel 2.6.18, net/core/rtnetlink.c 文件,
并只选择了与本主题相关的最重要的部分,其他的都用省略号略过,之后的各清单也一样。
 */ 
 void __init rtnetlink_init(void) 
 { 
 ...... 	
 rtnl = netlink_kernel_create(NETLINK_ROUTE, RTNLGRP_MAX, rtnetlink_rcv, THIS_MODULE); 
 if (rtnl == NULL) 
	 panic("rtnetlink_init: cannot initialize rtnetlink\n"); 
 ...... 

 }

从清单 1 中可以看到:
在 rtnetlink 进行初始化的时候,首先会调用 netlink_kernel_create 来创建一个 NETLINK_ROUTE 类型的 netlink 套接字,并指定接收函数为 rtnetlink_rcv,有关 rtnetlink_rcv 的实现细节可以查阅内核 net/core/rtnetlink.c 文件。这里需要指出的是,netlink 提供了包括 NETLINK_ROUTE、NETLINK_FIREWALL、NETLINK_INET_DIAG 等在内的多种协议簇(详细列表及各协议簇的含义可以自行查看参考资源),其中 NETLINK_ROUTE 类型提供了网络地址发生变化的消息,这正是 DDNS 需要用到的。
内核空间 IP 地址变化事件的通知过程
引起主机 IP 地址变化的原因有很多种,如:DHCP 分配的 IP 过期、用户手动修改了 IP 等等。无论何种原因,最终都会触发内核空间对相应事件的通知机制,这里以最常用的修改 IPV4 地址的工具 ifconfig 为例。
ifconfig 先是创建一个 AF_INET 的 socket,然后通过系统调用 ioctl 来完成配置的,ioctl 在内核中对应的函数是 sys_ioctl,对于 IP 地址、子网掩码、默认网关等配置的修改,其最终会调用 devinet_ioctl。devinet_ioctl 函数处理包括 get、set 在内的多种命令,与 DDNS 应用有关的是 set 类命令,图 2 给出了 SIOCSIFADDR 命令(设置网络地址)的 ifconfig 调用树:
图 2. SIOCSIFADDR 命令的 ifconfig 调用树

从图 2 中可以看到,当用户使用 ifconfig 对主机的 IP 地址作了修改,内核在进行了新地址的设置之后,会调用 rtmsg_ifa,传递的事件为 RTM_NEWADDR。
清单 2. rtmsg_ifa 发送 IP 地址变化消息

/* 
以下代码摘自 Linux kernel 2.6.18, net/ipv4/devinet.c 文件
 */ 
 static void rtmsg_ifa(int event, struct in_ifaddr* ifa) 
 { 
	 int size = NLMSG_SPACE(sizeof(struct ifaddrmsg) + 128); 
	 struct sk_buff *skb = alloc_skb(size, GFP_KERNEL); 

	 if (!skb) 
		 netlink_set_err(rtnl, 0, RTNLGRP_IPV4_IFADDR, ENOBUFS); 
 else if (inet_fill_ifaddr(skb, ifa, 0, 0, event, 0) <0) { 
		 kfree_skb(skb); 
		 netlink_set_err(rtnl, 0, RTNLGRP_IPV4_IFADDR, EINVAL); 
	 } else { 
		 netlink_broadcast(rtnl, skb, 0, RTNLGRP_IPV4_IFADDR, GFP_KERNEL); 
	 } 
 }

从清单 2 中可以看到,rtmsg_ifa 的实现主要包括:
首先分配了一块类型为 struct sk_buff 的空间用于存放需要发送的消息内容。
随后,调用 inet_fill_ifaddr 将消息填充至上述缓存(有关消息的格式,您可以自行查看参考资源)。值得注意的是,RTM_NEWADDR 被作为 nlmsg_type 封装到了内核发送给应用程序的 netlink 消息头 nlmsghdr 中,这样用户空间的应用程序在接收后就能够根据 type 来分别处理不同类型的消息了。
rtmsg_ifa 的最后是调用了 netlink_broadcast 将上述封装完毕的 sk_buff 结构广播到 RTNLGRP_IPV4_IFADDR 这个 group,以下是内核空间组播 group 与用户空间组播 group 的对应关系:
清单 3. 内核空间组播 group 与用户空间组播 group 的对应关系

/* 
以下代码摘自 Linux kernel 2.6.18, include/linux/rtnetlink.h 文件
 */ 
 /* RTnetlink multicast groups */ 
 enum rtnetlink_groups { 
	 RTNLGRP_NONE, 
 #define RTNLGRP_NONE 		 RTNLGRP_NONE 
 RTNLGRP_LINK, 
 #define RTNLGRP_LINK 		 RTNLGRP_LINK 
 ..... 
	 RTNLGRP_IPV4_IFADDR, 
 #define RTNLGRP_IPV4_IFADDR 	 RTNLGRP_IPV4_IFADDR 
 ...... 
 }; 

 #ifndef __KERNEL__ 
 /* RTnetlink multicast groups - backwards compatibility for userspace */ 
 #define RTMGRP_LINK 		 1 
 #define RTMGRP_NOTIFY 		 2 
 ...... 
 #define RTMGRP_IPV4_IFADDR 	 0x10 
 ...... 
 #endif

综上所述,当主机的 IP 地址发生变化时,内核会向所有 RTNLGRP_IPV4_IFADDR 组播成员发送 RTM_NEWADDR 消息。因此,在用户空间创建 netlink 套接字时,只需要加入到 RTMGRP_IPV4_IFADDR 这个组播 group 中,就可以实现当本机 IP 地址有更新的时候,DDNS 应用程序能够异步地收到内核空间发来的通知消息了。

用户空间 netlink socket 的创建、绑定与消息接收处理
用户空间创建 netlink 套接字
用户空间的 netlink socket 相关操作与标准 socket API 完全一致,因此可以像使用标准 socket 来进行两台主机间的 IP 协议通信一样地来使用它,这也是 netlink 之所以能够得到越来越广泛应用的一个重要原因。
清单 4. 用户空间创建 netlink socket

#include  
 #include  
 #include  
 #include  
 ...... 
 int main(void) 
 { 
 ...... 
 if((nl_socket = socket(PF_NETLINK, SOCK_DGRAM, NETLINK_ROUTE))==-1) 
   // 指定通信域、通信方式以及通信协议
 exit(1); 
 ...... 
 }

在创建 netlink 套接字时:
我们指定了通信域为 PF_NETLINK,表明这是一个 netlink 套接字。其定义可以在如下所示的内核 include/linux/socket.h 文件中找到。从中我们也可以看到自己非常熟悉的 AF_INET:
清单 5. 清单 4 中使用到的宏定义

 /* 以下代码摘自 include/linux/socket.h 文件 */ 
 /* Supported address families. */ 
 #define AF_UNSPEC 	   0 
 #define AF_UNIX 		 1 	 /* Unix domain sockets 	 */ 
 #define AF_LOCAL 		 1 	 /* POSIX name for AF_UNIX 	 */ 
 #define AF_INET 		 2 	 /* Internet IP Protocol 	 */ 
 ...... 
 #define AF_NETLINK 		 16 
 ...... 
 /* Protocol families, same as address families. */ 
 #define PF_NETLINK 	 AF_NETLINK 
 ......

对于通信方式,我们选择了 SOCK_DGRAM。事实上对于 netlink 这种基于无连接的 socket,使用 SOCK_DGRAM 或者 SOCK_RAW 都是可以的。
对于通信协议,我们使用了 NETLINK_ROUTE。这是因为在清单 1 中,内核空间创建 netlink 套接字、用于发送 IP 地址发生变化的消息时使用的是它,所以这里需要保持一致以进行双方间的通信。
用户空间绑定 netlink 套接字
与标准的 socket 使用方法相似,在建立 netlink 套接字之后,也需要绑定到一个 netlink 地址才能够进行消息的发送与接收。netlink 地址在 struct sockaddr_nl 结构中定义,各结构成员的含义可参见附录 3。
清单 6. 用户空间 bind netlink socket

 #include  
 #include  
 #include  
 #include  
 ...... 
 int main(void) 
 { 
 ...... 
 struct sockaddr_nl addr // 在 include/linux/netlink.h 中定义,结构各成员的含义可参见附录 3 
 memset(&addr, 0, sizeof(addr)); 
 addr.nl_family = PF_NETLINK;   // 定义协议簇为 PF_NETLINK 
 addr.nl_groups = RTMGRP_IPV4_IFADDR // 加入到 RTMGRP_IPV4_IFADDR 组播 group 中
 addr.nl_pid = 0;       // 让 kernel 来分配 pid 

 ...... 
 // 将清单 5 中创建的 netlink 套接字与上述协议地址进行绑定
 if(bind(nl_socket, (struct sockaddr *) &addr, sizeof(addr)) == -1) 
 { 
  close(nl_socket); 
  exit(1); 
 } 
 ...... 
 }

从清单 6 中可以看到,在绑定应用程序的 netlink 套接字时,我们将自己加入到了 RTMGRP_IPV4_IFADDR 组播 group 中,这与前文我们对内核空间 IP 地址变化事件的通知过程的分析是一致的。
用户空间接收并处理内核空间消息
同样与标准的 socket 使用方法类似,用户空间接收内核空间发来的 netlink 消息可以使用 recv、recvfrom 或 recvmsg。值得一提的是,netlink 套接字有自己的消息头:nlmsghdr 结构(该结构具体各成员变量的含义请查看参考资源),而其中的 nlmsg_type 正是我们需要用到的包含了消息类型的字段。
清单 7. 用户空间接收内核空间消息

#define MAX_MSG_SIZE 1024 
 ...... 
 #include  
 #include  
 #include  
 #include  
 ...... 
 struct if_info 
 { 
 int index;    //interface 的序号
 char name[IFNAMSIZ]; //interface 的名称,Linux 内核 include/linux/if.h 中定义了 IFNAMSIZ 
 uint8_t mac[ETH_ALEN]; 
 //interface 的 mac 地址,Linux 内核 include/linux/if_ether.h 中定义了 ETH_ALEN 
 ......      //interface 的其他信息
	 struct if_info *next; // 指向下一个 if_info 结构的指针
 }; 
 static struct if_info *if_list = NULL; // 存放现有的 interface 列表,在每次程序初始化时更新
 int receive_netlink_message(struct nlmsghdr *nl); // 用于接收内核空间发来的消息的函数
 handle_newaddr(struct ifinfomsg *ifi, int len); // 用于处理向 DNS 服务器发送更新的函数
 ...... 
 int main(void) 
 { 
 ...... 
 int len = 0; 
 struct nlmsghdr *nl; // 结构体定义可以参考内核 include/linux/netlink.h 文件

 while((len = receive_netlink_message(&nl)) > 0) 
 { 
	 while(NLMSG_OK(nl, len)) //NLMSG 相关的宏定义可以参考内核 include/linux/netlink.h 文件
	 { 
		 switch(nl->nlmsg_type) 
		 { 
		 case RTM_NEWADDR: // 处理 RTM_NEWADDR 的 netlink 消息类型
 //ifinfomsg 结构可以参考内核 include/linux/rtnetlink.h 文件
			 handle_newaddr((struct ifinfomsg *)NLMSG_DATA(nl), 
			    NLMSG_PAYLOAD(nl, sizeof(struct ifinfomsg))); 
			 break; 
			
		 ...... // 处理其他 netlink 消息类型,如:RTM_NEWLINK,这里略过
			
		 default: 
			 printf("Unknown netlink message type : %d", nl->nlmsg_type); 
		 } 
		
		 nl = NLMSG_NEXT(nl, len); 
	 } 
	 if( nl != NULL ) 
 free(nl); 
 } 
 ...... 
}
			
 int receive_netlink_message(struct nlmsghdr **nl) 
 { 
	 struct iovec iov; // 使用 iovec 进行接收
	 struct msghdr msg = {NULL, 0, &iov, 1, NULL, 0, 0}; // 初始化 msghdr 
	 int length; 
	
 *nl = NULL; 
 if ((*nl = (struct nlmsghdr *) malloc(MAX_MSG_SIZE)) == NULL ) 
		 return 0; 

 iov.iov_base = *nl;    // 封装 nlmsghdr 
 iov.iov_len = MAX_MSG_SIZE; // 指定长度
	
	 length = recvmsg(nl_socket, &msg, 0); 
	
	 if(length <= 0) 
 FREE(*nl); 
	
	 return length; 
 }

应用程序在收到了 RTM_NEWADDR 类型的 netlink 消息后,需要根据 IP 的变化进行处理。这里使用了 handle_newaddr 函数,对 IP 的变化分为了两种情况:一种是 interface 已经存在、仅仅是 IP 发生了变化;另一种是 interface 是新添加的。无论是哪种情况,handle_newaddr 函数在进行了相应的处理之后,都需要调用 update_dns.sh 这个脚本通知 DNS 服务器。关于 update_dns.sh 的实现参见下一章。
清单 8. 用户空间处理内核空间消息

void handle_newaddr(struct ifinfomsg *ifinfo, int len) 
 { 
	 struct if_info *i; 
	
	 for(i = if_list ; i ; i = i->next) // 遍历 in_list,找到 ip 发生变化的 interface 
		 if(i->index == ifinfo->ifi_index) 
			 break; 
	
	 if(i != NULL){ // 找到了相应的 interface,执行 update_dns.sh 
		 system(update_dns.sh); 
		 return; 
	 } 	
	
 // 没有找到对应的 interface,说明该 interface 是新添加的
 if((i = calloc(sizeof(struct if_info), 1)) == NULL)// 分配一个 if_info 结构用于添加新的 interface 
		 exit(1); 
	
	 // 根据 ifinfo->ifi_index 等信息更新 if_info 结构 i,考虑到与 ddns 应用关系不大,限于篇幅,这里略过
	 ...... 
	
	 system(update_dns.sh); // 执行 update_dns.sh 
	
	 i->next = if_list; // 在 if_list 的末尾添加新发现的 interface 
	 if_list = i; 
 }

应用程序与 DNS 服务器的交互
应用程序可以利用开源工具 nsupdate 来向 DNS 服务器发送 DNS update 消息。nsupdate 的详细用法及特性可以请查看参考资源,受篇幅所限,本章将会结合例子简单介绍这个工具的基本用法。
nsupdate 可以从终端或文件中读取命令,每个命令一行。一个空行或一个"send"命令,则会将先前输入的命令发送到 DNS 服务器上,典型的使用方法如清单 9 所示。nsupdate 默认从文件 /etc/resolv.conf 中解析 DNS 服务器和域名,在实际应用中,我们可以首先解析网络参数,生成 nsupdate 的输入文件,最后调用 nsupdate。update_dns.sh 的实现流程如图 3 所示。
清单 9. nsupdate 的使用例子

 # nsupdate 
 > server 9.0.148.50  //DNS 服务器地址 9.0.148.50,默认端口 53 
 > update delete oldhost.example.com A 
  // 删除域名 oldhost.example.com 的任何 A 类型记录 
 > update add newhost.example.com 86400 A 172.16.1.1 
 // 添加一条 172.16.1.1<----->newhost.example.com A 类型的记录,
 // 记录的 TTL 是 24 小时(86400 秒)           
 > send   // 发送命令

图 3. update_dns.sh 的实现流程

netlink socket 的关闭
用户空间关闭 netlink socket
同标准的 socket API 一样,用户空间关闭 netlink socket 使用的也是 close 函数,而且用法完全一致。您可以参考清单 6 中 close 函数在 DDNS 应用程序中的使用。
内核空间关闭 netlink socket
内核空间关闭 netlink socket 使用 sock_release 函数,函数原型如下所示:
清单 10. 内核空间关闭 netlink socket - sock_release

/* 
以下代码摘自 Linux kernel 3.4.3, net/socket.c 文件
 */ 
 void sock_release(struct socket * sock);

其中 sock 为 netlink_kernel_create 创建的 netlink 套接字。
值得一提的是,在最新的 Linux kernel 中,还提供了 netlink_kernel_release 接口,函数原型如下所示:
清单 11. 内核空间关闭 netlink socket —— netlink_kernel_release

 /* 
以下代码摘自 Linux kernel 3.4.3, net/netlink/af_netlink.c 文件
 */ 
 void netlink_kernel_release(struct sock *sk);

其中 sk 为 netlink_kernel_create 创建的 netlink 套接字。

对 DDNS 应用实现的扩展启示
DDNS 利用 rtnetlink 的 NETLINK_ROUTE 协议簇套接字来监听 Linux 内核网络事件“RTM_NEWADDR”,实时更新 DNS 映射信息,从而实现 DNS 信息的动态更新。除了 NETLINK_ROUTE,netlink_family 还提供了多种协议簇来实现多种信息的报告,比如 SELinux、防火墙、Netfilter、IPV6 等。就 NETLINK_ROUTE 协议簇而言,也提供了多个组播 group 对应多种网络连接、网络参数、路由信息、网络流量类别等等变化的事件。
这就启示我们可以利用 netlink,特别是 rtnetlink,实现许多其他的与网络相关的应用。比如:应用程序如果需要实时地监控本机路由表的变化,就可以在用户空间创建 NETLINK_ROUTE 协议簇的 netlink 套接字时把自己加到 RTMGRP_IPV4_ROUTE 及 RTMGRP_NOTIFY 的多播组中(即:addr.nl_groups = RTMGRP_IPV4_ROUTE | RTMGRP_NOTIFY;)通过这种方式,可以实现包括 OSPF、RIPv2、BGP 等在内的多种现行路由协议;再比如:也可以利用 rtnetlink 来监听网络的连接情况,rtnetlink 在初始化的时候将 rtnetlink 消息处理函数 rtnetlink_event 挂到了通知链 netdev_chain 上,网络设备的启动,关闭,更名等事件都能触发通知链并回调消息处理函数,从而组播 RTM_NEWLINK 或者 RTM_DELLINK 信息,向用户程序通知网络的连接情况。

总结
本文结合 DDNS 的工作原理,简单阐释了 DDNS 的实现流程,并在此基础之上,进一步演示了利用 Linux rtnetlink 套接字实现内核空间与用户空间的网络状态 IP 地址变化信息的交互、以及利用 nsupdate 实现 DDNS 客户端与服务器端的同步更新,并且在实际的应用中完全实现了 DDNS 的功能,希望能够为使用 DDNS 进行网络管理的人员及 Linux 网络编程爱好者提供有益的参考。

参考资料

学习

  • 参考“在 Linux 下用户空间与内核空间数据交换的方式,第 1 部分 : 内核启动参数、模块参数与 sysfs、sysctl、系统调用和 netlink”,了解 Linux 用户空间与内核空间的多种数据交换方式,以及它们各自的优缺点。
  • 参考“Linux 系统内核空间与用户空间通信的实现与分析”,了解 netlink 套接字在内核空间的 API
  • 参考 netlink(7) 的 man 手册,了解 Linux netlink 所支持的包括 NETLINK_ROUTE 在内的各种消息类型、消息头格式、消息地址格式及其用法等等。
  • 参考 nsupdate 的 man 手册,了解开源工具 nsupdate 的详细用法与特性
  • 在 developerWorks Linux 专区寻找为 Linux 开发人员(包括 Linux 新手入门)准备的更多参考资料。

作者:王 寒芷, 软件工程师, IBM
王 俊元, 软件工程师, IBM


推荐阅读
  • Linux服务器密码过期策略、登录次数限制、私钥登录等配置方法
    本文介绍了在Linux服务器上进行密码过期策略、登录次数限制、私钥登录等配置的方法。通过修改配置文件中的参数,可以设置密码的有效期、最小间隔时间、最小长度,并在密码过期前进行提示。同时还介绍了如何进行公钥登录和修改默认账户用户名的操作。详细步骤和注意事项可参考本文内容。 ... [详细]
  • 本文介绍了在rhel5.5操作系统下搭建网关+LAMP+postfix+dhcp的步骤和配置方法。通过配置dhcp自动分配ip、实现外网访问公司网站、内网收发邮件、内网上网以及SNAT转换等功能。详细介绍了安装dhcp和配置相关文件的步骤,并提供了相关的命令和配置示例。 ... [详细]
  • Centos7.6安装Gitlab教程及注意事项
    本文介绍了在Centos7.6系统下安装Gitlab的详细教程,并提供了一些注意事项。教程包括查看系统版本、安装必要的软件包、配置防火墙等步骤。同时,还强调了使用阿里云服务器时的特殊配置需求,以及建议至少4GB的可用RAM来运行GitLab。 ... [详细]
  • 本文介绍了在Hibernate配置lazy=false时无法加载数据的问题,通过采用OpenSessionInView模式和修改数据库服务器版本解决了该问题。详细描述了问题的出现和解决过程,包括运行环境和数据库的配置信息。 ... [详细]
  • 这是原文链接:sendingformdata许多情况下,我们使用表单发送数据到服务器。服务器处理数据并返回响应给用户。这看起来很简单,但是 ... [详细]
  • PHP设置MySQL字符集的方法及使用mysqli_set_charset函数
    本文介绍了PHP设置MySQL字符集的方法,详细介绍了使用mysqli_set_charset函数来规定与数据库服务器进行数据传送时要使用的字符集。通过示例代码演示了如何设置默认客户端字符集。 ... [详细]
  • 本文介绍了如何使用php限制数据库插入的条数并显示每次插入数据库之间的数据数目,以及避免重复提交的方法。同时还介绍了如何限制某一个数据库用户的并发连接数,以及设置数据库的连接数和连接超时时间的方法。最后提供了一些关于浏览器在线用户数和数据库连接数量比例的参考值。 ... [详细]
  • 如何使用Java获取服务器硬件信息和磁盘负载率
    本文介绍了使用Java编程语言获取服务器硬件信息和磁盘负载率的方法。首先在远程服务器上搭建一个支持服务端语言的HTTP服务,并获取服务器的磁盘信息,并将结果输出。然后在本地使用JS编写一个AJAX脚本,远程请求服务端的程序,得到结果并展示给用户。其中还介绍了如何提取硬盘序列号的方法。 ... [详细]
  • 本文介绍了如何找到并终止在8080端口上运行的进程的方法,通过使用终端命令lsof -i :8080可以获取在该端口上运行的所有进程的输出,并使用kill命令终止指定进程的运行。 ... [详细]
  • 树莓派Linux基础(一):查看文件系统的命令行操作
    本文介绍了在树莓派上通过SSH服务使用命令行查看文件系统的操作,包括cd命令用于变更目录、pwd命令用于显示当前目录位置、ls命令用于显示文件和目录列表。详细讲解了这些命令的使用方法和注意事项。 ... [详细]
  • 禁止程序接收鼠标事件的工具_VNC Viewer for Mac(远程桌面工具)免费版
    VNCViewerforMac是一款运行在Mac平台上的远程桌面工具,vncviewermac版可以帮助您使用Mac的键盘和鼠标来控制远程计算机,操作简 ... [详细]
  • 本文详细介绍了云服务器API接口的概念和作用,以及如何使用API接口管理云上资源和开发应用程序。通过创建实例API、调整实例配置API、关闭实例API和退还实例API等功能,可以实现云服务器的创建、配置修改和销毁等操作。对于想要学习云服务器API接口的人来说,本文提供了详细的入门指南和使用方法。如果想进一步了解相关知识或阅读更多相关文章,请关注编程笔记行业资讯频道。 ... [详细]
  • 阿,里,云,物,联网,net,core,客户端,czgl,aliiotclient, ... [详细]
  • [译]技术公司十年经验的职场生涯回顾
    本文是一位在技术公司工作十年的职场人士对自己职业生涯的总结回顾。她的职业规划与众不同,令人深思又有趣。其中涉及到的内容有机器学习、创新创业以及引用了女性主义者在TED演讲中的部分讲义。文章表达了对职业生涯的愿望和希望,认为人类有能力不断改善自己。 ... [详细]
  • 如何基于ggplot2构建相关系数矩阵热图以及一个友情故事
    本文介绍了如何在rstudio中安装ggplot2,并使用ggplot2构建相关系数矩阵热图。同时,通过一个友情故事,讲述了真爱难觅的故事背后的数据量化和皮尔逊相关系数的概念。故事中的小伙伴们在本科时参加各种考试,其中有些沉迷网络游戏,有些热爱体育,通过他们的故事,展示了不同兴趣和特长对学习和成绩的影响。 ... [详细]
author-avatar
痴情小猪噜噜1907181048
这个家伙很懒,什么也没留下!
PHP1.CN | 中国最专业的PHP中文社区 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved | 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有