(C#)长连接是怎么实现的,心跳包又是怎么运作的

(C#)长连接是如何实现的,心跳包又是如何运作的?
本帖最后由 leesony 于 2012-02-18 21:02:37 编辑
长连接的原理及心跳包的流程我知道的,但是以前没做过长连接,不知道代码如何写。

我的需求是这样的:
1、手机客户端连接服务器,从服务器获取资源,客户端连上服务器后不断开连接(保持一个Socket),不通过IIS,不走HTTP协议,通过XML协议传输数据包。服务器如何与客户端建立长连接?
2、设置心跳包(建一个心跳包Socket),每隔5秒收服务器发一次数据,用于判断是否连接正常,连续5次接收心跳包失败时,服务器断开与客户端的长连接释放资源(包括传输数据Socket和心跳包Socket),如何建立心跳包(包括心跳包的mode字段),如何收发心跳包?当通过心跳包判断出与客户端失去连接时,如何结束进程释放资源?
3、如何及时清理服务器与客户端已经断开的线程?

请给具体的代码如何写并给出注释,谢谢
------解决方案--------------------
给你一个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);