Sic.Framework-Nanjing-Baishi/MECF.Framework.RT.Core/IoProviders/Siemens/Net/NetworkBase/NetworkBase.cs

965 lines
33 KiB
C#
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Net.Sockets;
using System.IO;
using System.Threading;
using System.Net;
using System.Threading.Tasks;
using Aitex.Core.RT.Log;
using MECF.Framework.RT.Core;
using MECF.Framework.RT.Core.IoProviders.Siemens;
using MECF.Framework.RT.Core.IoProviders.Siemens.IMessage;
using MECF.Framework.RT.Core.IoProviders.Siemens.Transfer;
using MECF.Framework.RT.Core.IoProviders.Siemens.Net.StateOne;
#if (NET451 || NETSTANDARD2_0)
using System.Threading.Tasks;
#endif
/*************************************************************************************
*
* 说明:
* 本组件的所有网络类的基类。提供了一些基础的操作实现,部分实现需要集成实现
*
* 重构日期2018年3月8日 21:22:05
*
*************************************************************************************/
namespace MECF.Framework.RT.Core.IoProviders.Siemens.Net.NetworkBase
{
/// <summary>
/// 本系统所有网络类的基类,该类为抽象类,无法进行实例化
/// </summary>
/// <remarks>
/// network base class, support basic operation with socket
/// </remarks>
public abstract class NetworkBase
{
#region Constructor
/// <summary>
/// 实例化一个NetworkBase对象
/// </summary>
/// <remarks>
/// 令牌的默认值为空都是0x00
/// </remarks>
public NetworkBase( )
{
Token = Guid.Empty;
}
#endregion
#region Public Properties
/// <summary>
/// 网络类的身份令牌
/// </summary>
/// <remarks>
/// 适用于Hsl协议相关的网络通信类不适用于设备交互类。
/// </remarks>
/// <example>
/// 此处以 <see cref="Enthernet.NetSimplifyServer"/> 服务器类及 <see cref="Enthernet.NetSimplifyClient"/> 客户端类的令牌设置举例
/// <code lang="cs" source="HslCommunication_Net45.Test\Documentation\Samples\Core\NetworkBase.cs" region="TokenClientExample" title="Client示例" />
/// <code lang="cs" source="HslCommunication_Net45.Test\Documentation\Samples\Core\NetworkBase.cs" region="TokenServerExample" title="Server示例" />
/// </example>
public Guid Token { get; set; }
/// <summary>
/// 是否使用同步的网络通讯
/// </summary>
public bool UseSynchronousNet { get; set; } = false;
#endregion
#region Potect Member
/// <summary>
/// 通讯类的核心套接字
/// </summary>
protected Socket CoreSocket = null;
public bool IsConnected
{
get
{
return CoreSocket != null && CoreSocket.Connected;
}
}
#endregion
#region Protect Method
/// <summary>
/// 检查网络套接字是否操作超时,需要对套接字进行封装
/// </summary>
/// <param name="obj">通常是 <see cref="HslTimeOut"/> 对象 </param>
protected void ThreadPoolCheckTimeOut( object obj )
{
if (obj is HslTimeOut timeout)
{
while (!timeout.IsSuccessful)
{
System.Threading.Thread.Sleep( 100 );
if ((DateTime.Now - timeout.StartTime).TotalMilliseconds > timeout.DelayTime)
{
// 连接超时或是验证超时
if (!timeout.IsSuccessful)
{
timeout.Operator?.Invoke( );
timeout.WorkSocket?.Close( );
}
break;
}
}
}
}
#endregion
/*****************************************************************************
*
* 说明:
* 下面的三个模块代码指示了如何接收数据,如何发送数据,如何连接网络
*
********************************************************************************/
#region Reveive Content
/// <summary>
/// 接收固定长度的字节数组
/// </summary>
/// <remarks>
/// Receive Special Length Bytes
/// </remarks>
/// <param name="socket">网络通讯的套接字</param>
/// <param name="length">准备接收的数据长度</param>
/// <returns>包含了字节数据的结果类</returns>
protected OperateResult<byte[]> Receive( Socket socket, int length )
{
if (length == 0) return OperateResult.CreateSuccessResult( new byte[0] );
if (UseSynchronousNet)
{
try
{
byte[] data = NetSupport.ReadBytesFromSocket( socket, length );
return OperateResult.CreateSuccessResult( data );
}
catch (Exception ex)
{
socket?.Close( );
LOG.Write(ex, "Receive");
return new OperateResult<byte[]>( ex.Message );
}
}
OperateResult<byte[]> result = new OperateResult<byte[]>( );
ManualResetEvent receiveDone = null;
StateObject state = null;
try
{
receiveDone = new ManualResetEvent( false );
state = new StateObject( length );
}
catch (Exception ex)
{
return new OperateResult<byte[]>( ex.Message );
}
try
{
state.WaitDone = receiveDone;
state.WorkSocket = socket;
// Begin receiving the data from the remote device.
socket.BeginReceive( state.Buffer, state.AlreadyDealLength,
state.DataLength - state.AlreadyDealLength, SocketFlags.None,
new AsyncCallback( ReceiveCallback ), state );
}
catch (Exception ex)
{
// 发生了错误,直接返回
LOG.Write( ex ,ToString());
result.Message = ex.Message;
receiveDone.Close( );
socket?.Close( );
return result;
}
// 等待接收完成,或是发生异常
receiveDone.WaitOne( );
receiveDone.Close( );
// 接收数据失败
if (state.IsError)
{
socket?.Close( );
result.Message = state.ErrerMsg;
return result;
}
// 远程关闭了连接
if (state.IsClose)
{
// result.IsSuccess = true;
result.Message = StringResources.Language.RemoteClosedConnection;
socket?.Close( );
return result;
}
// 正常接收到数据
result.Content = state.Buffer;
result.IsSuccess = true;
state.Clear( );
state = null;
return result;
}
private void ReceiveCallback( IAsyncResult ar )
{
if (ar.AsyncState is StateObject state)
{
try
{
Socket client = state.WorkSocket;
int bytesRead = client.EndReceive( ar );
if (bytesRead > 0)
{
// 接收到了数据
state.AlreadyDealLength += bytesRead;
if (state.AlreadyDealLength < state.DataLength)
{
// 获取接下来的所有的数据
client.BeginReceive( state.Buffer, state.AlreadyDealLength,
state.DataLength - state.AlreadyDealLength, SocketFlags.None,
new AsyncCallback( ReceiveCallback ), state );
}
else
{
// 接收到了所有的数据,通知接收数据的线程继续
state.WaitDone.Set( );
}
}
else
{
// 对方关闭了网络通讯
state.IsClose = true;
state.WaitDone.Set( );
}
}
catch (Exception ex)
{
state.IsError = true;
LOG.Write(ex, "ReceiveCallback");
state.ErrerMsg = ex.Message;
state.WaitDone.Set( );
}
}
}
#if !NET35
/// <summary>
/// 接收固定长度的字节数组
/// </summary>
/// <remarks>
/// Receive Special Length Bytes
/// </remarks>
/// <param name="socket">网络通讯的套接字</param>
/// <param name="length">准备接收的数据长度</param>
/// <returns>包含了字节数据的结果类</returns>
protected OperateResult<byte[]> ReceiveAsync( Socket socket, int length )
{
if (length <= 0) return OperateResult.CreateSuccessResult( new byte[0] );
var state = new StateObjectAsync<byte[]>( length );
state.Tcs = new TaskCompletionSource<byte[]>( );
state.WorkSocket = socket;
try
{
socket.BeginReceive( state.Buffer, state.AlreadyDealLength, state.DataLength - state.AlreadyDealLength,
SocketFlags.None, new AsyncCallback( ReceiveAsyncCallback ), state );
byte[] byteResult = state.Tcs.Task.Result;
if (byteResult == null)
{
socket?.Close( );
return new OperateResult<byte[]>( StringResources.Language.RemoteClosedConnection );
}
state.Clear( );
state = null;
return OperateResult.CreateSuccessResult( byteResult );
}
catch (Exception ex)
{
return new OperateResult<byte[]>( ex.Message );
}
}
private void ReceiveAsyncCallback( IAsyncResult ar )
{
if (ar.AsyncState is StateObjectAsync<byte[]> state)
{
try
{
Socket socket = state.WorkSocket;
int bytesRead = socket.EndReceive( ar );
if (bytesRead > 0)
{
// 接收到了数据
state.AlreadyDealLength += bytesRead;
if (state.AlreadyDealLength < state.DataLength)
{
// 获取接下来的所有的数据
socket.BeginReceive( state.Buffer, state.AlreadyDealLength, state.DataLength - state.AlreadyDealLength,
SocketFlags.None, new AsyncCallback( ReceiveAsyncCallback ), state );
}
else
{
// 接收到了所有的数据,通知接收数据的线程继续
state.Tcs.SetResult( state.Buffer );
}
}
else
{
// 对方关闭了网络通讯
state.IsClose = true;
state.Tcs.SetResult( null );
}
}
catch (Exception ex)
{
state.IsError = true;
LOG.Write(ex, "ReceiveAsyncCallback");
state.Tcs.SetException( ex );
}
}
}
#endif
#endregion
#region Receive Message
/// <summary>
/// 接收一条完整的 <seealso cref="INetMessage"/> 数据内容 ->
/// Receive a complete <seealso cref="INetMessage"/> data content
/// </summary>
/// <param name="socket">网络的套接字</param>
/// <param name="timeOut">超时时间</param>
/// <param name="netMessage">消息的格式定义</param>
/// <returns>带有是否成功的byte数组对象</returns>
protected OperateResult<byte[]> ReceiveByMessage( Socket socket, int timeOut, INetMessage netMessage )
{
HslTimeOut hslTimeOut = new HslTimeOut( )
{
DelayTime = timeOut,
WorkSocket = socket,
};
if (timeOut > 0) ThreadPool.QueueUserWorkItem( new WaitCallback( ThreadPoolCheckTimeOut ), hslTimeOut );
// 接收指令头
OperateResult<byte[]> headResult = Receive( socket, netMessage.ProtocolHeadBytesLength );
if (!headResult.IsSuccess)
{
hslTimeOut.IsSuccessful = true;
return headResult;
}
netMessage.HeadBytes = headResult.Content;
int contentLength = netMessage.GetContentLengthByHeadBytes( );
if (contentLength <= 0)
{
hslTimeOut.IsSuccessful = true;
return headResult;
}
OperateResult<byte[]> contentResult = Receive( socket, contentLength );
if (!contentResult.IsSuccess)
{
hslTimeOut.IsSuccessful = true;
return contentResult;
}
hslTimeOut.IsSuccessful = true;
netMessage.ContentBytes = contentResult.Content;
return OperateResult.CreateSuccessResult( SoftBasic.SpliceTwoByteArray( headResult.Content, contentResult.Content ) );
}
#endregion
#region Send Content
/// <summary>
/// 发送消息给套接字,直到完成的时候返回
/// </summary>
/// <param name="socket">网络套接字</param>
/// <param name="data">字节数据</param>
/// <returns>发送是否成功的结果</returns>
protected OperateResult Send( Socket socket, byte[] data )
{
if (data == null) return OperateResult.CreateSuccessResult( );
if (UseSynchronousNet)
{
try
{
socket.Send( data );
return OperateResult.CreateSuccessResult( );
}
catch (Exception ex)
{
socket?.Close( );
LOG.Write(ex, "Send");
return new OperateResult<byte[]>( ex.Message );
}
}
OperateResult result = new OperateResult( );
ManualResetEvent sendDone = null;
StateObject state = null;
try
{
sendDone = new ManualResetEvent( false );
state = new StateObject( data.Length );
}
catch (Exception ex)
{
return new OperateResult( ex.Message );
}
try
{
state.WaitDone = sendDone;
state.WorkSocket = socket;
state.Buffer = data;
socket.BeginSend( state.Buffer, state.AlreadyDealLength, state.DataLength - state.AlreadyDealLength,
SocketFlags.None, new AsyncCallback( SendCallBack ), state );
}
catch (Exception ex)
{
// 发生了错误,直接返回
LOG.Write(ex);
result.Message = ex.Message;
socket?.Close( );
sendDone.Close( );
return result;
}
// 等待发送完成
sendDone.WaitOne( );
sendDone.Close( );
if (state.IsError)
{
socket.Close( );
result.Message = state.ErrerMsg;
return result;
}
state.Clear( );
state = null;
result.IsSuccess = true;
result.Message = StringResources.Language.SuccessText;
return result;
}
/// <summary>
/// 发送数据异步返回的方法
/// </summary>
/// <param name="ar">异步对象</param>
private void SendCallBack( IAsyncResult ar )
{
if (ar.AsyncState is StateObject state)
{
try
{
Socket socket = state.WorkSocket;
int byteSend = socket.EndSend( ar );
state.AlreadyDealLength += byteSend;
if (state.AlreadyDealLength < state.DataLength)
{
// 继续发送数据
socket.BeginSend( state.Buffer, state.AlreadyDealLength, state.DataLength - state.AlreadyDealLength,
SocketFlags.None, new AsyncCallback( SendCallBack ), state );
}
else
{
// 发送完成
state.WaitDone.Set( );
}
}
catch (Exception ex)
{
// 发生了异常
state.IsError = true;
LOG.Write(ex, "SendCallback");
state.ErrerMsg = ex.Message;
state.WaitDone.Set( );
}
}
}
#if !NET35
/// <summary>
/// 发送一个异步的数据信息该方式在NET35里是不可用的。
/// </summary>
/// <param name="socket">网络的套接字</param>
/// <param name="data">数据内容</param>
/// <returns>是否发送成功</returns>
protected OperateResult SendAsync( Socket socket, byte[] data )
{
if (data == null) return OperateResult.CreateSuccessResult( );
if (data.Length == 0) return OperateResult.CreateSuccessResult( );
var state = new StateObjectAsync<bool>( data.Length );
state.Tcs = new TaskCompletionSource<bool>( );
state.WorkSocket = socket;
state.Buffer = data;
try
{
socket.BeginSend( state.Buffer, state.AlreadyDealLength, state.DataLength - state.AlreadyDealLength,
SocketFlags.None, new AsyncCallback( SendAsyncCallBack ), state );
bool boolResult = state.Tcs.Task.Result;
return OperateResult.CreateSuccessResult( );
}
catch (Exception ex)
{
return new OperateResult( ex.Message );
}
}
private void SendAsyncCallBack( IAsyncResult ar )
{
if (ar.AsyncState is StateObjectAsync<bool> state)
{
try
{
Socket socket = state.WorkSocket;
state.AlreadyDealLength += socket.EndSend( ar );
if (state.AlreadyDealLength < state.DataLength)
{
// 继续发送数据
socket.BeginSend( state.Buffer, state.AlreadyDealLength, state.DataLength - state.AlreadyDealLength,
SocketFlags.None, new AsyncCallback( SendAsyncCallBack ), state );
}
else
{
// 发送完成
state.Tcs.SetResult( true );
}
}
catch (Exception ex)
{
state.IsError = true;
LOG.Write(ex, "SendAsyncCallBack");
state.Tcs.SetException( ex );
}
}
}
#endif
#endregion
#region Socket Connect
/// <summary>
/// 创建一个新的socket对象并连接到远程的地址默认超时时间为10秒钟
/// </summary>
/// <param name="ipAddress">Ip地址</param>
/// <param name="port">端口号</param>
/// <returns>返回套接字的封装结果对象</returns>
/// <example>
/// <code lang="cs" source="HslCommunication_Net45.Test\Documentation\Samples\Core\NetworkBase.cs" region="CreateSocketAndConnectExample" title="创建连接示例" />
/// </example>
protected OperateResult<Socket> CreateSocketAndConnect( string ipAddress, int port )
{
return CreateSocketAndConnect( new IPEndPoint( IPAddress.Parse( ipAddress ), port ), 10000 );
}
/// <summary>
/// 创建一个新的socket对象并连接到远程的地址
/// </summary>
/// <param name="ipAddress">Ip地址</param>
/// <param name="port">端口号</param>
/// <param name="timeOut">连接的超时时间</param>
/// <returns>返回套接字的封装结果对象</returns>
/// <example>
/// <code lang="cs" source="HslCommunication_Net45.Test\Documentation\Samples\Core\NetworkBase.cs" region="CreateSocketAndConnectExample" title="创建连接示例" />
/// </example>
protected OperateResult<Socket> CreateSocketAndConnect( string ipAddress, int port, int timeOut )
{
return CreateSocketAndConnect( new IPEndPoint( IPAddress.Parse( ipAddress ), port ), timeOut );
}
/// <summary>
/// 创建一个新的socket对象并连接到远程的地址
/// </summary>
/// <param name="endPoint">连接的目标终结点</param>
/// <param name="timeOut">连接的超时时间</param>
/// <returns>返回套接字的封装结果对象</returns>
/// <example>
/// <code lang="cs" source="HslCommunication_Net45.Test\Documentation\Samples\Core\NetworkBase.cs" region="CreateSocketAndConnectExample" title="创建连接示例" />
/// </example>
protected OperateResult<Socket> CreateSocketAndConnect( IPEndPoint endPoint, int timeOut )
{
if (UseSynchronousNet)
{
var socket = new Socket( AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp );
try
{
HslTimeOut connectTimeout = new HslTimeOut( )
{
WorkSocket = socket,
DelayTime = timeOut
};
ThreadPool.QueueUserWorkItem( new WaitCallback( ThreadPoolCheckTimeOut ), connectTimeout );
socket.Connect( endPoint );
connectTimeout.IsSuccessful = true;
return OperateResult.CreateSuccessResult( socket );
}
catch (Exception ex)
{
socket?.Close( );
LOG.Write(ex, "CreateSocketAndConnect");
return new OperateResult<Socket>( ex.Message );
}
}
else
{
OperateResult<Socket> result = new OperateResult<Socket>( );
ManualResetEvent connectDone = null;
StateObject state = null;
try
{
connectDone = new ManualResetEvent( false );
state = new StateObject( );
}
catch (Exception ex)
{
return new OperateResult<Socket>( ex.Message );
}
var socket = new Socket( AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp );
// 超时验证的信息
HslTimeOut connectTimeout = new HslTimeOut( )
{
WorkSocket = socket,
DelayTime = timeOut
};
ThreadPool.QueueUserWorkItem( new WaitCallback( ThreadPoolCheckTimeOut ), connectTimeout );
try
{
state.WaitDone = connectDone;
state.WorkSocket = socket;
socket.BeginConnect( endPoint, new AsyncCallback( ConnectCallBack ), state );
}
catch (Exception ex)
{
// 直接失败
connectTimeout.IsSuccessful = true; // 退出线程池的超时检查
LOG.Write(ex, ToString()); // 记录错误日志
socket.Close( ); // 关闭网络信息
connectDone.Close( ); // 释放等待资源
result.Message = StringResources.Language.ConnectedFailed + ex.Message; // 传递错误消息
return result;
}
// 等待连接完成
connectDone.WaitOne( );
connectDone.Close( );
connectTimeout.IsSuccessful = true;
if (state.IsError)
{
// 连接失败
result.Message = StringResources.Language.ConnectedFailed + state.ErrerMsg;
socket?.Close( );
return result;
}
result.Content = socket;
result.IsSuccess = true;
state.Clear( );
state = null;
return result;
}
}
/// <summary>
/// 当连接的结果返回
/// </summary>
/// <param name="ar">异步对象</param>
private void ConnectCallBack( IAsyncResult ar )
{
if (ar.AsyncState is StateObject state)
{
try
{
Socket socket = state.WorkSocket;
socket.EndConnect( ar );
state.WaitDone.Set( );
}
catch (Exception ex)
{
// 发生了异常
state.IsError = true;
LOG.Write(ex, "ConnectCallBack");
state.ErrerMsg = ex.Message;
state.WaitDone.Set( );
}
}
}
#if !NET35
private OperateResult<Socket> ConnectAsync( IPEndPoint endPoint, int timeOut )
{
var socket = new Socket( AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp );
var state = new StateObjectAsync<Socket>( );
state.Tcs = new TaskCompletionSource<Socket>( );
state.WorkSocket = socket;
// timeout check
HslTimeOut connectTimeout = new HslTimeOut( )
{
WorkSocket = socket,
DelayTime = timeOut
};
ThreadPool.QueueUserWorkItem( new WaitCallback( ThreadPoolCheckTimeOut ), connectTimeout );
try
{
socket.BeginConnect( endPoint, new AsyncCallback( ConnectAsyncCallBack ), state );
socket = state.Tcs.Task.Result;
return OperateResult.CreateSuccessResult( socket );
}
catch (Exception ex)
{
return new OperateResult<Socket>( ex.Message );
}
}
private void ConnectAsyncCallBack( IAsyncResult ar )
{
if (ar.AsyncState is StateObjectAsync<Socket> state)
{
try
{
Socket socket = state.WorkSocket;
socket.EndConnect( ar );
state.Tcs.SetResult( socket );
}
catch (Exception ex)
{
// 发生了异常
state.IsError = true;
LOG.Write(ex, "ConnectAsyncCallBack");
state.ErrerMsg = ex.Message;
state.Tcs.SetException( ex );
}
}
}
#endif
#endregion
/*****************************************************************************
*
* 说明:
* 下面的两个模块代码指示了如何读写文件
*
********************************************************************************/
#region Read Stream
/// <summary>
/// 读取流中的数据到缓存区
/// </summary>
/// <param name="stream">数据流</param>
/// <param name="buffer">缓冲区</param>
/// <returns>带有成功标志的读取数据长度</returns>
protected OperateResult<int> ReadStream( Stream stream, byte[] buffer )
{
ManualResetEvent WaitDone = new ManualResetEvent( false );
FileStateObject stateObject = new FileStateObject
{
WaitDone = WaitDone,
Stream = stream,
DataLength = buffer.Length,
Buffer = buffer
};
try
{
stream.BeginRead( buffer, 0, stateObject.DataLength, new AsyncCallback( ReadStreamCallBack ), stateObject );
}
catch (Exception ex)
{
LOG.Write(ex, ToString());
stateObject = null;
WaitDone.Close( );
return new OperateResult<int>( );
}
WaitDone.WaitOne( );
WaitDone.Close( );
if (stateObject.IsError)
{
return new OperateResult<int>( )
{
Message = stateObject.ErrerMsg
};
}
else
{
return OperateResult.CreateSuccessResult( stateObject.AlreadyDealLength );
}
}
private void ReadStreamCallBack( IAsyncResult ar )
{
if (ar.AsyncState is FileStateObject stateObject)
{
try
{
stateObject.AlreadyDealLength += stateObject.Stream.EndRead( ar );
stateObject.WaitDone.Set( );
}
catch (Exception ex)
{
LOG.Write(ex, ToString());
stateObject.IsError = true;
stateObject.ErrerMsg = ex.Message;
stateObject.WaitDone.Set( );
}
}
}
#endregion
#region Write Stream
/// <summary>
/// 将缓冲区的数据写入到流里面去
/// </summary>
/// <param name="stream">数据流</param>
/// <param name="buffer">缓冲区</param>
/// <returns>是否写入成功</returns>
protected OperateResult WriteStream( Stream stream, byte[] buffer )
{
ManualResetEvent WaitDone = new ManualResetEvent( false );
FileStateObject stateObject = new FileStateObject
{
WaitDone = WaitDone,
Stream = stream
};
try
{
stream.BeginWrite( buffer, 0, buffer.Length, new AsyncCallback( WriteStreamCallBack ), stateObject );
}
catch (Exception ex)
{
LOG.Write(ex, ToString());
stateObject = null;
WaitDone.Close( );
return new OperateResult( ex.Message );
}
WaitDone.WaitOne( );
WaitDone.Close( );
if (stateObject.IsError)
{
return new OperateResult( )
{
Message = stateObject.ErrerMsg
};
}
else
{
return OperateResult.CreateSuccessResult( );
}
}
private void WriteStreamCallBack( IAsyncResult ar )
{
if (ar.AsyncState is FileStateObject stateObject)
{
try
{
stateObject.Stream.EndWrite( ar );
}
catch (Exception ex)
{
LOG.Write(ex, ToString());
stateObject.IsError = true;
stateObject.ErrerMsg = ex.Message;
}
finally
{
stateObject.WaitDone.Set( );
}
}
}
#endregion
#region Object Override
/// <summary>
/// 返回表示当前对象的字符串
/// </summary>
/// <returns>字符串</returns>
public override string ToString( )
{
return "NetworkBase";
}
#endregion
}
}