免费注册 查看新帖 |

Chinaunix

  平台 论坛 博客 文库
12下一页
最近访问板块 发新帖
查看: 4413 | 回复: 15
打印 上一主题 下一主题

在Perl Socket代理中长连接心跳与重联接问题。 [复制链接]

论坛徽章:
0
跳转到指定楼层
1 [收藏(0)] [报告]
发表于 2014-08-14 23:21 |只看该作者 |倒序浏览
本帖最后由 yearn4fd 于 2014-08-18 12:50 编辑

学perl有一段时间了,最近用perl做一个应用,遇难题求解答,谢谢。
情况大致是这样:远程有一业务服务器(移动网络的交换机),
我们可以 raw socket 接入。发起移动业务指令(MML人机对话指令)之前需要进行鉴权,
鉴权时间开销不少,有1秒多一点,业务指令执行挺快。应用主要是采集远程交换机上的数据(查询)。
原来采用短连接模式,每次都要鉴权,效率低下。我想将之改为长联接方式,但改造过程遇到一些难题。

新的应用先建立一个与远程服务器连接的socket 句柄,然后又在本机上创建自已的socket服务器,对外提供最多10路同时接入。
说穿了,它就是一个socket代理服务器。如下图。

移动交换机远程socket接入服务器 <--------------长连接————————[代理socket服器 listen accept fork]〈〈〈〈〈〈〈〈〈〈
注:fork出来的子进程可接入多路源端。
接入交换机我写成独立的package,主要处理连接及鉴权,package对外提供子例程。
在主程序中use package_mobile 之后再调用 package_mobile::f_mobile($SOCK,$error)便可得到已完成鉴权的socket 句柄。

代理socket服务器是阻塞方式实现(对方要求最好是阻塞式)。
在代理socket服务器中,fork 之后需要对package_mobile::f_mobile($SOCK,$error)生成的$SOCK进行操作,操作时都使用flock锁定。
且在子进程中操作$SOCK时使用了eval保护。程序写好后一次运行成功,比之以前效率高多了。但过不了多久,问题来了。
1、对方服务器在我发起的长连接空闲一段时间后(没有需求接入),会自动中断我的创建的$SOCK,我想检测中断前的通知,但没有检测到。
也就是说,空闲一段后接入到代理服务器发起对$SOCK的操作均失败。我试过$SOCK->sockopt(SO_KEEPALIVE,1); 但没起作用
(可能对方不支持吧)。我想到周期性发起心跳指令,但除非再加一层fork,否则在阻塞式的代理服务器代码里,我真想不出如何找个地方
周期性发起心跳。大家帮忙想下,怎么加。再加层fork的话,相当于父进程周期性发心跳,子进程监听,孙进程服务于10路接入。

2、在不改为两次调用fork的前提下,我想到信号,在主程序内设了$SIG{USR1},并在子进程中操作$SOCK时设置了 超时处理:
local $SIG{ALRM} = sub { print "Timeout\n";die "MYTimeout\n";};
在设定时间内出错或未完成时,子进程向父进程发送USR1信号,USR1信号处理函数中重新调作 package_mobile::f_mobile($SOCK,$error),
我的想法无非是重新创建连接远程服务器的socket句柄,但很奇怪,客户端新发起对$SOCK的操作还是失败的。
难道是我的想法根本就不可能实现?

论坛徽章:
0
2 [报告]
发表于 2014-08-14 23:27 |只看该作者
现在我的解决方法很笨:在主机crontab中运行代理服务器客户端的程序,周期性发心跳指令,很笨,很管用,太难看了。

论坛徽章:
0
3 [报告]
发表于 2014-08-15 01:03 |只看该作者
两次fork,解决了心跳信号发送的问题。但$SOCK失效通过子进程发USER1信息通知父进程在信号函数内重新连接的方法还是失败。求解!

