哎,又来问了,其实我的程序已经基本上好了,但是就剩这个问题了。
暂时用我家的电脑做serversocket端,但是我家的ip过两天一般就会电信给变一下。目前用了一个治标的办法,用花生壳,将客户端连接的socket指定host域名。
但昨天下午出现了这样的事:我的server端程序自己关闭了。为什么我会知道,因为之前就出现过类似问题,所以我写了一个.bat守护,定期检测,检测到服务器进程没了,就再启动服务器,并显示启动的时间。昨天重启的时间是14:39。15点多的时候我自己试用单位的电脑连接了一下我的服务器,是正常的,也是我没去早连接测试,没实时发现这个问题。
由于晚上回去看了一下我电脑的IP,发现变了,所以严重怀疑这次掉线是因为变IP。
查看服务器我自己留存的日志,发现是这样的。掉线前是有一个客户端连接的,客户端的最后一个心跳是在14:33的,5分钟没动静(由于我自己刚刚发现了服务器代码有BUG,忘了关联保活,所以服务器没自动踢掉客户端),14:38才有这个客户端的disconnect消息传来。
假如是在这个时段变IP的,缓冲了5分钟本身可以理解,花生壳更新IP解析需要时间嘛。但这里有若干问题:
1、clientsocket是指定域名连接的,在连接状态下,此域名的IP变了,socket是如何处理的?
2、如果socket在更换IP的瞬间就已经不能连接了,也就是这5分钟是tcp协议的超时时间,5分钟以后才出现disconnect。这里会触发onerror之类的事件吗?哪端触发?因为服务器应该是不知道自己IP变的(路由嘛,只是将本机的对应端口映射到外网IP上去,serversocket总是打开本机的端口,这里没错)
3、既然服务器应该不知道自己IP变,为什么会出现服务器整个程序被关闭的情况呢?有没有办法解决这个问题,依赖守护bat去重启服务器总不是个事吧?
4、理论上是否有能够彻底解决这个问题的方法?就是服务器能在自己的外网IP改变的瞬间,先截获这个事件,在原来的连接断掉之前把新IP广播到在线的客户端,重新建立连接?

解决方案 »

  1.   

    搜到这个了,但是没太看懂。。三. read/write对连接异常的反馈行为:对应用程序来说,与另一进程的TCP通信其实是完全异步的过程:我并不知道对面什么时候、能否收到我的数据我不知道什么时候能够收到对面的数据我不知道什么时候通信结束(主动退出或是异常退出、机器故障、网络故障等等)对于1和2,采用write() -> read() -> write() -> read() ->…的序列,通过blocking read或者nonblock read+轮询的方式,应用程序基于可以保证正确的处理流程。对于3,kernel将这些事件的“通知”通过read/write的结果返回给应用层。假设A机器上的一个进程a正在和B机器上的进程b通信:某一时刻a正阻塞在socket的read调用上(或者在nonblock下轮询socket)当b进程终止时,无论应用程序是否显式关闭了socket(OS会负责在进程结束时关闭所有的文件描述符,对于socket,则会发送一个FIN包到对面)。同步通知:进程a对已经收到FIN的socket调用read,如果已经读完了receive buffer的剩余字节,则会返回EOF:0异步通知:如果进程a正阻塞在read调用上(前面已经提到,此时receive buffer一定为空,因为read在receive buffer有内容时就会返回),则read调用立即返回EOF,进程a被唤醒。socket在收到FIN后,虽然调用read会返回EOF,但进程a依然可以其调用write,因为根据TCP协议,收到对方的FIN包只意味着对方不会再发送任何消息。 在一个双方正常关闭的流程中,收到FIN包的一端将剩余数据发送给对面(通过一次或多次write),然后关闭socket。但是事情远远没有想象中简单。优雅地(gracefully)关闭一个TCP连接,不仅仅需要双方的应用程序遵守约定,中间还不能出任何差错。假如b进程是异常终止的,发送FIN包是OS代劳的,b进程已经不复存在,当机器再次收到该socket的消息时,会回应RST(因为拥有该socket的进程已经终止)。a进程对收到RST的socket调用write时,操作系统会给a进程发送SIGPIPE,默认处理动作是终止进程,知道你的进程为什么毫无征兆地死亡了吧:)
      

  2.   

    想到一个办法,在客户端加一个serversocket去监听,因为当客户端连上服务器的时候,服务器是有 客户端IP记录的,如果服务器检查到自身的外网IP变了,就创建clientsocket连到客户端的serversocket去,客户端的serversocket收到这个连接就知道服务器的新ip,然后就可以再连服务器,服务器替换掉socket。当然还要改应用层协议,重连时要发一个特殊的数据,服务器要能正确解析是重连,查找之前的连接替换
    当然这方法有个不足的地方,就是如果客户端没有外网IP就会失败,本来正常情况下客户端不需要外网IP的