[Serialization] Design and Implementation of C # communication (Serial Port and network) framework-5. Unified IO Design of serial port and network,

Source: Internet
Author: User

[Serialization] Design and Implementation of C # communication (Serial Port and network) framework-5. Unified IO Design of serial port and network,

Contents

Chapter V unified serial port and network I/O design... 2

5.1 unified IO Interface... 2

5.1.1 Serial Port IO .. 4

5.1.2 network I/O .. 7

5.1.3 extend the application... 12

5.2 IO manager... 12

5.2.1 serial port I O manager... 13

5.2.2 network I/O manager... 15

5.2.2.1 network listening... 16

5.2.2.2 connect to the remote server... 17

5.2.2.3 mutex operations... 18

5.3 Summary... 19

 

Chapter V unified serial port and network I/O Design

As a communication framework platform software, I/O is one of the core components. It involves information data interaction with hardware devices and software. It consists of two parts: I/O instances and I/O managers. IO instances are directly responsible for serial port and network operations; IO manager is responsible for managing IO instances.

Due to the impact of the application environment, I/O operations have encountered some problems, and it takes a long time to solve some problems. It is not difficult to solve the problem, but cannot determine the cause. After continuous improvement, I/O is gradually stabilized.

5.1 unified IO Interface

A major feature of the Framework platform is the development of a device driver (plug-in) that supports both the serial port and network communication modes, while the switching of the two communication modes only requires modifying the preparation file.

Different device types, protocols, and communication methods are developed using heap code, which cannot be used in different scenarios and increase the code maintenance cost, and modifying the code may cause potential bugs, which is a headache.

When we started designing the Framework Platform, a core idea was to design the changed things flexibly and design the unchanged things stably. The Protocol for the device is changed, and the IO part is relatively unchanged. Therefore, you need to integrate the serial port IO and network IO. Not only must the code be stable, but at the logic level, whether it is Serial Port IO or network IO, It is a unified interface within the framework. All IO operations will be completed through this unified interface.

The unified IO Interface code is as follows:

