server的代码
public abstract class Server {
static readonly ILog logger = LogManager.GetLogger(typeof(Server)); public int Port { get; set; }
public event ClientEventHandler OnClientAcceptEvent;
public event ClientEventHandler OnClientOnlineEvent;
public event ClientEventHandler OnClientRemoveEvent;
private bool bStarted;
static private int NextClientId = 0;
private TcpListener _listener;
protected Dictionary<int, Client> id2client = new Dictionary<int, Client>();
private List<Client> noHeartBeatClients = new List<Client>(); public bool HasHeartBeat;
private int CheckHeartBeatInterval = 30 * 1000;// ms
protected System.Timers.Timer CheckHeartBeatTimer = new System.Timers.Timer();
private object id2clientCS = new object();
private object noHeartBeatClientsCS = new object(); private Server() {
CheckHeartBeatTimer.Elapsed += (o1, a1) => {
if(HasHeartBeat == false) return;
List<Client> kickClientList = new List<Client>();
lock(id2clientCS) {
try {
DateTime now = DateTime.Now;
foreach(KeyValuePair<int, Client> pair in id2client) {
try {
Client client = pair.Value;
TimeSpan offset = now - client.LastHeartBeat;
if(client != null && client.State == Client.ConnectionState.Connected && offset.TotalMilliseconds > CheckHeartBeatInterval) {
kickClientList.Add(pair.Value);
logger.InfoFormat("检测到心跳超时: [IP]{0}, [ID]{1}, [Time]{2}", client.ClientIpAddress, pair.Key, DateTime.Now.ToString());
}
} catch { }
}
} catch(Exception ex) {
logger.WarnFormat("心跳检测时发生异常: \n{0}", ex);
}
}
kickClientList.ForEach(p => p.Close());
lock(noHeartBeatClientsCS) {
kickClientList.ForEach(c => noHeartBeatClients.RemoveAll(p => p.Id == c.Id));
}
};
} public Server(int port)
: this() {
this.Port = port;
} public List<Client> Clients {
get {
List<Client> result = new List<Client>();
lock(id2clientCS) {
foreach(Client each in id2client.Values) {
result.Add(each);
}
}
return result;
}
} public virtual void Open() {
_listener = new TcpListener(Port);
logger.InfoFormat("Server#Open port={0}", Port);
try {
_listener.Start();
if(HasHeartBeat) {
CheckHeartBeatTimer.Stop();
CheckHeartBeatTimer.Interval = CheckHeartBeatInterval;
CheckHeartBeatTimer.Start();
}
_listener.BeginAcceptTcpClient(new AsyncCallback(OnAccept), null);
bStarted = true;
} catch(SocketException ex) {
logger.WarnFormat("服务器监听发生异常:{0}\nSocket ErrorCode: {1}\n提示:请检查端口是否已被占用", ex.Message, ex.ErrorCode);
throw ex;
} catch(Exception ex) {
logger.Warn(ex);
throw ex;
}
} public virtual void Close() {
try {
if(HasHeartBeat) {
CheckHeartBeatTimer.Stop();
}
_listener.Stop();
bStarted = false; lock(id2clientCS) {
foreach(Client each in id2client.Values) {
try { if(each != null)each.Close(); } catch { }
}
id2client.Clear();
}
} catch(Exception ex) {
logger.Warn(ex);
throw ex;
}
} private void OnAccept(IAsyncResult ar) {
try {
Client client = CreateClient(NextClientId++, _listener.EndAcceptTcpClient(ar), this);
client.LastHeartBeat = DateTime.Now;
client.PostOfflineEvent += (obj, args) => RemoveClient(client);
lock(id2clientCS) {
id2client.Add(client.Id, client);
}
lock(noHeartBeatClientsCS) {
noHeartBeatClients.Add(client);
}
if(OnClientAcceptEvent != null) OnClientAcceptEvent(client);
} catch(ObjectDisposedException) { } catch(Exception ex) {
logger.Warn(ex);
} finally {
try {
_listener.BeginAcceptTcpClient(new AsyncCallback(OnAccept), null);
} catch(Exception) {
// ignore
}
}
} protected abstract Client CreateClient(int id, TcpClient tcpClient, Server server); public void RemoveClient(Client client) {
if(bStarted == false) return;
lock(id2clientCS) {
id2client.Remove(client.Id);
}
if(OnClientRemoveEvent != null) OnClientRemoveEvent(client);
} public void GetHeartBeat(Client client) {
if(HasHeartBeat == false) return; client.LastHeartBeat = DateTime.Now;
lock(noHeartBeatClientsCS) {
int index = noHeartBeatClients.FindIndex(p => p.Id == client.Id);
if(index == -1) return;
try {
if(OnClientOnlineEvent != null) OnClientOnlineEvent(client);
} catch(Exception ex) {
logger.Warn(ex);
}
noHeartBeatClients.RemoveAt(index);
}
}
}

