socket长连接,心跳包怎么实现啊

.Net技术 码拜 8年前 (2016-02-26) 2142次浏览
最近在做软件和硬件的开发 要用到socket和硬件去联系,本人这边是服务端,是socket长连接形式,可以打开多个客服端和本人这边服务端通信,正常通信 接受和发送都实现了 没问题,现在就是 客服端会每隔30秒给本人发一个心跳包过来检测能否与服务端还是正常连接,所以每个客服端发过来的心跳包本人不想去数据库建立一个表去存它的心跳数据 那样数据太多了。在代码里面怎么实现啊,现在就卡这里了,请各位高手帮帮忙啊。下面是本人的全部代码!
{
public Service1()
{
InitializeComponent();
}
System.Timers.Timer timer;
private StreamWriter streamWriter; //写文件
Thread threadWatch = null; // 负责监听客户端连接请求的 线程;
Socket socketWatch = null;
private string host = ConfigurationManager.AppSettings[“ip”].ToString();
private int port = int.Parse(ConfigurationManager.AppSettings[“port”].ToString());
private int second = int.Parse(ConfigurationManager.AppSettings[“second”].ToString());
DBAll db = new DBAll();
protected override void OnStart(string[] args)
{
btnBeginListen();
timer = new System.Timers.Timer();
timer.Elapsed += new System.Timers.ElapsedEventHandler(timer_Elapsed);
timer.Interval = second * 1000;
timer.Enabled = true;
timer.Start();
}
protected override void OnStop()
{
WriteError(“服务已经停止了!”);
}
//socket开启
public void btnBeginListen()
{
// 创建负责监听的套接字
socketWatch = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
IPAddress address = IPAddress.Parse(host);
// 创建包含ip和端口号的网络节点对象;
IPEndPoint endPoint = new IPEndPoint(address, port);
try
{
// 将负责监听的套接字绑定到唯一的ip和端口上;
socketWatch.Bind(endPoint);
}
catch
{
return;
}
// 设置监听队列的长度;
socketWatch.Listen(100);
// 创建负责监听的线程;
threadWatch = new Thread(WatchConnecting);
threadWatch.IsBackground = true;
threadWatch.Start();
//}
}
/// <summary>
/// 监听客户端请求的方法;
/// </summary>
void WatchConnecting()
{
while (true)  // 持续不断的监听客户端的连接请求;
{
// 开始监听客户端连接请求,Accept方法会阻断当前的线程;
Socket sokConnection = socketWatch.Accept(); // 一旦监听到一个客户端的请求,就返回一个与该客户端通信的 套接字;
// 客户端的IP信息;
// WriteError(sokConnection.RemoteEndPoint.ToString());
Thread thr = new Thread(RecMsg);
thr.IsBackground = true;
thr.Start(sokConnection);
}
}
/// <summary>
/// 接受信息的方法
/// </summary>
/// <param name=”sokConnectionparn”></param>
void RecMsg(object sokConnectionparn)
{
Socket sokClient = sokConnectionparn as Socket;
//List<byte> byteList = new List<byte>();
//int length = -1;
//do
//{
//    // 定义一个1M的缓存区;
//    byte[] arrMsgRec = new byte[1024];
//    // 将接受到的数据存入到输入  arrMsgRec中;
//    try
//    {
//        length = sokClient.Receive(arrMsgRec); // 接收数据,并返回数据的长度;
//        byteList.AddRange(arrMsgRec);
//        if (length < byteList.ToArray().Length)
//        {
//            string strMsg = System.Text.Encoding.Default.GetString(byteList.ToArray(), 0, length);// 将接受到的字节数据转化成字符串;
//            handleData(strMsg, sokClient);
//        }
//    }
//    catch
//    {
//        break;
//    }
//} while (true);
do
{
byte[] arrMsgRec = new byte[1024];
try
{
int length = sokClient.Receive(arrMsgRec); // 接收数据,并返回数据的长度;
string strMsg = System.Text.Encoding.Default.GetString(arrMsgRec, 0, length);// 将接受到的字节数据转化成字符串;
handleData(strMsg, sokClient);
}
catch
{
break;
}
} while (true);
}
/// <summary>
/// 处理数据的方法
/// </summary>
public void handleData(string str, Socket sokClient)
{

string[] strAll = str.Split(” “);
switch (int.Parse(strAll[8]))
{
case 2://登陆

break;
case 1://心跳
heartbeat(str, sokClient);
break;

}

}

#region 心跳
public void heartbeat(string str, Socket sokClient)
{
}
void timer_Elapsed(object sender, System.Timers.ElapsedEventArgs e)
{
}
#endregion

public void WriteError(string message)
{
try
{
string directPath = System.AppDomain.CurrentDomain.BaseDirectory + “info\”;
if (!Directory.Exists(directPath))   //判断文件夹能否存在,假如不存在则创建
{
Directory.CreateDirectory(directPath);
}
directPath += string.Format(@”\{0}.log”, DateTime.Now.ToString(“yyyy-MM-dd”));
if (streamWriter == null)
{
streamWriter = !File.Exists(directPath) ? File.CreateText(directPath) : File.AppendText(directPath);    //判断文件能否存在假如不存在则创建,假如存在则添加。
}
streamWriter.WriteLine(“***********************************************************************”);
streamWriter.WriteLine(“错误时间:” + DateTime.Now.ToString(“HH:mm:ss”));
if (message != null)
{
streamWriter.WriteLine(“异常信息:\r\n” + message);
}
}
finally
{
if (streamWriter != null)
{
streamWriter.Flush();
streamWriter.Dispose();
streamWriter = null;
}
}
}
}
主要是心跳包的检测是检测哪个方法 或放在哪个位置啊?