论坛徽章:
0
4 [报告]
发表于 2014-08-15 22:36 |只看该作者
本帖最后由 yearn4fd 于 2014-08-16 11:02 编辑

昨晚弄太晚,自已晕菜了。其实 $SOCK 在父进程及子进程中已是两码事了。父进程重连仅是父进程有效,对于子进程还是失效了的,反之亦然。
当然了,在父进程中重连,然后fork出来的子进程又会重新生效。需要注意一点,如果在信号函数中实施重连,这个时候子进程正好在操作$SOCK,
那肯定会报Alarm clok错误,然后整个程序退出。
请教一下经验丰富的同学,有什么重连办法同时让父子进程中的$SOCK均为生效?

论坛徽章:
42
19周年集字徽章-周
日期:2019-10-14 14:35:31平安夜徽章
日期:2015-12-26 00:06:30数据库技术版块每日发帖之星
日期:2015-12-01 06:20:002015亚冠之首尔
日期:2015-11-04 22:25:43IT运维版块每日发帖之星
日期:2015-08-17 06:20:00寅虎
日期:2014-06-04 16:25:27狮子座
日期:2014-05-12 11:00:00辰龙
日期:2013-12-20 17:07:19射手座
日期:2013-10-24 21:01:23CU十二周年纪念徽章
日期:2013-10-24 15:41:34IT运维版块每日发帖之星
日期:2016-01-27 06:20:0015-16赛季CBA联赛之新疆
日期:2016-06-07 14:10:01
5 [报告]
发表于 2014-08-16 08:56 |只看该作者
在父进程里面加信号,定期向设备发心跳不行?
你的阻塞监听应该是对下游
对上游是个client?

论坛徽章:
0
6 [报告]
发表于 2014-08-16 10:53 |只看该作者
本帖最后由 yearn4fd 于 2014-08-16 10:57 编辑
laputa73 发表于 2014-08-16 08:56
在父进程里面加信号,定期向设备发心跳不行?
你的阻塞监听应该是对下游
对上游是个client?

对上游是个client,对下游是个socket server.
server是阻塞式,
listen(SOCKSERVER,SOMAXCONN)后就进入
无限循环,检查有无accept.有连接激活的话才fork,
没有的话就一直阻塞在accept处。没有地方放置心跳代码段。
发心跳主要是在上游SOCK闲时才需要的,也就是accept一直阻塞时。
我现在是fork 再fork.
把心跳放在父,SERVER放在子和孙。
不知perl有什么办法不需fork两次,谢谢。

论坛徽章:
42
19周年集字徽章-周
日期:2019-10-14 14:35:31平安夜徽章
日期:2015-12-26 00:06:30数据库技术版块每日发帖之星
日期:2015-12-01 06:20:002015亚冠之首尔
日期:2015-11-04 22:25:43IT运维版块每日发帖之星
日期:2015-08-17 06:20:00寅虎
日期:2014-06-04 16:25:27狮子座
日期:2014-05-12 11:00:00辰龙
日期:2013-12-20 17:07:19射手座
日期:2013-10-24 21:01:23CU十二周年纪念徽章
日期:2013-10-24 15:41:34IT运维版块每日发帖之星
日期:2016-01-27 06:20:0015-16赛季CBA联赛之新疆
日期:2016-06-07 14:10:01
7 [报告]
发表于 2014-08-16 11:23 |只看该作者
父进程加alarm不行?

论坛徽章:
0
8 [报告]
发表于 2014-08-16 12:19 |只看该作者
本帖最后由 yearn4fd 于 2014-08-16 12:46 编辑
laputa73 发表于 2014-08-16 11:23
父进程加alarm不行?