-----解决方案--------------------------------------------------------
在定时器里隔段时间就发送几个字节的数据。如果3次没有返回则断开 
------解决方案--------------------------------------------------------
客户端:
30秒发送一个心跳包到服务器

服务器:
接收到心跳包,更新LastHeartbeatTime
并且有一个线程,一分钟扫描一次,如果LastHeartbeatTime超过一分钟没更新的视为下线

------解决方案--------------------------------------------------------

C# code
模拟心跳的机制
使用直接调用函数模拟心跳,没有涉及到socket
写得不好,不要太挑剔
using System;
using System.Collections.Generic;
using System.Threading; namespace ConsoleApplication1
{
// 客户端离线委托
public delegate void ClientOfflineHandler(ClientInfo client); // 客户端上线委托
public delegate void ClientOnlineHandler(ClientInfo client); public class Program
{
/// <summary>
/// 客户端离线提示
/// </summary>
/// <param name="clientInfo"></param>
private static void ClientOffline(ClientInfo clientInfo)
{
Console.WriteLine(String.Format("客户端{0}离线,离线时间:\t{1}", clientInfo.ClientID, clientInfo.LastHeartbeatTime));
} /// <summary>
/// 客户端上线提示
/// </summary>
/// <param name="clientInfo"></param>
private static void ClientOnline(ClientInfo clientInfo)
{
Console.WriteLine(String.Format("客户端{0}上线,上线时间:\t{1}", clientInfo.ClientID, clientInfo.LastHeartbeatTime));
} static void Main()
{
// 服务端
Server server = new Server(); // 服务端离线事件
server.OnClientOffline += ClientOffline; // 服务器上线事件
server.OnClientOnline += ClientOnline; // 开启服务器
server.Start(); // 模拟100个客户端
Dictionary<Int32, Client> dicClient = new Dictionary<Int32, Client>();
for (Int32 i = 0; i < 100; i++)
{
// 这里传入server只是为了方便而已
Client client = new Client(i + 1, server);
dicClient.Add(i + 1, client); // 开启客户端
client.Start();
} System.Threading.Thread.Sleep(1000); while (true)
{
Console.WriteLine("请输入要离线的ClientID,输入0则退出程序:");
String clientID = Console.ReadLine();
if (!String.IsNullOrEmpty(clientID))
{
Int32 iClientID = 0;
Int32.TryParse(clientID, out iClientID);
if (iClientID > 0)
{
Client client;
if (dicClient.TryGetValue(iClientID, out client))
{
// 客户端离线
client.Offline = true;
}
}
else
{
return;
}
}
}
}
} /// <summary>
/// 服务端
/// </summary>
public class Server
{
public event ClientOfflineHandler OnClientOffline;
public event ClientOnlineHandler OnClientOnline; private Dictionary<Int32, ClientInfo> _DicClient; /// <summary>
/// 构造函数
/// </summary>
public Server()
{
_DicClient = new Dictionary<Int32, ClientInfo>(100);
} /// <summary>
/// 开启服务端
/// </summary>
public void Start()
{
// 开启扫描离线线程
Thread t = new Thread(new ThreadStart(ScanOffline));
t.IsBackground = true;
t.Start();
} /// <summary>
/// 扫描离线
/// </summary>
private void ScanOffline()
{
while (true)
{
// 一秒扫描一次
System.Threading.Thread.Sleep(1000); lock (_DicClient)
{
foreach (Int32 clientID in _DicClient.Keys)
{
ClientInfo clientInfo = _DicClient[clientID]; // 如果已经离线则不用管
if (!clientInfo.State)
{
continue;
} // 判断最后心跳时间是否大于3秒
TimeSpan sp = System.DateTime.Now - clientInfo.LastHeartbeatTime;
if (sp.Seconds >= 3)
{
// 离线,触发离线事件
if (OnClientOffline != null)
{
OnClientOffline(clientInfo);
} // 修改状态
clientInfo.State = false;
}
}
}
}
} /// <summary>
/// 接收心跳包
/// </summary>
/// <param name="clientID">客户端ID</param>
public void ReceiveHeartbeat(Int32 clientID)
{
lock (_DicClient)
{
ClientInfo clientInfo;
if (_DicClient.TryGetValue(clientID, out clientInfo))
{
// 如果客户端已经上线,则更新最后心跳时间
clientInfo.LastHeartbeatTime = System.DateTime.Now;
}
else
{
// 客户端不存在,则认为是新上线的
clientInfo = new ClientInfo();
clientInfo.ClientID = clientID;
clientInfo.LastHeartbeatTime = System.DateTime.Now;
clientInfo.State = true; _DicClient.Add(clientID, clientInfo); // 触发上线事件
if (OnClientOnline != null)
{
OnClientOnline(clientInfo);
}
}
}
}
} /// <summary>
/// 客户端
/// </summary>
public class Client
{
public Server Server;
public Int32 ClientID;
public Boolean Offline; /// <summary>
/// 构造函数
/// </summary>
/// <param name="clientID"></param>
/// <param name="server"></param>
public Client(Int32 clientID, Server server)
{
ClientID = clientID;
Server = server;
Offline = false;
} /// <summary>
/// 开启客户端
/// </summary>
public void Start()
{
// 开启心跳线程
Thread t = new Thread(new ThreadStart(Heartbeat));
t.IsBackground = true;
t.Start();
} /// <summary>
/// 向服务器发送心跳包
/// </summary>
private void Heartbeat()
{
while (!Offline)
{
// 向服务端发送心跳包
Server.ReceiveHeartbeat(ClientID); System.Threading.Thread.Sleep(1000);
}
}
} /// <summary>
/// 客户端信息
/// </summary>
public class ClientInfo
{
// 客户端ID
public Int32 ClientID; // 最后心跳时间
public DateTime LastHeartbeatTime; // 状态
public Boolean State;
}
}

