序号 |
功能需求 |
说明 |
1 |
基本功能 |
1. 根据NTP 服务协议,与外部 ntp 服务器进行通讯。 2. 解析ntp 协议包,从中提取有效信息。 3. 通过计算得到网络延时、本地时差,并计算出本地的标准时间。 4. 重新设定本地时间。 |
2 |
扩展功能 |
1. 设置校准时间间隔,即每隔指定时间校准一次。 2. 实现服务日志,对启动服务后的程序状态进行记录。 3. 实现后台运行,使程序脱离终端在后台运行,程序需通过解析配置文档获得配置信息。 |
3 |
服务模式 |
1. 交互式运行:在终端交互式运行服务程序,交互式获取配置信息。 2. 守护进程模式:作为守护进程在后台运行,通过配置文件获取配置信息,配置文件默认路劲:/etc/ntpclient/ntpclient.conf 。 |
4 |
启动脚本 |
1. 默认路径:/etc/ini.d/ntpclient 。 2. 启动选项:start ,后台启动服务。 3. 启动选项:stop ,终止后台服务。 4. 启动选项:status ,查看服务状态。 5. 启动选项:restart ,重启服务。 |
5 |
安装脚本 |
1. 安装选项:-i ,安装程序。 2. 安装选项:-d ,卸载程序。 |
实验要求视频讲解:
<<Linux应用程序开发班 >>/ 第 8 天 -NTP 网络协议实现 / 培训视频 /NTP网络协议实现 -项目要求 .avi
l 背景知识:
1. 培训视频:
《Linux 应用程序开发班》 / 第 8 天 -NTP 网络协议实现 /培训视频 /NTP网络协议实现 .avi 。
2. NTP协议介绍:
网络时间协议(NTP )是一种通过因特网服务于计算机时钟的同步时间协议。它提供了一种同步时间机制,能在庞大而复杂多样的因特网中用光速调整时间分配。它使用的是可返回时间设计方案,其特点是:时间服务器是一种分布式子网,能自我组织操作、分层管理配置,经过有线或无线方式同步逻辑时钟达到国家标准时间。此外,通过本地路由选择运算法则及时间后台程序,服务器可以重新分配标准时间。
NTP 的校时涉及三个概念 — 时间偏差、时间延迟及差量,它们与指定参考时钟都是相关联的。时钟偏差表示本地时钟与参考时钟之间的偏差数;时间延迟表示在指定时间内由一方发送消息到另一方接收到消息间的延时时间;差量表示了相对于参考时钟本地时钟的最大偏差错误。因为大多数主机时间服务器通过其它对等时间服务器达到同步,所以这三个参量都有两个组成部分:其一是由对等决定的部分,这部分是相对于原始标准时间的参考来源而言;其二是由主机衡量的部分,这部分是相对于对等而言。每一部分在协议中都是独立维持的,从而可以使错误控制和子网本身的管理操作变得容易。它们不仅提供了偏移和延迟的精密测量,而且提供了明确的最大错误范围,这样用户接口不但可以决定时间,而且可以决定时间的准确度。
3. NTP协议包结构:
进行网络协议实现时最重要的是了解协议数据格式。除了可扩展部分,基本的NTP 数据包有 48 个字节,其中 NTP 包头 16 字节,时间戳 32 个字节。其协议格式下表所示。
2 |
5 |
8 |
16 |
24 |
32bit |
LI (2 ) |
VN (3 ) |
Mode (3 ) |
Stratum (8 ) |
Poll (8 ) |
Precision (8 ) |
Root Delay |
|||||
Root Dispersion |
|||||
Reference Identifier |
|||||
Reference timestamp( 64 ) |
|||||
Originate Timestamp( 64 ) |
|||||
Receive Timestamp( 64 ) |
|||||
Transmit Timestamp( 64 ) |
|||||
Key Identifier( optional )( 32 ) |
|||||
Message digest( optional )( 128 ) |
· LI:跳跃指示器,警告在当月最后一天的最终时刻插入的迫近闺秒(闺秒)。
· VN:版本号。
· Mode:模式。该字段包括以下值:0-预留;1-对称行为;3-客户机;4-服务器;5-广播;6-NTP 控制信息
· Stratum:对本地时钟级别的整体识别。
· Poll:有符号整数表示连续信息间的最大间隔。
· Precision:有符号整数表示本地时钟精确度。
· Root Delay:有符号固定点序号表示主要参考源的总延迟,很短时间内的位15到16间的分段点。
· Root Dispersion:无符号固定点序号表示相对于主要参考源的正常差错,很短时间内的位15到16间的分段点。
· Reference Identifier:识别特殊参考源。
· Originate Timestamp:这是向服务器请求分离客户机的时间,采用64位时标(Timestamp)格式。
· Receive Timestamp:这是向服务器请求到达服务器的时间,采用64位时标(Timestamp)格式。
· Transmit Timestamp:这是向客户机答复分离服务器的时间,采用64位时标(Timestamp)格式。
Authenticator(Optional):当实现了 NTP 认证模式,主要标识符和信息数字域就包括已定义的信息认证代码(MAC)信息。
4. Daemon 进程概念:
Daemon是长时间运行的进程,通常在系统启动后就运行,在系统关闭时才结束。一般说Daemon程序在后台运行,是因为它没有控制终端,无法和前台的用户交互。Daemon程序一般都作为服务程序使用,等待客户端程序与它通信。我们也把运行的Daemon程序称作守护进程。
比如,我们的网络服务程序,可以在完成创建套接口,绑定套接口,设置套接口为监听模式后,变成守护进程进入后台执行而不占用控制终端,这是网络服务程序的常用模式。Linux下的网络服务程序,如samba、FTP、Telnet一般都是由守护进程(Daemon)来实现的。Linux的守护进程一般都命名为*d的形式,如httpd,telnetd等等。守护进程一旦脱离了终端,退出就成了问题。可以使用命令:ps aux|grep *,其中*号为进程名,找到相应进程的ID,再使用命令:kill -SIGTERM ID,终止它。
5. Daemon 程序编写:
编写Daemon程序有一些基本的规则,以避免不必要的麻烦。
(1) 首先是程序运行后调用fork,并让父进程退出。子进程获得一个新的进程ID,但 继承了父进程的进程组ID。
(2) 调用setsid创建一个新的session,使自己成为新session和新进程组的leader,并使进程没有控制终端(tty)。
(3) 改变当前工作目录至根目录,以免影响可加载文件系统。或者也可以改变到某些特定的目录。
(4) 设置文件创建mask为0,避免创建文件时权限的影响。
(5) 关闭不需要的打开文件描述符。因为Daemon程序在后台执行,不需要于终端交互,通常就关闭STDIN、STDOUT和STDERR。其它根据实际情况处理。
另一个问题是Daemon程序不能和终端交互,也就无法使用printf方法输出信息了。我们可以使用syslog机制来实现信息的输出,方便程序的调试。当然,你也可以把这些信息输出到自己的日志文件中查看。
下面给出一段Daemon程序的例子:
#include <unistd.h> #include <sys/types.h> #include <sys/stat.h> #include <stdlib.h> #include <stdio.h> #include <syslog.h> #include <signal.h>
int daemon_init(void) { pid_t pid; if((pid = fork()) < 0) return(-1); else if(pid != 0) exit(0); /* parent exit */ /* child continues */ setsid(); /* become session leader */ chdir("/"); /* change working directory */ umask(0); /* clear file mode creation mask */ close(0); /* close stdin */ close(1); /* close stdout */ close(2); /* close stderr */ return(0); }
void sig_term(int signo) { if(signo == SIGTERM) //catched signal sent by kill(1) command { syslog(LOG_INFO, "program terminated."); closelog(); exit(0); } }
int main(void) { daemon_init(); openlog("daemontest", LOG_PID, LOG_USER); syslog(LOG_INFO, "program started."); signal(SIGTERM, sig_term); /* arrange to catch the signal */ while(1) { sleep(1); /* put your main program here */ } return(0); } |
1. src子目录:
实验代码stdinc.h,这是程序使用到的标准头文件的一个集合,这样做不仅可以减少编码工作量,也不至于因为修改了头文件的内容而减慢编译的速度。
#include <stdio.h> #include <stdlib.h> #include <string.h> #include <stdarg.h> #include <unistd.h> #include <netinet/in.h> #include <sys/socket.h> #include <sys/types.h> #include <arpa/inet.h> #include <netdb.h> #include <sys/time.h> #include <time.h> #include <sys/select.h> #include <stdbool.h> #include <signal.h> #include <sys/param.h> #include <sys/stat.h> #include <fcntl.h> |
实验代码def.h,这里主要是一些宏定义和结构类型定义。另外,调试宏函数PDEBUG的定义方式值得大家注意。
#ifndef __DEF_H__ #define __DEF_H__
#define CMD_NAME "ntpclient" //ntp时间从年开始,本地时间从年开始,这是两者之间的差值 #define JAN_1970 0x83aa7e80 //3600s*24h*(365days*70years+17days) //x*10^(-6)*2^32 微妙数转 NtpTime 结构的 fraction 部分 #define NTPFRAC(x) (4294 * (x) + ((1981 * (x)) >> 11)) //NTPFRAC的逆运算 #define USEC(x) (((x) >> 12) - 759 * ((((x) >> 10) + 32768) >> 16))
#define DEF_NTP_SERVER "210.72.145.44" //国家授时中心 ip //#define DEF_NTP_SERVER "stdtime.gov.hk" //香港标准时间 //#define DEF_NTP_SERVER "pool.ntp.org" //ntp官方时间 #define DEF_NTP_PORT 123 #define DEF_PSEC 10 #define DEF_PMIN 0 #define DEF_PHOUR 0 #define DEF_TIMEOUT 10 #define DEF_LOGEN 1 #define DEF_LOGPATH "/tmp/" CMD_NAME ".log"
#define CONF_PATH "/etc/" CMD_NAME "/" CMD_NAME ".conf" #define DAE_PID_PATH "/var/run/" CMD_NAME ".pid" #define INIT_PATH "/etc/init.d/" CMD_NAME
//ntp时间戳结构 typedef struct { unsigned int integer; unsigned int fraction; } NtpTime;
//校准信息结构 typedef struct { struct timeval dlytime; struct timeval offtime;
|