加Alarm,只能加在阻塞的accept之前,perl的Alarm遇到系统调用时要local化,有时有点问题(我的perl用得半生不熟),
而且有一点不得不提,第一次Alarm之后如果一直无客户操作,accept一直就阻塞,难道要在SIG{Alarm}再次调用Alarm,
否则也就是只能发一次心跳了。
能给个例子不?谢谢。
&get_mobile_socket ($SOCK,$error); ##here create   socket  for the remote mobile switch
##SOCKC for agent socket server
bind(SOCKC,$my_addr)    or die "bind() failed: $!";
listen(SOCKC,SOMAXCONN) or die "listen() failed: $!";
print "Starting   Agent Server On Port: $portc..\n";
while (1)
{
   
         next unless my $remote_addr = accept(SESSION_IN,SOCKC); #没有接入时阻塞在这儿。。。。。
         defined(my $pid=fork) or die "Can't Fork: $!\n";
          if($pid==0) {

论坛徽章:
0
9 [报告]
发表于 2014-08-16 13:11 |只看该作者
本帖最后由 yearn4fd 于 2014-08-16 13:21 编辑

perl还真可以这么做
$SIG{ALRM}=sub{print "定时到达\n";alarm 5;};
也就是说,自激振荡。
可是下面的代码段执行结果有些不理解。
$SIG{ALRM}=sub{print "定时到达\n";alarm 5;};
&get_mobile_socket ($SOCK,$error); ##here create   socket  for the remote mobile switch
##SOCKC for agent socket server
bind(SOCKC,$my_addr)    or die "bind() failed: $!";
listen(SOCKC,SOMAXCONN) or die "listen() failed: $!";
print "Starting   Agent Server On Port: $portc..\n";
alarm 5;
while (1)
{
        print "echo loop\n";
         next unless my $remote_addr = accept(SESSION_IN,SOCKC); #没有接入时阻塞在这儿。。。。。
         defined(my $pid=fork) or die "Can't Fork: $!\n";
          if($pid==0) {

结果是在没有客户接入时
while(1) 里的 print "echo loop\n";每次alarm信号到达时,它都被周期性执行了。不解。

论坛徽章:
42
19周年集字徽章-周
日期:2019-10-14 14:35:31平安夜徽章
日期:2015-12-26 00:06:30数据库技术版块每日发帖之星
日期:2015-12-01 06:20:002015亚冠之首尔
日期:2015-11-04 22:25:43IT运维版块每日发帖之星
日期:2015-08-17 06:20:00寅虎
日期:2014-06-04 16:25:27狮子座
日期:2014-05-12 11:00:00辰龙
日期:2013-12-20 17:07:19射手座
日期:2013-10-24 21:01:23CU十二周年纪念徽章
日期:2013-10-24 15:41:34IT运维版块每日发帖之星
日期:2016-01-27 06:20:0015-16赛季CBA联赛之新疆
日期:2016-06-07 14:10:01
10 [报告]
发表于 2014-08-16 14:06 |只看该作者
alarm要结合eval用的吧,不然SIG执行完之后的再入点可能会有问题
perldoc上的例子是这样的
  1.     eval {
  2.         local $SIG{ALRM} = sub { die "alarm\n" }; # NB: \n required
  3.         alarm $timeout;
  4.         $nread = sysread SOCKET, $buffer, $size;
  5.         alarm 0;
  6.     };
  7.     if ($@) {
  8.         die unless $@ eq "alarm\n";   # propagate unexpected errors
  9.             # timed out
  10.     }
  11.     else {
  12.             # didn't
  13.     }
复制代码
您需要登录后才可以回帖 登录 | 注册

本版积分规则 发表回复

  

北京盛拓优讯信息技术有限公司. 版权所有 京ICP备16024965号-6 北京市公安局海淀分局网监中心备案编号:11010802020122 niuxiaotong@pcpop.com 17352615567
未成年举报专区
中国互联网协会会员  联系我们:huangweiwei@itpub.net
感谢所有关心和支持过ChinaUnix的朋友们 转载本站内容请注明原作者名及出处

清除 Cookies - ChinaUnix - Archiver - WAP - TOP