分类 技术 Technology 下的文章

一直以来,公司某游戏平台使用 SQL Server 作为数据存储解决方案。为了数据的安全,每天凌晨在本机上做一次备份。但随着时间的推移,原先并没有设计到备份需求的硬件配置,尤其是外部存储这块,已经快不能满足 SQL Server 备份文件日益丰满的身姿。(这句话好像谁说过?)

为了彻底解决这个病痛,我打算为其实施远程异地备份。但查阅无数资料,均找不到较好的解决之道。大部分方案都是在备份目的地建共享目录,新增一个用户并赋权。然后在数据库端写个存储过程,添加数据库维护计划并使用这个存储过程。这样做有太多弊端:对于操作系统来说,很不安全;对于硬件架构设计来说,必须满足同一内网的条件。于是乎,放弃这样的方案。

经过几天的折腾测试,最后我采用了这样的方案。虽然比较老土,但至少 DIY 出来了,也暂时性满足了需求,缓解了阶级矛盾:

1、打开 SQL Server 企业管理器,找到数据库维护计划。

2、添加一个数据库维护计划,为其设置一个优美而和谐的名字,并选择需要操作的数据库对象。

- 阅读剩余部分 -

昨晚,是一个愉快的、有意义的、充实的夜晚;昨晚,对于我的一生来说都是十分重要的一晚;昨晚,虽然是夜晚,但却是明媚、阳光的一晚。这一切,都是因为有了你,feelinglucky

lucky 兄已在 gracecode.com 上面概述了昨夜我们之间发生的故事,这里做一个全面的流水记录。让花花草草都来羡慕我们这天上的一对、地上的一双吧。

昨晚接到一个工单,要给一个客户对帐。我和同事 Henry Xu 分别在 MySQL 和 SQL Server 做了一次查询,结果显示的时间正好相差八小时。Henry 说:“肯定是你 MySQL 的时区有问题,慢了八个小时”。我看看是啊,想起 lucky 的一篇文章,于是到 phpmyadmin 中查询:

SELECT UNIX_TIMESTAMP(); 结果显示的时间戳,通过 PHP 的 date 函数转换出来,确实与那时的北京时间相差八个小时。我继续在 phpmyadmin 中查询:

SELECT NOW(); 这次结果居然是正确的。暂时糊涂了,看看时间,北京时间21点多,一通电话直接找到 lucky。这里插播一则广告,本人专业出售 lucky 手机号码,有需要的 MM 请直接在下面留言,谢谢。

我们一边谈一边查资料,后来在 MySQL 手册上看到:UNIX_TIMESTAMP() 是返回世界协调时 UTC 的时间,不参照本机系统时区。网上也有一种说法:使用 UNIX_TIMESTAMP() 函数是获取不到正确的当地时间的时间戳,除非当前系统时区就在 0 时区。后来 lucky 提示使用 CURRENT_TIMESTAMP() 代替,但是由于我目前的数据库设计习惯,暂时不能接收非时间戳的值。

后面 lucky 就在说服我使用 datetime 的数据库类型。我一边写了一个小脚本:

<?php
$a 
0000000000//从 phpmyadmin 中查询 SELECT UNIX_TIMESTAMP(); 后复制过来的 10 位数字
$b time();
echo 
date('Y-m-d H:i:s'$a) . "<br />" date('Y-m-d H:i:s'$b);
?>
并把这个脚本放到服务器上跑了一下,结果发现两行输出居然是一样的。我纳闷,然后将脚本复制到 Zend Studio 里面,F5…… My god,原来是 Zend Studio 的问题……

下面复制一些与 lucky 的交流记录,谨作这一夜的纪念。

