本帖最后由 bdmh 于 2014-10-20 20:09:29 编辑

解决方案 »

  1.   

    我做过的方案是基于SignalR来建立客户端和服务端的双向通信。是在局域网环境下使用的。如果是广域的,那会有些不一样。服务端维护一个在线记录。
    客户端定时向服务端发心跳,服务端如果发现太长时间没收到某客户端的心跳,就认为其离线,将其从在线记录中删除。客户端注销时,向服务端发送注销信息,服务端也将其从在线记录中删除。当客户端登录时,向服务端的发送一个连接请求,这时SignalR就会给它分配一个唯一的Id,这个Id就代表了客户端的这次连接。将这个Id和用户账户信息保存在在线记录里。检查当前在线的有没有相同的账户,如果有,就向其(由唯一Id表示)发送强制离线信息,客户端收到这个信息就做退出处理。也将其从在线表中删除。服务端收到其它请求时要先检查在线记录,如果发起请求的客户端不在在记录里,对它的请求做已离线的错误返回。客户端提示掉线,请重新登录。对于你的场景1,再次登录时,服务端会试图向现在表里那个账户的上一次连接发送一个强制离线请求,不过因为客户端异常退出,这个连接已经无效了。用户仍然可以正常登录。如果服务端要判断客户端是不同的机器登录同样的账户,还是相同的机器异常退出,那需要客户端登录的时候发送一定信息,IP其实不准,因为局域网环境一个路由下可能很多机器,不论是服务端看到的IP还是客户端自己的IP都可能重复。准确些的信息是机器的用户名加上机器的硬件指纹(有时候还有限制客户端数量的需求)。如果不用SignalR,也可以用wcf双工模式,或者自己定义的tcp连接等等建立双向通信。如果不能实现双向通信,服务端纯被动接收请求,那需要和B/S结构一样,在客户端登录时,服务端生成一个cookie给客户端,由这个cookie来代表客户端的一次登录。客户端的每个请求都要带上这个cookie。其实就是用cookie替换了上面说的双向连接的唯一Id。然后客户端需要主动定时去请求服务端看自己的cookie还是否有效,无效就是心跳超时,或者账号再其它机器登录。
      

  2.   


    记录IP在我补充的情景1中,也会出问题,即非正常退出后在同一IP登录,会登不上
      

  3.   

    考虑非正常退出,你需要客户端维持一个心跳,记录当前的时间
    然后其他地方再登陆的时候,判断一下是否超时,超时说明其他客户端非正常退出不过具体的应用场景不同,这个也不能一概而论的
    要看你做的到底是单机程序,还是CS,还是BS,做法不同
      

  4.   

    对于短链接,或者单向通讯方式,服务器不可能去主动访问客户端,因此在制定协议中就要求客户端必须以“心跳消息”方式访问服务器。例如每分钟必须有一次,那么服务器每隔一段时间可以对于心跳延迟超过1分钟的会话信息进行清理。对于长连接,通常服务器会定期向客户端广播一些消息,然后最后一次广播时如果通讯异常,则自然就清理与客户端连接会话了。这里的“会话”,至少是指随机分配的 SessionID 所代表的意思,跟客户端 IP、甚至用户登录名都没有关系。相同IP、用户名等等,根本不能用来区分是不同连接。
      

  5.   

    参考脚本之家这个文章:http://www.jb51.net/article/24993.htm