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. unity3d Hair real time rendering 真实头发实时渲染(转)

    惊现塞拉酱 算法是Weta Digital根据siggraph2003的论文加以改进,改进之前使用的是Kajiya and Kay’s 模型,它能量不守恒,也就是说不是基于物理的,不准确 电镜下真实头 ...

  2. Floyd算法解决最短路径问题

    时间限制:10000ms 单点时限:1000ms 内存限制:256MB 描述 万圣节的中午,A和B在吃过中饭之后,来到了一个新的鬼屋!鬼屋中一共有N个地点,分别编号为1..N,这N个地点之间互相有一些 ...

  3. Spring MVC + Spriing + MyBatis整合,写给新人

    开发环境: 开发工具:MyEclipse 8.6 数据库:MySQL 操作系统:WIN8.1 Jar包: Spirng和SpringMVC版本:3.2.9 MyBatis版本:3.2.8 其他关联Ja ...

  4. 英语之idiom

    1 quick and dirty = Done or constructed in a hasty, approximate, temporarily adequate manner, but no ...

  5. 【c_prime_plus】第十七章笔记

    抽象数据类型(ADT) 为类型的属性和可对类型执行的操作提供一个抽象的描述,这个米阿叔不受任何实现的约束,甚至不受任何特定编程语言的约束,这样一种正式的抽象描述被称为抽象数据类型. 开一个实现该ADT ...

  6. Ruby on Rails vs. PHP vs. Python

    开发者在开发web应用时,往往会对平台的选择感到困惑,而web专家通常会建议:要考虑几个因素,这些因素包括周转时间.质量.跨浏览器兼容性.与其他框架的整合.数据安全性.易于访问性等. 在考虑了这些因素 ...

  7. 使用Powershell 添加,选择更改订阅

    PS C:\WINDOWS\system32> Import-AzurePublishSettingsFile 'C:\Users\Ling\Desktop\Free-11-24-2014-cr ...

  8. bzoj3717: [PA2014]Pakowanie

    Time Limit: 90 Sec Memory Limit: 256 MBSubmit: 128 Solved: 43[Submit][Status][Discuss]Description 你有 ...

  9. 【数据结构与算法分析——C语言描述】第一章总结 引论

    这一章主要复习了一些数学知识,像指数.对数.模运算.级数公式:还有2种证明方法,归纳假设法和反证法.所幸以前学过,重新拾捡起来也比较轻松. 简要地复习了递归,提出了编写递归例程的四条基本法则: 基准情 ...

  10. jszs 枚举算法

    <!doctype html> <html> <head> <meta charset="utf-8"> <title> ...