Public interface IIOChannel: IDisposable {// <summary> // Synchronous lock /// </summary> object SyncLock {get ;} /// <summary> // I/O keyword. For serial communication, the serial port number is used, for example, COM1. for network communication, the IP address and port number are used, for example, 127.0.0.1: 1234 /// </summary> string Key {get ;}/// <summary> // IO channel, which can be COM, it can also be SOCKET // </summary> object IO {get;} // <summary> // read IO; /// </summary> /// <returns> </returns> byte [] ReadIO (); /// <summary> /// write IO /// </summary> int WriteIO (byte [] data ); /// <summary> /// Close /// </summary> void Close (); /// <summary> // IO type /// </summary> CommunicationType IOType {get ;} /// <summary> /// whether it is released /// </summary> bool IsDisposed {get ;}}

Both Serial Port IO and network IO inherit from the IIOChannel interface to complete specific IO communication operations. The inheritance relationship diagram is as follows:

 

5.1.1 Serial Port IO

The original serial port I/O operation uses the SerialPort component provided by MS. However, this component is incompatible with some niche industrial serial port cards, and an exception "Incorrect Parameter" is prompted during the operation. The SerialPort component encapsulates Win32 APIs. Therefore, the analysis should not be a problem of this component. Feedback from some netizens, such:

 

However, from the perspective of the cost of solving the problem, starting with the software is the lowest cost and the most efficient. Based on this consideration, there is no similar problem in using the PCOMM. DLL component of MOXA. Therefore, the PCOMM. DLL component is used in code refactoring and runs stably.

It is relatively simple to perform IO operations on the serial port, mainly implementing the ReadIO and WriteIO interfaces. The Code is as follows:

public class SessionCom : ISessionCom{       ......       public byte[] ReadIO()       {              if (_ReceiveBuffer != null)              {                     int num = InternalRead(_ReceiveBuffer, 0, _ReceiveBuffer.Length);                     if (num > 0)                     {                            byte[] data = new byte[num];                            Buffer.BlockCopy(_ReceiveBuffer, 0, data, 0, data.Length);                            return data;                     }                     else                     {                            return new byte[] { };                     }              }              else              {                     return new byte[] { };              }       }       public int WriteIO(byte[] data)       {              int sendBufferSize = GlobalProperty.GetInstance().ComSendBufferSize;              if (data.Length <= sendBufferSize)              {                     return this.InternalWrite(data);              }              else              {                     int successNum = 0;                     int num = 0;                     while (num < data.Length)                     {                            int remainLength = data.Length - num;                            int sendLength = remainLength >= sendBufferSize                                   ? sendBufferSize                                   : remainLength;                            successNum += InternalWrite(data, num, sendLength);                            num += sendLength;                     }                     return successNum;              }       }       ......}

For ReadIO interface functions, you can perform multiple operations, such as reading a fixed length, determining the end character, and always reading the IO cache as null. Read with a fixed length. If occasional communication interference or data loss occurs, this method will affect the correct reading of data in the future. Judging the end character cannot be universal in the I/O implementation within the Framework; the IO cache is always read as null. If the frequency of receiving data is greater than the frequency of reading data from the IO cache, the polling scheduling thread will be blocked. Based on a wide range of considerations, the field environment is often more complex than imagined. On the basis of setting the read timeout, the system returns the result after one read.

It also takes into account the actual application environment on site, for example, the USB serial port is easy to loosen, resulting in instability and 9-pin serial port damage. Therefore, I/O operations may fail due to changes in the hardware environment. At this time, the TryOpen interface function will be used to re-open the Serial Port IO. In addition, when the serial port parameters change, use the IOSettings interface function to reconfigure parameters.

5.1.2 network I/O

The essence of network I/O communication is Socket operations. The framework platform now supports TCP communication. The work module supports Server and Client, that is, a device driver can be developed to support data Interaction Between Tcp Server and Tcp Client. Currently, UDP communication is not supported and will be improved later.

The code implementation for sending and receiving is relatively simple. ReadIO and WriteIO in the SessionSocket class are implemented in synchronous mode. When concurrent communication and automatic communication mode are used, data is received asynchronously. Of course, you can also use the fully asynchronous programming method to use the SocketAsyncEventArgs operation class. The SessionSocket operation code is implemented as follows:

Public class SessionSocket: ISessionSocket {public byte [] ReadIO () {if (! This. isDisposed) {if (this. acceptedSocket. connected) {if (this. acceptedSocket. poll (10, SelectMode. selectRead) {if (this. acceptedSocket. available> this. acceptedSocket. receiveBufferSize) {throw new Exception ("the received data is greater than the configured receiving buffer size");} # region int num = this. acceptedSocket. receive (this. _ ReceiveBuffer, 0, this. _ ReceiveBuffer. length, SocketFlags. none); if (num <= 0) {throw new SocketException (int) S OcketError. hostDown);} else {this. _ NoneDataCount = 0; byte [] data = new byte [num]; Buffer. blockCopy (_ ReceiveBuffer, 0, data, 0, data. length); return data ;}# endregion} else {this. _ NoneDataCount ++; if (this. _ NoneDataCount> = 60) {this. _ NoneDataCount = 0; throw new SocketException (int) SocketError. hostDown);} else {return new byte [] {};}} else {throw new SocketException (int) SocketErro R. HostDown) ;}} else {return new byte [] {};}} public int WriteIO (byte [] data) {if (! This. isDisposed) {if (this. acceptedSocket. connected & this. acceptedSocket. poll (10, SelectMode. selectWrite) {int successNum = 0; int num = 0; while (num <data. length) {int remainLength = data. length-num; int sendLength = remainLength> = this. acceptedSocket. sendBufferSize? This. acceptedSocket. sendBufferSize: remainLength; SocketError error; successNum + = this. acceptedSocket. send (data, num, sendLength, SocketFlags. none, out error); num + = sendLength; if (successNum <= 0 | error! = SocketError. success) {throw new SocketException (int) SocketError. hostDown) ;}} return successNum;} else {throw new SocketException (int) SocketError. hostDown) ;}} else {return 0 ;}}}

When a Socket failure occurs during the operation of ReadIO and WriteIO, A SocketException exception is thrown. When the Framework Platform captures an exception, the IO instance is destroyed. Listen again passively or actively connect to obtain the Socket instance.

Considering the hardware, the network I/O operation exceptions caused by the PC Nic may be relatively small. However, consider the various terminal (client) hardware devices connected to the Framework platform, such: DTU, wireless routing, and network conversion modules also involve communication links, such as GPRS and 2G/3G/4G. Different hardware features and different communication links, multiple reasons may cause failure of the communication link. For example, the program at the other end is unstable and resources cannot be released; when the line connection is virtualized, the transmission and receiving data are unstable due to the good or bad link conditions. When the network is connected due to a Socket "false", the data is successfully sent, but the other end does not receive it.

For Socket communication, the I/O instance is regularly poll in the original thread, and the heartbeat detection data is sent to the other end through the I/O instance. If the sending fails, the I/O resources are released immediately, the disadvantage of this operation is that the other end will receive some redundant data information. During Reconstruction, the asynchronous function is changed to another method to perform online heartbeat detection on the underlying layer. when data is sent and received asynchronously, if the link is faulty, the asynchronous function will return immediately, the returned results show that the number of sent and received messages is 0. The IO instance resources are destroyed by making judgments. The underlying heartbeat detection function is added when I/O instances are initialized. The Code is as follows:

public SessionSocket(Socket socket){       uint dummy = 0;       _KeepAliveOptionValues = new byte[Marshal.SizeOf(dummy) * 3];       _KeepAliveOptionOutValues = new byte[_KeepAliveOptionValues.Length];       BitConverter.GetBytes((uint)1).CopyTo(_KeepAliveOptionValues, 0);       BitConverter.GetBytes((uint)(2000)).CopyTo(_KeepAliveOptionValues, Marshal.SizeOf(dummy));BitConverter.GetBytes((uint)(GlobalProperty.GetInstance().HeartPacketInterval)).CopyTo(_KeepAliveOptionValues, Marshal.SizeOf(dummy) * 2);       socket.IOControl(IOControlCode.KeepAliveValues, _KeepAliveOptionValues, _KeepAliveOptionOutValues);       socket.NoDelay = true;       socket.SetSocketOption(SocketOptionLevel.Socket, SocketOptionName.DontLinger, true);       ......}

Checks the effectiveness of the Socket IO instance by sending, receiving, throwing, and detecting the underlying heartbeat. Sending and receiving operations in normal communication are simple, but various unexpected situations must be prevented through technical means, thus affecting the stability of the Framework Platform.

Communication is simple, difficult, and difficult. The difficulty level varies with the Application Scenario and environment. Today, with the development of the online world in full swing, network task scheduling, distributed messages, and big data processing all involve information interaction between multiple points and multiple points, therefore, various protocols, algorithms, and data verification have been developed on the basis of communication.

5.1.3 extend the application

I/O design is stable, but it does not mean there is no room for expansion. Specific applications are introduced in "3.7 IO Data Interaction Design" in "3. Design of device drivers. When calling the Send and Receive interfaces of the IRunDevice device driver, I/O instances are passed in as parameters. During secondary development, You can override these two functions, develop specific sending and receiving services.

Some netizens asked: During serial communication, the hardware device has been sending data to the software, and the software analyzes the received data for data processing, using SuperIOHow should we implement it?

This one-way communication method also exists. This type of situation has been taken into account before the framework design. The specific implementation steps are as follows:

5.2 IO Manager

The I/O Manager manages the serial port IO and network I/O instances. They all inherit from the IIOChannelManager interface, but their respective functions are quite different, making the network I/O manager more complicated. The structure of the inheritance relationship is as follows:

5.2.1 serial port I O manager

It is much simpler, because the probability of dynamic changes to the serial port IO is relatively small, but the event is fed back to the serial port monitoring form when I/O is created and I/O is disabled. The main code is as follows:

Public class SessionComManager: IOChannelManager, ISessionComManager <string, IIOChannel> {...... /// <summary> /// create and enable the Serial port IO // </summary> /// <param name = "port"> </param> // <param name = "baud"> </param> // <returns> </returns> public ISessionCom BuildAndOpenComIO (int port, int baud) {ISessionCom com = new SessionCom (port, baud); com. tryOpen (); if (COMOpen! = Null) {bool openSuccess = false; if (com. isOpen) {openSuccess = true;} else {openSuccess = false;} COMOpenArgs args = new COMOpenArgs (port, baud, openSuccess); this. COMOpen (com, args);} return com ;} /// <summary> /// close IO /// </summary> /// <param name = "key"> </param> public override void CloseIO (string key) {ISessionCom com = (ISessionCom) this. getIO (key); base. closeIO (key); if (COMClose! = Null) {bool closeSuccess = false; if (com. isOpen) {closeSuccess = false;} else {closeSuccess = true;} COMCloseArgs args = new COMCloseArgs (com. port, com. baud, closeSuccess); this. COMClose (com, args );}}......}
5.2.2 network I/O manager

The network I/O manager is relatively complex, involves the dynamic connection and disconnection of sockets, And the handling method of switching the connection according to the working mode (Server or Client) set by the device driver. In the past, I was also responsible for conducting heartbeat detection for all network I/O instances through threads. Now this part is replaced by the underlying heartbeat detection.

5.2.2.1 network listening

When listening and receiving a remote connection to the instance, two things will be done:

2. Check whether the current IO manager has the same IP address instance object. If so, the IP address instance object will be destroyed. Because the instance object may have expired, at least the remote client considers the current connection to have expired. Therefore, in this case, both parties reach a consensus to destroy such IP instance objects and receive new IP connection instances.

The code for receiving connection instance objects is as follows:

Private void Monitor_SocketHanler (object source, AcceptSocketArgs e) {IRunDevice [] devs = DeviceManager. getInstance (). getDevices (e. remoteIP, WorkMode. tcpClient); if (devs. length> 0) {DeviceMonitorLog. writeLog (String. format ("{0} has been set to Tcp Client mode, this IP does not support remote active connection", e. remoteIP); SessionSocket. closeSocket (e. socket); return;} CheckSameSessionSocket (e. remoteIP); _ ManualEvent. waitOne (); // If the SOCKET operation is being completed, wait until the completion of the socket operation and then perform the edge operation ISessionSocket SOCKET = new SessionSocket (e. socket); SessionSocketConnect (socket );}
5.2.2.2 connect to a remote server

Open up a thread to obtain the device drivers whose working mode is Client, and check whether the communication parameters of each device driver exist in the IO manager. If the communication parameters of each device driver do not exist, then, connect to the remote server. After the connection is successful, connect the connected IO instance to the IO manager.

The implementation code is as follows:

private void ConnectTarget(){       while (true)       {              if (!_ConnectThreadRun)              {                     break;              }              IRunDevice[] devList = DeviceManager.GetInstance().GetDevices(WorkMode.TcpClient);              for (int i = 0; i < devList.Length; i++)              {                     try                     {                            if (!this.ContainIO(devList[i].DeviceParameter.NET.RemoteIP))                            {                                   ConnectServer(devList[i].DeviceParameter.NET.RemoteIP, devList[i].DeviceParameter.NET.RemotePort);                            }                     }                     catch (Exception ex)                     {                            devList[i].OnDeviceRuningLogHandler(ex.Message);                     }              }              System.Threading.Thread.Sleep(2000);       }}
5.2.2.3 mutex operation

When a new connection is established to check whether the same IP address instance exists, if the same IP address instance exists, you cannot place the newly connected IP instance in the IO manager. The two instances with the same IP address may destroy the newly connected IP address together when resources are destroyed or when resources are created.

To prevent this situation, use the ManualResetEvent signal to control and change the status. The Code is as follows:

Public class SessionSocketManager: IOChannelManager, ISessionSocketManager <string, IIOChannel >{/// <summary> /// the initial status is termination. /// </summary> private ManualResetEvent _ ManualEvent = new ManualResetEvent (true ); private void Monitor_SocketHanler (object source, AcceptSocketArgs e) {SessionSocketClose (e. remoteIP); _ ManualEvent. waitOne (); // If the SOCKET operation is being completed, wait until the completion of the socket operation and then perform the edge operation ISessionSocket SOCKET = new SessionS Ocket (e. socket); SessionSocketConnect (socket);} private void SessionSocketClose (string key) {this. _ ManualEvent. reset (); // non-terminated state SessionSocket io = (SessionSocket) GetIO (key); if (io! = Null) {CloseIO (key);} this. _ ManualEvent. Set (); // termination status} private void SessionSocketConnect (ISessionSocket socket) {if (! This. ContainIO (socket. Key. ToString () {this. AddIO (socket. Key. ToString (), (IIOChannel) socket );}}}
Conclusion 5.3

The idea of IO Design is that one is responsible for executing a responsible management, the IO instance is a specific channel operation, the IO manager is responsible for managing IO, and coordinating the relationship and work between devices and IO.

 

Author: Wei Xiaozhi

Email: 504547114@qq.com

QQ: 504547114

. NET Development Technology Alliance: 54256083

Document Download: http://pan.baidu.com/s/1pJ7lZWf

Http://www.bmpj.net

Related Article

Contact Us

The content source of this page is from Internet, which doesn't represent Alibaba Cloud's opinion; products and services mentioned on that page don't have any relationship with Alibaba Cloud. If the content of the page makes you feel confusing, please write us an email, we will handle the problem within 5 days after receiving your email.

If you find any instances of plagiarism from the community, please send an email to: info-contact@alibabacloud.com and provide relevant evidence. A staff member will contact you within 5 working days.

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.