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)); } }
/// <summary> /// 读写ModBus服务器的基础方法,支持任意的数据操作 /// </summary> /// <param name="socket">网络服务数据</param> /// <param name="send">发送的字节数据</param> /// <param name="result">结果对象</param> /// <returns></returns> private bool ReadFromModBusSocket(System.Net.Sockets.Socket socket, byte[] send, OperateResult <byte[]> result) { if (!SendBytesToSocket(socket, send, result, "发送数据到服务器失败")) { return(false); } HslTimeOut hslTimeOut = new HslTimeOut(); hslTimeOut.WorkSocket = socket; hslTimeOut.DelayTime = 2000; // 2秒内接收到数据 try { System.Threading.ThreadPool.QueueUserWorkItem(new System.Threading.WaitCallback( ThreadPoolCheckConnect), hslTimeOut); byte[] head = NetSupport.ReadBytesFromSocket(socket, 6); int length = head[4] * 256 + head[5]; byte[] data = NetSupport.ReadBytesFromSocket(socket, length); byte[] buffer = new byte[6 + length]; head.CopyTo(buffer, 0); data.CopyTo(buffer, 6); hslTimeOut.IsSuccessful = true; result.Content = buffer; } catch (Exception ex) { socket?.Close(); result.Message = "从服务器接收结果数据的时候发生错误:" + ex.Message; return(false); } return(true); }
/// <summary> /// 向PLC写入数据,数据格式为原始的字节类型 /// </summary> /// <param name="type">写入的数据类型</param> /// <param name="address">初始地址</param> /// <param name="data">原始的字节数据</param> /// <returns>结果</returns> public OperateResult WriteIntoPLC(MelsecDataType type, ushort address, byte[] data) { OperateResult <byte[]> result = new OperateResult <byte[]>(); byte[] _PLCCommand = GetWriteCommand(type, address, data); Array.Copy(data, 0, _PLCCommand, 21, data.Length); //测试指令数据 //string test = ByteToHexString(_PLCCommand); Socket socket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp); HslTimeOut timeout = new HslTimeOut() { WorkSocket = socket }; try { ThreadPool.QueueUserWorkItem(new WaitCallback(ThreadPoolCheckConnect), timeout); socket.Connect(new IPEndPoint(PLCIpAddress, PortWrite)); timeout.IsSuccessful = true; } catch { result.Message = StringResources.ConnectedFailed; socket.Close(); return(result); } byte[] DataHead = null; try { socket.Send(_PLCCommand); //先接收满9个数据 int NeedReceived = 9; DataHead = NetSupport.ReadBytesFromSocket(socket, NeedReceived); NeedReceived = BitConverter.ToUInt16(DataHead, 7); DataHead = NetSupport.ReadBytesFromSocket(socket, NeedReceived); result.ErrorCode = BitConverter.ToUInt16(DataHead, 0); result.IsSuccess = true; } catch (Exception ex) { result.Message = StringResources.SocketIOException + ex.Message; Thread.Sleep(10); socket.Close(); return(result); } socket.Shutdown(SocketShutdown.Both); Thread.Sleep(10); socket.Close(); socket = null; if (result.ErrorCode > 0) { result.IsSuccess = false; } return(result); }
/// <summary> /// [自校验] 接收一条完整的同步数据,包含头子节和内容字节,基础的数据,如果结果异常,则结束通讯 /// </summary> /// <param name="socket">套接字</param> /// <param name="timeout">超时时间设置,如果为负数,则不检查超时</param> /// <returns></returns> /// <exception cref="ArgumentNullException">result</exception> protected OperateResult <byte[], byte[]> ReceiveAndCheckBytes(Socket socket, int timeout) { // 30秒超时接收验证 HslTimeOut hslTimeOut = new HslTimeOut( ) { DelayTime = timeout, IsSuccessful = false, StartTime = DateTime.Now, WorkSocket = socket, }; if (timeout > 0) { ThreadPool.QueueUserWorkItem(new WaitCallback(ThreadPoolCheckTimeOut), hslTimeOut); } // 接收头指令 OperateResult <byte[]> headResult = Receive(socket, HslProtocol.HeadByteLength); if (!headResult.IsSuccess) { hslTimeOut.IsSuccessful = true; return(OperateResult.CreateFailedResult <byte[], byte[]>(headResult)); } hslTimeOut.IsSuccessful = true; // 检查令牌 if (!CheckRemoteToken(headResult.Content)) { socket?.Close( ); return(new OperateResult <byte[], byte[]>( ) { Message = StringResources.TokenCheckFailed }); } int contentLength = BitConverter.ToInt32(headResult.Content, HslProtocol.HeadByteLength - 4); // 接收内容 OperateResult <byte[]> contentResult = Receive(socket, contentLength); if (!contentResult.IsSuccess) { return(OperateResult.CreateFailedResult <byte[], byte[]>(contentResult)); } // 返回成功信息 OperateResult checkResult = SendLong(socket, HslProtocol.HeadByteLength + contentLength); if (!checkResult.IsSuccess) { return(OperateResult.CreateFailedResult <byte[], byte[]>(checkResult)); } byte[] head = headResult.Content; byte[] content = contentResult.Content; content = HslProtocol.CommandAnalysis(head, content); return(OperateResult.CreateSuccessResult(head, content)); }
/// <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) { var result = new OperateResult <Socket>( ); var connectDone = new ManualResetEvent(false); var state = new StateObject( ); 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; // 退出线程池的超时检查 LogNet?.WriteException(ToString( ), ex); // 记录错误日志 socket.Close( ); // 关闭网络信息 connectDone.Close( ); // 释放等待资源 result.Message = "Connect Failed : " + ex.Message; // 传递错误消息 return(result); } // 等待连接完成 connectDone.WaitOne( ); connectDone.Close( ); connectTimeout.IsSuccessful = true; if (state.IsError) { // 连接失败 result.Message = "Connect Failed : " + state.ErrerMsg; socket?.Close( ); return(result); } result.Content = socket; result.IsSuccess = true; state.Clear( ); state = null; return(result); }
/// <summary> /// 读写ModBus服务器的基础方法,支持任意的数据操作 /// </summary> /// <param name="send">发送的字节数据</param> /// <returns></returns> public OperateResult <byte[]> ReadFromModBusServer(byte[] send) { OperateResult <byte[]> result = new OperateResult <byte[]>(); readModbusLock.Enter(); if (!CreateSocketAndConnect(out System.Net.Sockets.Socket socket, iPEndPoint, result)) { readModbusLock.Leave(); socket = null; return(result); } if (!SendBytesToSocket(socket, send, result, "发送数据到服务器失败")) { readModbusLock.Leave(); return(result); } HslTimeOut hslTimeOut = new HslTimeOut(); hslTimeOut.WorkSocket = socket; hslTimeOut.DelayTime = 5000; // 2秒内接收到数据 try { System.Threading.ThreadPool.QueueUserWorkItem(new System.Threading.WaitCallback( ThreadPoolCheckConnect), hslTimeOut); byte[] head = NetSupport.ReadBytesFromSocket(socket, 6); int length = head[4] * 256 + head[5]; byte[] data = NetSupport.ReadBytesFromSocket(socket, length); byte[] buffer = new byte[6 + length]; head.CopyTo(buffer, 0); data.CopyTo(buffer, 6); hslTimeOut.IsSuccessful = true; result.Content = buffer; } catch (Exception ex) { socket?.Close(); result.Message = "从服务器接收结果数据的时候发生错误:" + ex.Message; readModbusLock.Leave(); return(result); } socket?.Close(); readModbusLock.Leave(); result.IsSuccess = true; return(result); }
/// <summary> /// 检查是否超时的静态方法 /// </summary> /// <param name="timeout">数据封送对象</param> /// <param name="millisecond">超时的时间</param> internal static void ThreadPoolCheckConnect(HslTimeOut timeout, int millisecond) { while (!timeout.IsSuccessful) { if ((DateTime.Now - timeout.StartTime).TotalMilliseconds > millisecond) { // 连接超时或是验证超时 if (!timeout.IsSuccessful) { timeout.WorkSocket?.Close( ); } break; } Thread.Sleep(100); } }
/// <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))); }
/// <summary> /// 从西门子PLC中读取想要的数据,返回结果类说明 /// </summary> /// <param name="type">想要读取的数据类型</param> /// <param name="address">读取数据的起始地址</param> /// <param name="lengh">读取的数据长度</param> /// <returns>返回读取结果</returns> public OperateResult <byte[]> ReadFromPLC(SiemensDataType type, ushort address, ushort lengh) { OperateResult <byte[]> result = new OperateResult <byte[]>(); byte[] _PLCCommand = new byte[16]; _PLCCommand[0] = 0x53; _PLCCommand[1] = 0x35; _PLCCommand[2] = 0x10; _PLCCommand[3] = 0x01; _PLCCommand[4] = 0x03; _PLCCommand[5] = 0x05; _PLCCommand[6] = 0x03; _PLCCommand[7] = 0x08; //指定数据区 _PLCCommand[8] = type.DataCode; _PLCCommand[9] = 0x00; //指定数据地址 _PLCCommand[10] = (byte)(address / 256); _PLCCommand[11] = (byte)(address % 256); //指定数据长度 _PLCCommand[12] = (byte)(lengh / 256); _PLCCommand[13] = (byte)(lengh % 256); _PLCCommand[14] = 0xff; _PLCCommand[15] = 0x02; //超时验证的线程池技术 Socket socket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp); HslTimeOut timeout = new HslTimeOut() { WorkSocket = socket }; try { ThreadPool.QueueUserWorkItem(new WaitCallback(ThreadPoolCheckConnect), timeout); socket.Connect(new IPEndPoint(PLCIpAddress, GetPort())); timeout.IsSuccessful = true; } catch { ChangePort(); result.Message = StringResources.ConnectedFailed; Thread.Sleep(10); socket?.Close(); return(result); } try { //连接成功,发送数据 socket.Send(_PLCCommand); byte[] rec_head = NetSupport.ReadBytesFromSocket(socket, 16); if (rec_head[8] != 0x00) { result.ErrorCode = rec_head[8]; Thread.Sleep(10); socket?.Close(); return(result); } result.Content = NetSupport.ReadBytesFromSocket(socket, lengh); result.IsSuccess = true; result.Message = "读取成功"; } catch (Exception ex) { result.Message = StringResources.SocketIOException + ex.Message; Thread.Sleep(10); socket?.Close(); return(result); } Thread.Sleep(10); socket?.Close(); //所有的数据接收完成,进行返回 return(result); }
/// <summary> /// 向PLC中写入数据,返回值说明 /// </summary> /// <param name="type">要写入的数据类型</param> /// <param name="address">要写入的数据地址</param> /// <param name="data">要写入的实际数据</param> /// <exception cref="ArgumentNullException">写入的数据不能为空</exception> /// <returns>返回写入结果</returns> public OperateResult <byte[]> WriteIntoPLC(SiemensDataType type, ushort address, byte[] data) { var result = new OperateResult <byte[]>(); byte[] _PLCCommand = new byte[16 + data.Length]; _PLCCommand[0] = 0x53; _PLCCommand[1] = 0x35; _PLCCommand[2] = 0x10; _PLCCommand[3] = 0x01; _PLCCommand[4] = 0x03; _PLCCommand[5] = 0x03; _PLCCommand[6] = 0x03; _PLCCommand[7] = 0x08; //指定数据区 _PLCCommand[8] = type.DataCode; _PLCCommand[9] = 0x00; //指定数据地址 _PLCCommand[10] = (byte)(address / 256); _PLCCommand[11] = (byte)(address % 256); //指定数据长度 _PLCCommand[12] = (byte)(data.Length / 256); _PLCCommand[13] = (byte)(data.Length % 256); _PLCCommand[14] = 0xff; _PLCCommand[15] = 0x02; //放置数据 Array.Copy(data, 0, _PLCCommand, 16, data.Length); //超时验证的线程池技术 var socket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp); var timeout = new HslTimeOut() { WorkSocket = socket }; try { ThreadPool.QueueUserWorkItem(new WaitCallback(ThreadPoolCheckConnect), timeout); socket.Connect(new IPEndPoint(PLCIpAddress, PortWrite)); timeout.IsSuccessful = true; } catch { result.Message = StringResources.ConnectedFailed; Thread.Sleep(10); socket?.Close(); return(result); } try { //连接成功,发送数据 socket.Send(_PLCCommand); byte[] rec_head = NetSupport.ReadBytesFromSocket(socket, 16); //分析数据 if (rec_head[8] != 0x00) { result.ErrorCode = rec_head[8]; Thread.Sleep(10); socket?.Close(); return(result); } socket.Shutdown(SocketShutdown.Both); socket.Close(); result.IsSuccess = true; result.Message = "写入成功"; } catch (Exception ex) { result.Message = StringResources.SocketIOException + ex.Message; Thread.Sleep(10); socket?.Close(); return(result); } return(result); }
/// <summary> /// 接收一条完整的数据,使用异步接收完成,包含了指令头信息 /// </summary> /// <param name="socket">已经打开的网络套接字</param> /// <param name="timeOut">超时时间</param> /// <param name="netMsg">消息规则</param> /// <returns>数据的接收结果对象</returns> protected OperateResult <TNetMessage> ReceiveMessage <TNetMessage>(Socket socket, int timeOut, TNetMessage netMsg) where TNetMessage : INetMessage { OperateResult <TNetMessage> result = new OperateResult <TNetMessage>(); // 超时接收的代码验证 HslTimeOut hslTimeOut = new HslTimeOut() { DelayTime = timeOut, WorkSocket = socket, }; if (timeOut > 0) { ThreadPool.QueueUserWorkItem(new WaitCallback(ThreadPoolCheckTimeOut), hslTimeOut); } // 接收指令头 OperateResult <byte[]> headResult = Receive(socket, netMsg.ProtocolHeadBytesLength); if (!headResult.IsSuccess) { hslTimeOut.IsSuccessful = true; result.CopyErrorFromOther(headResult); return(result); } netMsg.HeadBytes = headResult.Content; if (!netMsg.CheckHeadBytesLegal(Token.ToByteArray())) { // 令牌校验失败 hslTimeOut.IsSuccessful = true; socket?.Close(); LogNet?.WriteError(ToString(), StringResources.TokenCheckFailed); result.Message = StringResources.TokenCheckFailed; return(result); } int contentLength = netMsg.GetContentLengthByHeadBytes(); if (contentLength == 0) { netMsg.ContentBytes = new byte[0]; } else { OperateResult <byte[]> contentResult = Receive(socket, contentLength); if (!headResult.IsSuccess) { hslTimeOut.IsSuccessful = true; result.CopyErrorFromOther(contentResult); return(result); } netMsg.ContentBytes = contentResult.Content; } // 防止没有实例化造成后续的操作失败 if (netMsg.ContentBytes == null) { netMsg.ContentBytes = new byte[0]; } hslTimeOut.IsSuccessful = true; result.Content = netMsg; result.IsSuccess = true; return(result); }
/// <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( ); LogNet?.WriteException("CreateSocketAndConnect", ex); return(new OperateResult <Socket>(ex.Message)); } } else { #if NET35 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; // 退出线程池的超时检查 LogNet?.WriteException(ToString( ), ex); // 记录错误日志 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); #else 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)); } #endif } }
/// <summary> /// 从三菱PLC中读取想要的数据,返回读取结果 /// </summary> /// <param name="type">想要读取的数据类型</param> /// <param name="address">读取数据的起始地址</param> /// <param name="length">读取的数据长度,字最大值960,位最大值7168</param> /// <returns>带数据头的字节数组</returns> public OperateResult <byte[]> ReadFromPLC(MelsecDataType type, ushort address, ushort length) { OperateResult <byte[]> result = new OperateResult <byte[]>(); //获取指令 byte[] _PLCCommand = GetReadCommand(type, address, length); Socket socket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp); HslTimeOut timeout = new HslTimeOut() { WorkSocket = socket }; try { ThreadPool.QueueUserWorkItem(new WaitCallback(ThreadPoolCheckConnect), timeout); socket.Connect(new IPEndPoint(PLCIpAddress, GetPort())); timeout.IsSuccessful = true; } catch { ChangePort(); result.Message = StringResources.ConnectedFailed;; socket.Close(); return(result); } byte[] DataHead = null; try { socket.Send(_PLCCommand); //先接收满9个数据 int NeedReceived = 9; DataHead = NetSupport.ReadBytesFromSocket(socket, NeedReceived); NeedReceived = BitConverter.ToUInt16(DataHead, 7); DataHead = NetSupport.ReadBytesFromSocket(socket, NeedReceived); //获取读取代码 result.ErrorCode = BitConverter.ToUInt16(DataHead, 0); result.Content = new byte[DataHead.Length - 2]; Array.Copy(DataHead, 2, result.Content, 0, DataHead.Length - 2); if (type.DataType == 1) { result.Content = ReceiveBytesTranslate(result.Content); } result.IsSuccess = true; } catch (Exception ex) { result.Message = StringResources.SocketIOException + ex.Message; socket.Close(); return(result); } socket.Shutdown(SocketShutdown.Both); socket.Close(); socket = null; if (result.ErrorCode > 0) { result.IsSuccess = false; } return(result); }