454 lines
16 KiB
C#
454 lines
16 KiB
C#
|
||
using Chat.Framework;
|
||
using Chat.Framework.WXSdk.Implement;
|
||
using System;
|
||
using System.Collections.Concurrent;
|
||
using System.Collections.Generic;
|
||
using System.Diagnostics;
|
||
using System.Linq;
|
||
using System.Net;
|
||
using System.Net.Sockets;
|
||
using System.Text;
|
||
using System.Threading;
|
||
using System.Threading.Tasks;
|
||
|
||
namespace Chat.Framework.WXSdk.IPAD
|
||
{
|
||
/// <summary>
|
||
/// Socket管理
|
||
/// </summary>
|
||
public sealed class SocketClient
|
||
{
|
||
class SocketResult
|
||
{
|
||
public DateTime Time { get; set; }
|
||
public byte[] Data { get; set; }
|
||
}
|
||
public WeixinBase weixin;
|
||
|
||
public enum WXSocketClientError
|
||
{
|
||
connetedErr = 0,//连接错误
|
||
sendErr = 1,//发送错误
|
||
receiveErr = 2,//接受错误
|
||
};
|
||
|
||
internal Socket clientSocket;
|
||
internal DnsEndPoint hostEndPoint { get; set; }
|
||
public bool connected
|
||
{
|
||
get
|
||
{
|
||
if (this.clientSocket != null) return this.clientSocket.Connected;
|
||
return false;
|
||
}
|
||
}
|
||
Dictionary<int, SocketResult> mPackDic = new Dictionary<int, SocketResult>();
|
||
private object mPackDicLock = new object();
|
||
public Func<byte[], bool> NotifyCallback { get; set; }
|
||
int Seq = 0;
|
||
|
||
private byte[] packageData = null;
|
||
|
||
public void ClearPack()
|
||
{
|
||
lock (mPackDic)
|
||
{
|
||
var vas = mPackDic.ToList().Where(f => f.Value.Time < DateTime.Now.AddMinutes(-5));
|
||
foreach (var item in vas)
|
||
{
|
||
mPackDic.Remove(item.Key);
|
||
}
|
||
}
|
||
}
|
||
|
||
|
||
public Func<WXSocketClientError,SocketClient, bool> SocketExceptionCallback { get; set; }
|
||
|
||
|
||
public ManualResetEvent allDone = new ManualResetEvent(false);
|
||
public SocketClient(DnsEndPoint hostEndPoint, WeixinBase weixin)
|
||
{
|
||
this.weixin = weixin;
|
||
this.hostEndPoint = hostEndPoint;
|
||
CreateSocket();
|
||
}
|
||
|
||
//创建socket
|
||
public bool CreateSocket()
|
||
{
|
||
clientSocket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
|
||
clientSocket.SendTimeout = 10000;
|
||
SetXinTiao();
|
||
return true;
|
||
}
|
||
|
||
private object lock_login = new object();
|
||
|
||
public void BeginConnection()
|
||
{
|
||
try
|
||
{
|
||
this.allDone = new ManualResetEvent(false);
|
||
if (null == clientSocket)
|
||
{
|
||
CreateSocket();
|
||
}
|
||
|
||
//开始连接到服务器
|
||
clientSocket.BeginConnect(hostEndPoint, delegate
|
||
{
|
||
try
|
||
{
|
||
AsynRecive();
|
||
if (weixin.Status == Chat.Framework.WXSdk.WxStatus.在线)
|
||
{
|
||
//var client = (this.weixin as WXClientImpl_IPAD);
|
||
//client.AsyncSyncMessage();
|
||
// if (!client.AsyncSyncMessage()) this.weixin.ResetConnection();
|
||
}
|
||
}
|
||
catch (Exception)
|
||
{ }
|
||
}, null);
|
||
|
||
Thread.Sleep(200);
|
||
|
||
// wait here until the connect finishes. The callback sets allDone.
|
||
//阻塞直到connected=true
|
||
//allDone.WaitOne(1000 * 5);
|
||
|
||
// Log("连接 WeChat 成功!");
|
||
LogHelper.GetSingleObj().Info("Wechat Connect", $"{weixin.WeixinHao}-{hostEndPoint}-连接成功!!!!");
|
||
}
|
||
catch (Exception ex)
|
||
{
|
||
LogHelper.GetSingleObj().Error("Wechat Connect", $"{weixin.WeixinHao}-{hostEndPoint}-连接失败!{ex.Message}");
|
||
// Log("连接 WeChat 失败!" + ex.Message);
|
||
//Log(new LogInfo(jobinfo.JobID, "调试", "创建wx服务器连接失败,详情:" + ex.Message + Environment.NewLine + ex.StackTrace, 3));
|
||
//Log(new LogInfo(jobinfo.JobID, "错误", "创建wx服务器连接异常", 1));
|
||
|
||
}
|
||
}
|
||
//异步连接
|
||
public bool Connect()
|
||
{
|
||
lock (lock_login)
|
||
{
|
||
this.Disconnect();
|
||
BeginConnection();
|
||
Thread.Sleep(1000);
|
||
}
|
||
|
||
return connected;
|
||
}
|
||
|
||
//断开socket
|
||
public void Disconnect()
|
||
{
|
||
|
||
try
|
||
{
|
||
if (clientSocket != null && clientSocket.Connected)
|
||
{
|
||
LogHelper.GetSingleObj().Info("Wechat Disconnect", $"{weixin.WeixinHao}-{hostEndPoint}-关闭");
|
||
}
|
||
clientSocket.Shutdown(SocketShutdown.Both);
|
||
clientSocket.Disconnect(true);
|
||
clientSocket.Close();
|
||
clientSocket.Dispose();
|
||
clientSocket = null;
|
||
allDone.Dispose();
|
||
|
||
}
|
||
catch (Exception)
|
||
{ }
|
||
}
|
||
|
||
|
||
|
||
///// 设置socket心跳
|
||
private void SetXinTiao()
|
||
{
|
||
//byte[] inValue = new byte[] { 1, 0, 0, 0, 0x20, 0x4e, 0, 0, 0xd0, 0x07, 0, 0 };// 首次探测时间20 秒, 间隔侦测时间2 秒
|
||
byte[] inValue = new byte[] { 1, 0, 0, 0, 0x88, 0x13, 0, 0, 0xd0, 0x07, 0, 0 };// 首次探测时间5 秒, 间隔侦测时间2 秒
|
||
clientSocket.IOControl(IOControlCode.KeepAliveValues, inValue, null);
|
||
}
|
||
|
||
/// <summary>
|
||
/// 发送vx socket消息
|
||
/// </summary>
|
||
/// <param name="socket"></param>
|
||
/// <param name="message"></param>
|
||
public void AsynSend(byte[] _SendBuff)
|
||
{
|
||
try
|
||
{
|
||
int times = 0;
|
||
while (!connected && times < 5)
|
||
{
|
||
// Log(new LogInfo(jobinfo.JobID, "调试", , 3));
|
||
// LogHelper.GetSingleObj().Error("Wechat AsynSend", $"{weixin.WeixinHao}-{hostEndPoint}- 等待连接!");
|
||
Thread.Sleep(2000);
|
||
times++;
|
||
}
|
||
if (!connected)
|
||
{
|
||
|
||
LogHelper.GetSingleObj().Error("Wechat AsynSend", $"{weixin.WeixinHao}-{hostEndPoint}- 等待连接!");
|
||
if (null != SocketExceptionCallback)
|
||
{
|
||
SocketExceptionCallback(WXSocketClientError.connetedErr,this);
|
||
}
|
||
return;
|
||
//Log("wx服务器连接失败,请尝试重启工号");
|
||
// this.Connect();
|
||
}
|
||
|
||
byte[] SendBuff = _SendBuff;
|
||
var temp = Encoding.UTF8.GetString(SendBuff);
|
||
clientSocket.BeginSend(SendBuff, 0, SendBuff.Length, SocketFlags.None, asyncResult =>
|
||
{
|
||
try
|
||
{
|
||
//完成发送消息
|
||
int length = clientSocket.EndSend(asyncResult);
|
||
|
||
LogHelper.GetSingleObj().Info("Wechat AsynSend", $"{weixin.WeixinHao}-{hostEndPoint}-已发送{length}字节!");
|
||
}
|
||
catch (Exception)
|
||
{ }
|
||
}, null);
|
||
}
|
||
catch (Exception ex)
|
||
{
|
||
//Log ("wx服务器接收数据异常,详情:" + ex.Message + Environment.NewLine + ex.StackTrace) ;
|
||
LogHelper.GetSingleObj().Error("Wechat AsynSend", $"{weixin.WeixinHao}-{hostEndPoint}- 发送失败!{ex.Message}");
|
||
//微信socket异常问题
|
||
if (null != SocketExceptionCallback)
|
||
{
|
||
if (ex.Message.Contains("主机中的软件中止了一个已建立的连接。"))
|
||
SocketExceptionCallback(WXSocketClientError.connetedErr,this);
|
||
else SocketExceptionCallback(WXSocketClientError.sendErr,this);
|
||
}
|
||
}
|
||
}
|
||
|
||
/// <summary>
|
||
/// 通过seq取微信服务器返回的socket包数据
|
||
/// </summary>
|
||
/// <param name="Seq"></param>
|
||
/// <returns></returns>
|
||
public byte[] GetBuffBySeq(int Seq)
|
||
{
|
||
lock (mPackDicLock)
|
||
{
|
||
//Thread.Sleep(10);
|
||
byte[] buf = null;
|
||
try
|
||
{
|
||
if (mPackDic.ContainsKey(Seq))
|
||
{
|
||
buf = mPackDic[Seq].Data;
|
||
mPackDic.Remove(Seq);
|
||
return buf;
|
||
}
|
||
}
|
||
catch (Exception ee)
|
||
{
|
||
LogHelper.GetSingleObj().Error("Wechat GetBuffBySeq", $"{weixin.WeixinHao}-{hostEndPoint}- 获取超时!{ee.Message}");
|
||
}
|
||
return buf;
|
||
}
|
||
}
|
||
/// <summary>
|
||
/// 接收微信socket返回的数据
|
||
/// </summary>
|
||
/// <param name="socket"></param>
|
||
private void AsynRecive()
|
||
{
|
||
Thread.Sleep(50);
|
||
|
||
if (connected == false || clientSocket == null)
|
||
return;
|
||
|
||
//byte[] data = new byte[0x20000];
|
||
byte[] data = new byte[150000];
|
||
|
||
try
|
||
{
|
||
//开始接收数据
|
||
clientSocket.BeginReceive(data, 0, data.Length, SocketFlags.None,
|
||
asyncResult =>
|
||
{
|
||
if (connected == false || clientSocket == null)
|
||
{
|
||
return;
|
||
}
|
||
try
|
||
{
|
||
//本次接收的数据长度
|
||
int nowReceiveLenth = clientSocket.EndReceive(asyncResult);
|
||
|
||
//建立起连接后 若长时间不发送数据 wx会立马关闭socket
|
||
if (nowReceiveLenth == 0)
|
||
{
|
||
//vx socket 服务端关闭了连接,需要重连socket
|
||
//if (clientSocket.Connected)//上次socket的状态
|
||
//{
|
||
//Log("wxsocket服务端关闭了连接,将尝试新建连接");
|
||
//微信socket异常问题
|
||
if (null != SocketExceptionCallback)
|
||
{
|
||
SocketExceptionCallback(WXSocketClientError.receiveErr,this);
|
||
}
|
||
//}
|
||
return;
|
||
}
|
||
byte[] new_data = new byte[nowReceiveLenth];
|
||
Buffer.BlockCopy(data, 0, new_data, 0, nowReceiveLenth);
|
||
LogHelper.GetSingleObj().Info("Wechat AsynRecive", $"{weixin.WeixinHao}-{hostEndPoint}-{data.Length}");
|
||
DePack(new_data);
|
||
AsynRecive();
|
||
}
|
||
catch (Exception ex)
|
||
{
|
||
//LogHelper.GetSingleObj().Error("Wechat AsynRecive1", ex.Message);
|
||
LogHelper.GetSingleObj().Error("Wechat AsynRecive1", $"{weixin.WeixinHao}-{hostEndPoint}- { ex.Message}");
|
||
// Log("解析wx数据异常,详情:" + ee.Message + Environment.NewLine + ee.StackTrace);
|
||
// Log(new LogInfo(jobinfo.JobID, "调试", "wxsocket服务端关闭了连接,将尝试新建连接", 3));
|
||
//微信socket异常问题
|
||
if (null != SocketExceptionCallback)
|
||
{
|
||
SocketExceptionCallback(WXSocketClientError.receiveErr,this);
|
||
}
|
||
}
|
||
}, null);
|
||
}
|
||
catch (Exception ex)
|
||
{
|
||
// Log("连接被wx服务器关闭,接收数据失败。异常详情:" + ex.Message + Environment.NewLine + ex.StackTrace);
|
||
// LogHelper.GetSingleObj().Error("Wechat AsynRecive2", ex.Message);
|
||
LogHelper.GetSingleObj().Error("Wechat AsynRecive2", $"{weixin.WeixinHao}-{hostEndPoint}- { ex.Message}");
|
||
//微信socket异常问题
|
||
if (null != SocketExceptionCallback) SocketExceptionCallback(WXSocketClientError.receiveErr,this);
|
||
}
|
||
//finally
|
||
//{
|
||
|
||
//}
|
||
}
|
||
|
||
//分包
|
||
public bool DePack(byte[] bys)
|
||
{
|
||
bool result = false;
|
||
int x = bys.Length;
|
||
|
||
if (packageData != null)
|
||
{
|
||
byte[] temp = new byte[packageData.Length + x];
|
||
Buffer.BlockCopy(packageData, 0, temp, 0, packageData.Length);
|
||
Buffer.BlockCopy(bys, 0, temp, packageData.Length, x);
|
||
packageData = temp;
|
||
}
|
||
else
|
||
{
|
||
packageData = new byte[x];
|
||
Buffer.BlockCopy(bys, 0, packageData, 0, x);
|
||
}
|
||
|
||
while (packageData != null && packageData.Length > 16)
|
||
{
|
||
int newPackageLength = WXClientTool.ReadInt(packageData, 0);
|
||
if (newPackageLength <= 0 || newPackageLength > 8000000)
|
||
{
|
||
LogHelper.GetSingleObj().Error("Wechat DePack", "长度不合法");
|
||
packageData = null;
|
||
}
|
||
else if (newPackageLength <= packageData.Length)
|
||
{
|
||
byte[] newPackage = new byte[newPackageLength];
|
||
Buffer.BlockCopy(packageData, 0, newPackage, 0, newPackageLength);
|
||
if (newPackageLength < packageData.Length)
|
||
{
|
||
byte[] temData = new byte[packageData.Length - newPackageLength];
|
||
Buffer.BlockCopy(packageData, newPackageLength, temData, 0, packageData.Length - newPackageLength);
|
||
packageData = temData;
|
||
}
|
||
else
|
||
{
|
||
packageData = null;
|
||
}
|
||
HandlePackage(newPackage);
|
||
}
|
||
else
|
||
{
|
||
break;
|
||
}
|
||
}
|
||
return result;
|
||
}
|
||
|
||
//解析包
|
||
public void HandlePackage(byte[] bys)
|
||
{
|
||
if (bys.Length == 20 && bys[3] == 20 && bys[5] == 16 && bys[7] == 1)
|
||
{
|
||
// 有新消息就会接受到此包
|
||
SaveBufferToPackDic(bys);
|
||
return;
|
||
}
|
||
else
|
||
{
|
||
if (bys.Length >= 16 && bys[16] != (byte)191
|
||
&& !(bys[3] == 58 && bys[5] == 16 && bys[7] == 1 && bys.Length == 58)
|
||
&& !(bys[3] == 47 && bys[5] == 16 && bys[7] == 1 && bys.Length == 47))
|
||
{
|
||
return;
|
||
}
|
||
SaveBufferToPackDic(bys);
|
||
}
|
||
}
|
||
|
||
|
||
//保存数据到mPackDic
|
||
public void SaveBufferToPackDic(byte[] bys)
|
||
{
|
||
lock (mPackDicLock)
|
||
{
|
||
Seq = WXClientTool.ReadInt(bys, 12);
|
||
if (Seq == 0)
|
||
{
|
||
byte[] buffers = new byte[bys.Length];
|
||
Buffer.BlockCopy(bys, 0, buffers, 0, bys.Length);
|
||
|
||
int cmd = WXClientTool.ReadInt(buffers, 8);
|
||
int selector = WXClientTool.ReadInt(buffers, 16);
|
||
LogHelper.GetSingleObj().Info("Wechat SaveBufferToPackDic", $"{weixin.WeixinHao}-{hostEndPoint}-收到{buffers.Length}字节!");
|
||
if (cmd == 318 && NotifyCallback != null)
|
||
{
|
||
NotifyCallback(buffers);
|
||
}
|
||
|
||
if (cmd == 24 && NotifyCallback != null)
|
||
{
|
||
NotifyCallback(buffers);
|
||
}
|
||
|
||
if (cmd != 24 && cmd != 318)
|
||
{
|
||
}
|
||
}
|
||
else
|
||
{
|
||
byte[] buffers = new byte[bys.Length];
|
||
Buffer.BlockCopy(bys, 0, buffers, 0, bys.Length);
|
||
mPackDic[Seq] = new SocketResult() { Data = buffers, Time = DateTime.Now };
|
||
Seq = 0;
|
||
}
|
||
}
|
||
}
|
||
}
|
||
}
|