解决方案

50

首先,你不能在  int length = sokClient.Receive(arrMsgRec) 语句之后立刻去搞什么 Encoding.GetString(….),原因是tcp 是会粘包和分包的,你应该是等到1次或多次 Receive 之后、看到了消息结束标志之后(有时候粘包的话,那么消息结束标志之后还跟着下一个消息的一部分内容),才来把这个从缓冲中消息取出来并进行处理。
第二,你的 handleData 方法本来就是处理各种消息的。刚一看你的问题描述,给本人说“蒙了”,本人说“这个人怎么会说通讯是到数据库表存数据”呢?看了你这个方法本人知道了,你根本没有搞懂本人写了什么代码。这里就是处理消息啊,你就按照你们的约定返回呗。例如说约定要返回“OK”,那么就向 sokClient 写入包含了“OK”的消息(的二进制编码)呗。
第三,不要滥用 Encoding.Default。这个在不同的机器上、不同的操作系统上,返回结果不同。所以用这个 Default,看上去挺高大上(通用),实际上是滥用,你根本不确定本人再干什么。所以使用 UTF8 或 gb2312 都可以,但是不要滥用 Default。
第四,handleData 通常应该异步多线程来实现,例如写

ThreadPool.QueueUserWorkItem(h =>handleData(strMsg, sokClient));

,这样可以释放 I/O 线程,从而让它提前处理 sokClient 的后边的消息。当然,这是假设你对于长连接的通讯方式,知道信令处理完然后返回结果的次序跟收到信令的次序可能有所改变的情况下。原因是有的任务可能处理要200毫秒,而有的可能只要3毫秒,那么当客户端以次序1、2、3、4、5来发送消息时,可能受到反馈的次序是对应着1、4、2、5、3消息的。
假如你不想处理长连接的业务逻辑,那么就不要写成一个长连接的通讯程序。

10

最后一点,假如这些代码是你靠“蒙”弄来的,那么你要从头重新学编程。否则就要先看懂本人的白纸黑字写的够明白的代码。
这对你有好处。

10

心跳包本质是当客户端无业务数据时,发送标识包,以维持长连接;
对于server来说,要管理的对象不能是个“裸”的socket,而是要抽象下,是基于socket的客户端session
这session中除了socket,大多数情况下还要记录身份验证信息,最后一次接收数据的时间等,
同时server侧要有超时剔除逻辑,当session的操作时间timeout后,
将相应的socket关闭,释放资源

10

心跳其实很单纯,你就当常规数据一样去收,收到后更新一下对应clinet的最后活动时间即可,不需要去数据库捣腾。另外你需要启动一个清理后台线程,定期检查全部client是不是假死了(最后活动时间过于久远),假死了就主动断开,这也是心跳的目的。

10

服务端对每一个连接上的客户端SOCKET进行管理,每收到一个心跳包更新对应的客户端SOCKET超时判断,另外实时监控客户端SOCKET,有连接超时即中断连接并清理。

8

你不断地 new Thread 你不怕电脑挂了吗?同步socket应该用线程池操作吧!

1

定时发一个约定的标识符给对方,说明你还活着,这就是心跳包

1

socket长连接,心跳包怎么实现啊考虑心跳包之前还是把现有代码给整理整理吧

CodeBye 版权所有丨如未注明 , 均为原创丨本网站采用BY-NC-SA协议进行授权 , 转载请注明socket长连接,心跳包怎么实现啊
喜欢 (0)
[1034331897@qq.com]
分享 (0)