socket 怎么设置心跳判断连接的更多相关文章

  1. socket通信时如何判断当前连接是否断开--select函数,心跳线程,QsocketNotifier监控socket

    client与server建立socket连接之后,如果突然关闭server,此时,如果不在客户端close(socket_fd),会有不好的影响: QsocketNotifier监控socket的槽 ...

  2. Socket编程中的长连接、短链接以及心跳包机制详解

    参考:http://blog.csdn.net/zdwzzu2006/article/details/7723738 一.定义 1.TCP连接 当网络通信时采用TCP协议时,在真正的读写操作之前,se ...

  3. C# 网络连接中异常断线的处理:ReceiveTimeout, SendTimeout 及 KeepAliveValues(设置心跳)

    C# 网络连接中异常断线的处理:ReceiveTimeout, SendTimeout 及 KeepAliveValues(设置心跳) 在使用 TcpClient 网络连接中常常会发生客户端连接异常断 ...

  4. WCF心跳判断服务端及客户端是否掉线并实现重连接

    WCF心跳判断服务端及客户端是否掉线并实现重连接 本篇文章将通过一个实例实现对WCF中针对服务端以及客户端是否掉线进行判断:若掉线时服务器或客户端又在线时将实现自动重连:将通过WCF的双工知识以及相应 ...

  5. [转] python 远程主机强迫关闭了一个现有的连接 socket 超时设置 errno 10054

    python socket.error: [Errno 10054] 远程主机强迫关闭了一个现有的连接.问题解决方案: 前几天使用python读取网页.因为对一个网站大量的使用urlopen操作,所以 ...

  6. Java Socket 网络编程心跳设计概念

    Java Socket 网络编程心跳设计概念   1.一般是用来判断对方(设备,进程或其它网元)是否正常动行,一 般采用定时发送简单的通讯包,如果在指定时间段内未收到对方响应,则判断对方已经当掉.用于 ...

  7. libevent (一) socket属性设置与初始化操作

    socket属性设置与初始化操作 libevent是一个事件触发的网络库,适用于windows.linux.bsd等多种平台,内部使用select.epoll.kqueue等系统调用管理事件机制.著名 ...

  8. C++ Socket超时设置

    用winsocket时,send(),recv()过程中有时由于网络状况等原因,收发不能预期进行,可以设置收发时限:int nNetTimeout = 1000; //1秒//发送时限setsocko ...

  9. Python socket实现处理多个连接

       socket实现处理多个连接 实现处理多个连接 使用whlie循环实现多个客户端,排队请求服务端 循环监听端口发送信息(windos,Linux) 1.Linux 系统如果客户端断开连接,会循环 ...

