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;
|
|||
|
}
|
|||
|
}
|
|||
|
}
|
|||
|
}
|
|||
|
}
|