大顽石

凡事太紧,缘分必早尽

  IT博客 :: 首页 :: 联系 :: 聚合  :: 管理
  9 Posts :: 0 Stories :: 14 Comments :: 0 Trackbacks

本文提供的解决办法并没有通过充分验证,也不知道会带来什么后果。如果读者对这个问题有更深的心得,欢迎和我联系。
发现的问题是:
1,dp的server和client建立联系后,当网线断开的时候,client端或server端会出现100%的CPU占用率。Wish似乎挂在那里;
2,dp的server和client建立联系后,当server端的wish用exit退出的时候,client端会出现100%的CPU占用率,Wish似乎给挂在那里;

快速解决:
下载:/Files/bigstone1998/tcldp-win_bugfix.rar
解压包后,用其中的win目录下的dp40.dll替换掉原来dp安装目录下的dp40.dll。如何安装dp,请参看:http://www.cnitblog.com/bigstone1998/archive/2008/08/05/47623.html

代码分析:
问题一:网线断开带来的问题
 根据我跟踪,发现当网线断开时,client端会在dpwinsock.c/sockwatchdpwinsock.c/socketEventProcdpRPC.c/DpReadRPCChanneldpWinTcp.c/TcpInput的函数间来回跳动。从代码来看,在dpRPC.c/DpReadRPCChannel中,有一段:

    numRead = Tcl_Read (rpcChanPtr->chan, scratch, sizeof(scratch));

    if (numRead < 1) {

              if (Tcl_Eof(rpcChanPtr->chan)) {

当从channel读取错误的时候,numRead返回0-1。我调试发现,当网线断开的时候,返回-1。但是在代码中,没有对-1进行处理,而是简单return,这样导致不停进行读取,返回。于是我干脆把(Tcl_Eof(rpcChanPtr->chan))这个判定条件给去掉。这样,当numRead<1的时候,也就是出错的时候,就把channel给删除掉,如下:

              if(1){

/*

                      * End of File on the RPC channel. This will usually happen

                      * if the TCP connection is reset.

                      *

                      * We'll just close the channel quietly.

                      */

 

                     Tcl_Channel chan = rpcChanPtr->chan;

                     Tcl_Interp *interp = rpcChanPtr->interp;

                     DpDeleteRPCChannel(rpcChanPtr->interp, rpcChanPtr);

                     DBG(printf("Closing EOF'd RPC channel"));

                            DpClose(interp, chan);

                     return;

}


问题二:Server退出,对client端的影响
 

这个问题在第一个问题解决后,依然出现。通过我跟踪代码,终于发现当serverexitclient端把channel删除后,还不断有sockwatch被调用,导致不断有Tcl_Event触发,而触发的消息其实只是FD_CLOSEDP中对孤独的FD_CLOSE又没有进行清理。导致这种情况出现,我感觉是FD_CLOSE消息处理不当。当仅有FD_CLOSE消息的时候,就不应该再出发channel的消息ready开关了。

 

于是在dpWinSock.c/SocketProc这个函数中做了如下调整(这个函数其实是Windows体系下的对socket消息的响应函数):

                     infoPtr->readyEvents |= event;

是原函数的内容。这里面的问题就是,当event就是FD_CLOSE的时候,这条语句其实是打开了readyEvents。于是在后面的SockWatch中,由于这个readyEvent不为空,导致了Tcl_SetMaxBlockTime的设置。从而导致了下一步的SocketCheckProc去创建一个Tcl_Event,并放到Tcl_Queue中。于是这个Tcl_Event中的SocketEventProc又会被处理。而在这个函数的处理中,对FD_CLOSE的处理又很奇怪,(我不理解)。反正最后,这个FD_CLOSE不停触发这个过程。最后,我在源头:dpWinSock.c/SocketProc把语句进行了修改,修改为:

              if (!(event & FD_CLOSE)) { // added by GYZ

                     infoPtr->readyEvents |= event;

              }// added by GYZ

也就是说,如果仅仅是FD_CLOSE的话,就不去把它添加到readyEvents中去了。这样原来的问题就消失了。




 

posted on 2008-08-05 10:31 大顽石 阅读(254) 评论(0)  编辑 收藏 引用
只有注册用户登录后才能发表评论。