随机推荐

  1. MapReduce 中job.setJarByClass()方法的疑惑

    在调试mr实例的时候,遇到如下的情况,如图所示 说明:就是我的mr程序类名称和我设置的setJarByclass()中设置的不一样,但是程序竟然没有报错!!!!当时把我吓尿了 疑惑:如果这样设置的话, ...

  2. 详解centos下vi的用法

    vi编辑器是所有Unix及Linux系统下标准的编辑器,它的强大不逊色于任何最新的文本编辑器,这里只是简单地介绍一下它的用法和一小部分指令.由于对Unix及Linux系统的任何版本,vi编辑器是完全相 ...

  3. arp spoofing

    Today our tutorial will talk about Kali Linux Man in the Middle Attack. How to perform man in the mi ...

  4. Netmask v. Address Prefix Length

    Netmask Address Prefix Length Hosts / Class C's / Class B's / Class A's (Class C) / / , / , / , / , ...

  5. Raspberry Pi上手

    2013-05-21 买的树莓派终于到手了,嘿嘿.我在官方代理ICKEY买的,是英国版,B型. 上手教程可以根据Getting Started with Raspberry Pi(网上有电子版免费下载 ...

  6. cubieboard中使用py-kms与dnsmasq搭建局域网内全自动KMS激活环境

    众所周知,KMS激活方式是当前广大网民“试用”windows,office的最广泛的激活方式.几乎可以用于微软的全线产品. 但是在本机使用KMS类的激活工具总是有些不放心,一方面每隔180天都要重新激 ...

  7. HW7.18

    public class Solution { public static void main(String[] args) { int[][] m = {{1, 2}, {3, 4}, {5, 6} ...

  8. 一排下去再上来的div

    <!DOCTYPE HTML> <html> <head> <meta http-equiv="Content-Type" content ...

  9. 著名加密库收集 Encrypt

    CryptoAPI 微软的CryptoAPI crypt32.lib,advapi32.lib,cryptui.lib #include <wincrypt.h>#include < ...

  10. cocos2d-x使用python创建vs模板

    cocos2d-x 2.2推荐使用create_project.py创建工程,所有的平台都可以通过这个python文件创建工程.这个文件位置在源码cocos2d-x-2.2.2\tools\proje ...