feelinglucky: http://www.modwest.com/help/kb6-256.html
feelinglucky: -_- 我搜索 “mysql 时区”竟然都是我的文章……
wiLdGoose: 是的
feelinglucky: http://dev.mysql.com/doc/refman/5.0/en/date-and-time-functions.html
NOW 是正确的是吧?
用 CURRENT_TIMESTAMP(), CURRENT_TIMESTAMP 看看
Synonyms for NOW()
wiLdGoose: ..........
feelinglucky: CURRENT_TIMESTAMP and CURRENT_TIMESTAMP() are synonyms for NOW().
feelinglucky: http://dev.mysql.com/doc/refman/5.0/en/date-and-time-functions.html#function_current-date
http://dev.mysql.com/doc/refman/5.0/en/date-and-time-functions.html#function_current-timestamp
wiLdGoose: 我知道的
他和 NOW() 一样的
我不要字符串的
我要整数
feelinglucky: Returns the current date and time as a value in 'YYYY-MM-DD HH:MM:SS' or YYYYMMDDHHMMSS.uuuuuu format, depending on whether the function is used in a string or numeric context. The value is expressed in the current time zone.
这个是 NOW() 的,注意最后一句
wiLdGoose: yep
NOW 是对的啊
那 UNIX_TIMESTAMP() 就乱来了?
不根据当前时区了?
feelinglucky: UTC_TIMESTAMP()(v4.1.1) Return the current UTC date and time
是 return UTC 时间
当前 UTC 时间
feelinglucky: 应该用 UNIX_TIMESTAMP
wiLdGoose: http://www.linuxrpm.com/forums/viewtopic.php?t=232
看这个
他的意思是放弃 unix_timestamp
feelinglucky: 这种说法是不对的
wiLdGoose: ?
feelinglucky: 返回 UTC 时间是有用处的,不能更改的
打个比方,两台服务器之间如果跨时区的话,时间保证会统一到 UTC 时间的
CCTV 不是经常说的嘛“格林威治时间 XXX”嫦娥一号升空
wiLdGoose: 是不是 unix_timestamp() 返回的时间和 now() 就是不一样的
先不论格式
feelinglucky: 用 CURRENT_TIMESTAMP 不行吗
wiLdGoose: 那还是一样要改代码
feelinglucky: 至少可以确定它是根据时区选项来的
wiLdGoose: 再说 CURRENT_TIMESTAMP 返回的是 datetime 格式
feelinglucky: :( 搞不懂为什么一定要时间戳
wiLdGoose: i like it
:D
feelinglucky: 你不会吧数据库都用时间戳设计的吧。。。
wiLdGoose: 是的
feelinglucky: 然后用 INT(10) ?
wiLdGoose: 是啊
你喜欢 datetime?
feelinglucky: 天啊
:(
wiLdGoose: 来吧 说服我
我听着
feelinglucky: 这样快不了多少的
直观方面先不说,这个都清楚
然后就是计算问题
比如获得最近一周的所有数据,你怎么办?
在一个表里面
wiLdGoose: 你是不是想说, 用 datetime 类型的数据, 可以直接用mysql的函数比较日期
feelinglucky: 或者获得星期一我要的所有打卡记录
wiLdGoose: 时间戳也有时间戳的好处嘛
datetime的话, 遇到特殊情况, 还是要 strtotime
多不完美
feelinglucky: 如果按照你的,保证会有一个 xxxx < monday and xxxx > monday 这样的 WHERE 条件
想象一下,这个不是日期的比较哦,是整形对整形的比较
wiLdGoose: 是的
feelinglucky: PHP 方面你首先要知道星期一的时间戳,然后再让数据库 WHERE 计算
wiLdGoose: 数字与数字比较, 不是比字符串与字符串比较方便么?
feelinglucky: 相比 MYSQL 一条语句就 OK,哪个完美呢
时间类型不是字符串类型 :(
时间对于用户来看很直观,会“误认为”是字符串,但是在 mysql 内部存储还是长整型的
feelinglucky: 这个亲爱的可以 Google 一下
我以前也这样认为的,和你的做法一样,后来看了 wordpress 的数据库设计方面的一篇文章,谈到日期的数据库设计,才转变回观念来的
退一步讲,就算时间计算比整形的要低,但是毕竟用这个能方便很多
wiLdGoose: 我空了去研究一下, 先去改代码了
feelinglucky: OK,长篇大论留到 Blog 让亲爱的欣赏 :D
wiLdGoose: 好的.
Thx.
wiLdGoose: 我晕死了
feelinglucky: ?
wiLdGoose: 我发现我用php写一个 echo time() 输出都错的
但是php配置文件已经修改的了啊.
[Date]
; Defines the default timezone used by the date functions
date.timezone = PRC
feelinglucky: ?!
ft
wiLdGoose: 我吐血了, 刚才写了一个 test.php
你看看 www.xxxx.com/test.php
feelinglucky: 对的啊
wiLdGoose: 我被忽悠了
feelinglucky: ?
wiLdGoose: 我电话你, ok?
feelinglucky: ok

关于坚持使用时间戳还是转投 datetime 的环抱,我目前还没有想好。花点时间研究一下吧。

另外有一则好消息吧,Perl 5.10.0 终于释出了。

因为某种需要,也不得不试着了解一下未知领域。首先介绍两个术语:

IVR:交互式语音应答模块,解释和运行应用业务流程的软硬件。
SP:业务提供商。

一、概述

1、功能简介

总的来说,通用网关是IVR和SP应用服务器之间进行消息交互的中间协议转换程序。通用网关的位置如下图所示:

IVR和通用网关之间采用内部协议,通用网关和外部SP应用服务程序之间采用TCP/IP协议,通用网关作为客户端,SP应用服务程序作为服务器端,可以采用长接连或者短连接两个连接方式。

对于一个通用网关,可以连接若干个SP的应用服务器,每个连接可以采用不同的连接方式和连接协议。

为了实现冗余备份和负载均衡,对于一个SP服务器,有时采用两个通用网关作为客户端与之进行连接。一般的,一个SP所有的业务交互接口都在一台应用服务器上。

按照TCP协议长连接和短连接的连接方式,以及业务数据否需要加密等业务需求,通用网关对外提供了四种方式的连接协议,分别是:短连接不加密的字符串传输方式,短连接根据密钥加密的传输方式,长连接不加密的字符串传输方式,以及长连接根据密钥加密传输协议。

下面根据一个业务流程的例子,对相关的IVR业务过程和接口协议的四种方式的做一个详细的描述。

2、业务举例

考虑如下一个IVR节目流程,该节目在增值声讯业务平台上,利用电信运营商代收费方式,为电话用户进行SP有关业务的帐号的充值。

假设该节目的接入号为12345,一个主叫为057188880000的用户,拨打该节目号,进入语音IVR流程;在流程中,根据IVR的语音提示,用户输入自己的在SP处的充值帐号 10001,以及要充值的金额10元,这10元的消费费用将由电信运营商在计费月末向当前主叫话机057188880000的帐户进行收费。IVR流程收集完相关的信息后,首先会让用户再次确认,然后将这些信息组合为一个字符串,各信息字段之间用一个分隔符隔开,如$等,譬如约定生成的字符串格式为“操作代码$主叫号码$被叫$充值帐号$充值金额(分)$时间戳$”,那么这里组合成的字符串最后就是“10$057188880000$12345$10001$10000$20071115165500$”,一共是50个字符。

这里需要说明的是,IVR业务流程能够发送、接收和处理的数据信息,就是这些文本字符串,IVR能对这些字符串进行解析:如分解、组合和判断,但不能处理其他如二进制字节流等方式的数据结构。

至于如何组织这些具体信息字段,字段间究竟采用什么字符进行分隔,字符串定长还是不定长,这些属于业务流程的具体应用协议,是由SP流程开发人员和SP应用服务程序开发人员之间进行协商约定的。同样,对最终返回给IVR的结果信息字符串,也是由他们进行协商约定的。

通用网关是不关心这些业务格式信息的,通用网关和SP应用服务程序之间,需要约定的是通信连接方式,字符串是否加密,如何完整的发送和接收每次交互的信息包等。

二、协议描述

通用网关对外提供了四种方式的连接协议,分别是:短连接不加密的字符串传输方式,短连接根据密钥加密的传输方式,长连接不加密的字符串传输方式,以及长连接根据密钥加密传输协议。

1、直接传输字符串的包格式

对于不加密的字符串短连接传输协议方式,由于传输的数据以“\0”为结束符,控制简单,因此数据是直接传输的,传输的包为以”\0”结尾的字符串数据。

2、需要包头信息的包格式

对于其他三种方式,包括根据密钥加密的短连接传输协议、不加密的字符串长连接传输协议、根据密钥加密的长连接传输协议,都需要在数据之前加上包头通信控制信息。

数据包格式如下图所示,所包含的内容从左到右进行传输:

这里值得注意的是包头中的信息全部为网络字节顺序。

包头的各字段说明如下:

包头信息中,除了包体长度(length)以外,其他的字段,包括开始标识(head),版本信息(version),任务流水号(taskid),发送方标识1(sender),发送方标识2(sessionid),时间戳(timestamp),以及数据加密标识(flag),需要在响应包中原样返回;否则通用网关接收返回消息后,可能解析出错,最终导致IVR业务处理错误。

再次说明:包头中的字段信息全部为网络字节顺序;短连接不加密方式不需要包头信息。

3、不加密字符串的短连接传输协议

这种方式并发性好,开发实现起来简单,程序配置方便,容易排除错误,适合于安全性要求不高,或要求高安全性但通过专线网络连接的业务;除了在公网上开展小额支付的业务以外,其他的业务都建议采用这种方式。

对于这种方式,系统消息交互的过程,大概分如下几个步骤,见下图所示:

(1)、IVR流程中收集完用户的相关信息之后,发送一个字符串给通用网关,在上述的那个例子中,这个字符串是” 10$057188880000$12345$10001$1000$20071115165500$”;

(2)、通用网关收到IVR流程业务应用请求之后,向SP服务器程序启动一个连接(Connect)请求;

(3)、SP应用服务程序此时应处于监听(Listen)状态,监听到有客户端请求连接;

(4)、SP应用服务程序根据此连接请求的IP地址等信息进行身份验证,如果验证通过则接受(Accept)连接请求,双方建立一个Socket连接;

(5)、通用网关在连接建立之后,立即开始发送(Send)刚才从IVR处接收到字符串数据(包括最后的’\0’),发送的字节数为字符串自身的长度加1,即strlen(发送字符串)+1;在上述的例子中,这里是strlen(“10$057188880000$12345$10001$1000$20071115165500$”)+1=51;

(6)、SP应用服务程序接收(Recv)该业务字符串,然后根据流程开发人员与之协商好的格式对接收的字符串进行解析,然后进行响应的处理;譬如例子中,分解出操作码10为充值操作,分解主叫为057188880000,帐号为10001,充值金额为10元等,然后查询该帐号是否存在和帐号状态是否合法,如果满足条件后,对该卡进行充值等操作;

(7)、SP应用服务程序处理对应的业务后,把处理后的结果数据进行加工,组合成字符串,发送(Send)给通用网关,同样的,该字符串格式是事先和流程开发人员进行协商确定的;如在例子中,充值成功后,返回”11$2$10001$1000$”的字符串,表示充值10元失败,11是充值操作响应码,2表示卡号不存在,10001表示卡号,1000表示刚才要充值的金额,以分为单位;

(8)、通用网关接收到(Recv)返回业务数据后,主动关闭(Close)和SP服务器的这次Socket连接,将返回的字符串信息原封不动的发送给IVR;

(9)、IVR收到该返回字符串” 11$2$10001$1000$”,然后根据业务流程对之进行解析,做进一步的处理,譬如提示用户说,该帐户不存在等;

(10)、如果SP应用服务程序在5秒钟中时间内,没有返回处理结果,通用网关会自动断开该Socket连接,然后向IVR报告处理超时消息。

4、根据密钥加密的短连接传输协议

这种方式并发性好,安全性高,但实现起来相对较难,联调出现错误不容易排除。在公网上实现的并发性高的电子商务类交互业务,建议采用这种方式。

本方式采用DES加密,双方需要约定一个密钥(Key),目前约定密钥为长度额为8位的字符串,一般由SP服务器端来指定,提供给通用网关的维护人员,由维护人员在本地对通用网关进行配置。

在发送数据给对方之前,通信的双方根据本地配置的密钥对数据进行DES加密处理;在收到数据后,同样根据本地配置的密匙进行解密处理;通信连接方式上和上述第一种完全一样。

具体过程详细说明如下,如图所示:

(1)、IVR流程中收集完用户的相关信息之后,发送一个字符串给通用网关;在上述的那个例子中,这个字符串是”10$057188880000$12345$10001$1000$20071115165500$”;

(2)、通用网关收到IVR流程业务应用请求之后,根据配置的8位密钥(这里假设为” SuntekD6”)进行加密处理,得到加密后的字节流,假设为32f69fce6dca6ace576b08115c099975817e2b3b68f9eafd6ca55dfec83816739b7152375
e09872b31acb888b4f4b83e6aee7d1bb8c5c41a39(57字节的二进制字节流)存储在本地;

(3)、通用网关向SP服务器程序启动一个连接(Connect)请求;

(4)、SP应用服务程序此时应处于监听(Listen)状态,监听到有客户端请求连接;

(5)、SP应用服务程序根据此连接请求的IP地址等信息进行身份验证,如果验证通过则接受(Accept)连接请求,双方建立一个Socket连接;

(6)、通用网关在连接建立之后,根据刚才加密的字节流,加上信息包头,组合成信息包,然后发送(Send)信息包,发送的字节数为加密后的字节流字节数+24(24信息包头的字节长度);在上述的例子中,信息包大约如ffff0200 00001100 00004e5f 00114b3f 00000000 0100 0038 2fd65329e2770074791503c2dab1d209fe0225636dd07f7277bd291209e1a498df28f7af2
f0eb4f1be2831f0fa8987b7f9;其中业务加密字节流的长度这里是56,体现在网络字节中是0x0038,如果是x86系列的PC,需要做主机到网络(ntohs)字节顺序转换;

(7)、SP应用服务程序接收(Recv)该信息包,根据包头信息截取对应的字节流,然后进行解密处理,得到加密前的业务字符串,另外还需要把其他的包头标识、两个包头保留字,存储在本地;

(8)、SP应用服务程序根据流程开发人员与之协商好的格式对接收的字符串进行解析,然后进行响应的处理;譬如例子中,分解出操作码10为充值操作,分解主叫为057188880000,帐号为10001,充值金额为10元等,然后查询该帐号是否存在和帐号状态是否合法,如果满足条件后,对该卡进行充值等操作;

(9)、SP应用服务程序处理对应的业务后,把处理后的结果数据进行加工,组合成字符串,同样,该字符串格式是事先和流程开发人员进行协商确定的;如在例子中,充值成功后,返回”11$2$10001$1000$”的字符串,表示充值10元失败,11是充值操作响应码,2表示卡号不存在,10001表示卡号,1000表示刚才要充值的金额,以分为单位;

(10)、SP应用服务程序在发送之前,同样根据密匙对该返回字符串进行加密处理,然后对加密字节流长度进行计算,加上刚才发送过来的包头标识和版本号以及保留字等,组合成信息包;注意这时,字节流的长度是返回加密字节流的长度,而且是网络字节的;

(11)、通用网关接收到(Recv)返回业务数据后,主动关闭(Close)和SP服务器的这次Socket连接,将返回的加密字节流进行解密处理之后,还原原有的信息字符串,发送给IVR;

(12)、IVR收到该返回字符串” 11$2$10001$1000$”,然后根据业务流程对之进行解析,做进一步的处理,譬如提示用户说,该帐户不存在等;

(13)、如果SP应用服务程序在5秒钟中时间内,没有返回处理结果,通用网关会自动断开该Socket连接,然后向IVR报告处理超时消息。

5、不加密的字符串长连接传输协议

这种方式可靠性高,能够保持持续的连接,但对于资源的消耗比较高,并发性不太好,调试跟踪不太方便,一般不建议采用这种方式。

具体过程详细说明如下,如图所示:

(1)、通用网关启动服务模块时向SP服务器程序启动一个连接(Connect)请求;

(2)、SP应用服务程序此时应处于监听(Listen)状态,监听到有客户端请求连接;

(3)、SP应用服务程序根据此连接请求的IP地址等信息进行身份验证,如果验证通过则接受(Accept)连接请求,双方建立一个Socket连接;

(4)、IVR流程中收集完用户的相关信息之后,发送一个字符串给通用网关;在上述的那个例子中,这个字符串是”10$057188880000$12345$10001$1000$20071115165500$”;

(5)、通用网关根据IVR流程发送的字节流,加上信息包头,组合成信息包,然后发送(Send)信息包,发送的字节数为加密后的字节流字节数+24(24信息包头的字节长度);在上述的例子中,信息包大约如ffff0200 00001100 00004e5f 00114b3f 00000000 0100 0033 10$057188880000$12345$10001$1000$20071115165500$;发送的字节数为字符串自身的长度加1,即strlen(发送字符串)+1;在上述的例子中,这里是strlen(“10$057188880000$12345$10001$1000$20071115165500$”)+1=51,即0X33;

(6)、SP应用服务程序接收(Recv)该信息包,根据包头信息截取对应的字节流,然后得到业务字符串,另外还需要把其他的包头标识、两个包头保留字,存储在本地;

(7)、SP应用服务程序根据流程开发人员与之协商好的格式对接收的字符串进行解析,然后进行响应的处理;譬如例子中,分解出操作码10为充值操作,分解主叫为057188880000,帐号为10001,充值金额为10元等,然后查询该帐号是否存在和帐号状态是否合法,如果满足条件后,对该卡进行充值等操作;

(8)、SP应用服务程序处理对应的业务后,把处理后的结果数据进行加工,组合成字符串,同样,该字符串格式是事先和流程开发人员进行协商确定的;如在例子中,充值成功后,返回”11$2$10001$1000$”的字符串,表示充值10元失败,11是充值操作响应码,2表示卡号不存在,10001表示卡号,1000表示刚才要充值的金额,以分为单位;

(9)、SP应用服务程序然后对字节流长度进行计算,加上刚才发送过来的包头标识和版本号以及保留字等,组合成信息包;注意这时,字节流的长度是是网络字节的;

(10)、通用网关接收到(Recv)返回业务数据后,将返回的字节流发送给IVR;

(11)、IVR收到该返回字符串” 11$2$10001$1000$”,然后根据业务流程对之进行解析,做进一步的处理,譬如提示用户说,该帐户不存在等;

(12)、如果SP应用服务程序在5秒钟中时间内,没有返回处理结果,通用网关会向IVR报告处理超时消息;

(13)、通用网关服务模块退出或停止的时候断开和SP应用服务器的连接。

6、根据密钥加密的长连接传输协议

这种方式可靠性高,安全性高,能够保持持续的连接,但对于资源的消耗比较高,并发性不太好,调试跟踪不太方便,一般不建议采用这种方式。

本方式采用DES加密,双方需要约定一个密钥(Key),目前约定密钥为长度额为8位的字符串,一般由SP服务器端来指定,提供给通用网关的维护人员,由维护人员在本地对通用网关进行配置。

在发送数据给对方之前,通信的双方根据本地配置的密钥对数据进行DES加密处理;在收到数据后,同样根据本地配置的密匙进行解密处理;通信连接方式上和上述第三种完全一样。

具体过程详细说明如下,如图所示:

(1)、通用网关启动服务模块时向SP服务器程序启动一个连接(Connect)请求;

(2)、SP应用服务程序此时应处于监听(Listen)状态,监听到有客户端请求连接;

(3)、SP应用服务程序根据此连接请求的IP地址等信息进行身份验证,如果验证通过则接受(Accept)连接请求,双方建立一个Socket连接;

(4)、IVR流程中收集完用户的相关信息之后,发送一个字符串给通用网关;在上述的那个例子中,这个字符串是”10$057188880000$12345$10001$1000$20071115165500$”;

(5)、通用网关收到IVR流程业务应用请求之后,根据配置的8位密钥(这里假设为” SuntekD6”)进行加密处理,得到加密后的字节流,假设为32f69fce6dca6ace576b08115c099975817e2b3b68f9eafd6ca55dfe(56字节的二进制字节流)存储在本地;

(6)、通用网关在连接建立之后,根据刚才加密的字节流,加上信息包头,组合成信息包,然后发送(Send)信息包,发送的字节数为加密后的字节流字节数+24(24信息包头的字节长度);在上述的例子中,信息包大约如ffff0200 00001100 00004e5f 00114b3f 00000000 0100 0038 2fd65329e2770074791503c2dab1d209fe0225636dd07f7277bd291209e1a498df28f7af
2f0eb4f1be2831f0fa8987b7f9;其中业务加密字节流的长度这里是56,体现在网络字节中是0x0038,如果是x86系列的PC,需要做主机到网络(ntohs)字节顺序转换;

(7)、SP应用服务程序接收(Recv)该信息包,根据包头信息截取对应的字节流,然后进行解密处理,得到加密前的业务字符串,另外还需要把其他的包头标识、两个包头保留字,存储在本地;

(8)、SP应用服务程序根据流程开发人员与之协商好的格式对接收的字符串进行解析,然后进行响应的处理;譬如例子中,分解出操作码10为充值操作,分解主叫为057188880000,帐号为10001,充值金额为10元等,然后查询该帐号是否存在和帐号状态是否合法,如果满足条件后,对该卡进行充值等操作;

(9)、SP应用服务程序处理对应的业务后,把处理后的结果数据进行加工,组合成字符串,同样,该字符串格式是事先和流程开发人员进行协商确定的;如在例子中,充值成功后,返回”11$2$10001$1000$”的字符串,表示充值10元失败,11是充值操作响应码,2表示卡号不存在,10001表示卡号,1000表示刚才要充值的金额,以分为单位;

(10)、SP应用服务程序在发送之前,同样根据密匙对该返回字符串进行加密处理,然后对加密字节流长度进行计算,加上刚才发送过来的包头标识和版本号以及保留字等,组合成信息包;注意这时,字节流的长度是返回加密字节流的长度,而且是网络字节的;

(11)、通用网关接收到(Recv)返回业务数据后,主动关闭(Close)和SP服务器的这次Socket连接,将返回的加密字节流进行解密处理之后,还原原有的信息字符串,发送给IVR;

(12)、IVR收到该返回字符串” 11$2$10001$1000$”,然后根据业务流程对之进行解析,做进一步的处理,譬如提示用户说,该帐户不存在等;

(13)、如果SP应用服务程序在5秒钟中时间内,没有返回处理结果,通用网关会向IVR报告处理超时消息。

(14)、通用网关服务模块退出或停止的时候断开和SP应用服务器的连接。

今天在一台几乎废弃的 freebsd 服务器上折腾,删除了里面的 postfix 邮件系统。折腾完毕修改了 /etc/rc.conf,去掉了 hostname 键,reboot 之后发现 apache 没有自动启动,手动启动也失败。通过 apachectl configtest 检查结果是 Syntax OK,于是去找 /var/log/http_error.log,错误日志如下:
[Sat Dec 15 00:17:43 2007] [alert] (EAI 8)hostname nor servname provided, or not known: mod_unique_id: unable to find IPv4 address of ""
Configuration Failed

查了资料,修改 httpd.conf,将下面一行注释掉就可以了:
LoadModule unique_id_module libexec/apache2/mod_unique_id.so

在 apache 手册上查到这个 unique_id_module 模块的概述:
This module provides a magic token for each request which is guaranteed to be unique across "all" requests under very specific conditions. The unique identifier is even unique across multiple machines in a properly configured cluster of machines. The environment variable UNIQUE_ID is set to the identifier for each request. Unique identifiers are useful for various reasons which are beyond the scope of this document.

大概意思就是给每个请求提供一个唯一标识的环境变量,具体说明在这里

再次启动 apache 还发现一个报错:
No such file or directory: Failed to enable the 'httpready' Accept Filter

曾经在某网的一篇文章上看过,google 找到相关资料,说这貌似是 freebsd 与 apache2 之间的一个小 bug。解决方案如下:
# kldload accf_http
# vi /boot/defaults/loader.conf

找到下面这行:
accf_http_load = "NO"

将 NO 改为 YES 就可以了。

从来没有认真关注过这个时间同步,今天终于在公司的一台 Windows 2003 服务器上领教了。

该服务器运行着 Windows 2003 Enterprise SP2,并涉及精密时间和金融数据处理。照理这宝贝对于时间的精确度应该非常敏感,但由于常年无法与微软的时间同步服务器通讯,使得时间误差越来越大。从上架服务到今天,这宝贝已经工作了将近半年的时间,时间误差已经接近两分钟,到了无可救药的地步了。

这半年以来,公司还未曾与合作单位对过账。今天收到合作单位的账单,整整 241 页。因为第一次对账,出现了不少坏账,需要核对。于是写了个脚本开始对,没想到合作单位的账单上每条记录的时间居然与我们的数据记录不一样,少则相差一分钟,多则相差近两分钟,而且这个差距没有任何规律。

我疯了,终于意识到时间同步是一件多么重要、多么必要、多么和谐、多么健康、多么有益的事情。然后开启 Windows 的时间同步服务开始对时,发现总是报错“不能连接到对等机器”。想到服务器上使用了 TCP/IP 筛选,仅开放了几个少的可怜的 TCP 端口,猜想是不是端口被阻止了。netstat -an 之后看到 UDP 协议上有一个 123 端口监听着公网 IP 地址和 127.0.0.1,查了资料,就是它了。添加一个 UDP 123 的端口后重启,抛弃慢如蜗牛的 time.windows.com,使用 218.75.4.130 作为时间同步服务器,亡羊补牢告成。

我们的服务器在湖州电信机房,所以使用 218.75.4.130 作为时间同步服务器,这个机器在台州电信,速度不错。在电信线路上,222.73.106.220 这个机器的速度也不错,可以用来同步您的服务器时间。