超级消息转换器

之前实现的检验虚拟盒子虽然实现了串口转TCP功能,但是缺乏设计,表现为设计太僵硬,只能固定两三种转换模式。而且随着转换增加复杂度太大。比如要实现某些仪器TCP不能跨网站做TCP直接跳板就难度大大增加。

虚拟盒子体现为代码力,这次体现为设计力。

源码

虚拟盒子如下:
在这里插入图片描述
新效果-TCP跳转示例
在这里插入图片描述
其他测试
在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

为了解决设计不足导致的扩展困难,全新设计了超级转换器,分享初版代码。这次抽取了转换器接口,和提取了委托。实现为一个dll,方便其他exe快速引入。

拿串口端来说,本质就是1.监控串口发来的消息,然后转发出去。2.把其他端发来的消息推送到串口。

拿tcp端来说,本质就是1.监控tcp端发来的消息,然后转发出去。2.把其他端发来的消息转发给tcp。

默认实现了串口、tcp服务端、tcp客户端、udp服务端、udp客户端。也可以实现接口来实现读写文本,调用webservice等,只要是需要转发消息的都可。

抽取接口如下(所有的消息实现端实现该接口):

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace MessageSwitch
{
    ///<summary  NoteObject="Class">
    /// [功能描述:消息转换器接口,所有消息用户控件实现该接口,符合该接口的实现可以配置到转接中心两端] <para/>
    /// [创建者:zlz] <para/>
    /// [创建时间:2021年11月13日] <para/>
    ///<说明>
    ///  [说明:消息转换器接口,所有消息用户控件实现该接口,符合该接口的实现可以配置到转接中心两端]<para/>
    ///</说明>
    ///<修改记录>
    ///    [修改时间:本次修改时间]<para/>
    ///    [修改内容:本次修改内容]<para/>
    ///</修改记录>
    ///<修改记录>
    ///    [修改时间:本次修改时间]<para/>
    ///    [修改内容:本次修改内容]<para/>
    ///</修改记录>
    ///</summary>
    public interface IMessageSwitch
    {
        /// <summary>
        /// 得到实现类型的名字,供写日志用
        /// </summary>
        /// <returns></returns>
        string GetTypeName();

        /// <summary>
        /// 得到配置串
        /// </summary>
        /// <returns>配置串</returns>
        string GetConfig();


        /// <summary>
        /// 设置配置,供主程序把配置设置上去
        /// </summary>
        /// <param name="confStr">配置串</param>
        /// <returns>是否成功</returns>
        bool SetConfig(string confStr);

        /// <summary>
        /// 供转换中心设置回调方法给消息实现
        /// </summary>
        /// <param name="push">推送器</param>
        /// <param name="type">推送端来源</param>
        /// <param name="log">写日志委托</param>
        /// <returns>是否成功</returns>
        bool SetPushDelegate(PushMessageToSwitch push, SwitchType type, WriteLog log);


        /// <summary>
        /// 启动
        /// </summary>
        /// <returns>是否成功</returns>
        bool Start();

        /// <summary>
        /// 停止
        /// </summary>
        /// <returns>是否成功</returns>
        bool Stop();

        /// <summary>
        /// 提供转换中心给改接口推送消息
        /// </summary>
        /// <param name="msgByteArr">消息的比特数组</param>
        /// <returns>是否成功</returns>
        bool SendMessage(byte[] msgByteArr);

        /// <summary>
        /// 得到该对象的反射配置串,供主程序按配置加载,格式:类全名,动态库名字(不带dll)
        /// </summary>
        /// <returns></returns>
        string GetReflectStr();

    }
}

转换器及委托实现转换逻辑

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Reflection;

namespace MessageSwitch
{
    /// <summary>
    /// 交换类型,标识消息实现所在的端
    /// </summary>
    public enum SwitchType
    {
        In,
        Out
    }

    /// <summary>
    /// 把收到的消息推送到转换器委托,所有消息接口通过该委托把消息推送到转换中心
    /// </summary>
    /// <param name="msgByteArr">消息</param>
    /// <param name="type">推送端来源</param>
    /// <returns>是否成功</returns>
    public delegate bool PushMessageToSwitch(byte[] msgByteArr,SwitchType type);

    /// <summary>
    /// 写日志委托
    /// </summary>
    /// <param name="msg">消息</param>
    public delegate void WriteLog(string msg);

    /// <summary>
    /// 添加转换器
    /// </summary>
    /// <param name="sw"></param>
    public delegate void AddMessageSwitch(IMessageSwitch sw,SwitchType type);


    ///<summary  NoteObject="Class">
    /// [功能描述:消息转换器核心,实现消息转换] <para/>
    /// [创建者:zlz] <para/>
    /// [创建时间:2021年11月13日] <para/>
    ///<说明>
    ///  [说明:消息转换器核心,实现消息转换]<para/>
    ///</说明>
    ///<修改记录>
    ///    [修改时间:本次修改时间]<para/>
    ///    [修改内容:本次修改内容]<para/>
    ///</修改记录>
    ///<修改记录>
    ///    [修改时间:本次修改时间]<para/>
    ///    [修改内容:本次修改内容]<para/>
    ///</修改记录>
    ///</summary>
    public class SwitchMian
    {
        /// <summary>
        /// 输入端接口集合
        /// </summary>
        private List<IMessageSwitch> inList = new List<IMessageSwitch>();

        /// <summary>
        /// 输出端消息集合
        /// </summary>
        private List<IMessageSwitch> outList = new List<IMessageSwitch>();

        /// <summary>
        /// 推送消息委托
        /// </summary>
        private PushMessageToSwitch PushMessage;

        /// <summary>
        /// 写日志委托
        /// </summary>
        private WriteLog log;


        /// <summary>
        /// 构造函数
        /// </summary>
        public SwitchMian(WriteLog logW)
        {
            PushMessage = new PushMessageToSwitch(PushMessageToSwitchDo);
            log = logW;
        }

        /// <summary>
        /// 接收推过来的消息
        /// </summary>
        /// <param name="msgByteArr">消息</param>
        /// <param name="type">来源</param>
        /// <returns>是否成功</returns>
        private bool PushMessageToSwitchDo(byte[] msgByteArr, SwitchType type)
        {
            string lineStr = System.Text.Encoding.Default.GetString(msgByteArr);
            //处理不可见字符为可见字符
            lineStr = Util.DealNotSeeToSeeChar(lineStr);
            //消息来自In就给所有Out推送
            if (type == SwitchType.In)
            {
                if (outList != null && outList.Count > 0)
                {
                    foreach (IMessageSwitch s in outList)
                    {
                        if (log != null)
                        {
                            log("输入->输出"+lineStr);
                        }
                        s.SendMessage(msgByteArr);
                    }
                }
            }
            //消息来自Out就给所有In推送
            if (type == SwitchType.Out)
            {
                if (inList != null && inList.Count > 0)
                {
                    foreach (IMessageSwitch s in inList)
                    {
                        if (log != null)
                        {
                            log("输出->输入" + lineStr);
                        }
                        s.SendMessage(msgByteArr);
                    }
                }
            }
            return true;
        }

        

        /// <summary>
        /// 添加消息实现到输入端
        /// </summary>
        /// <param name="obj">消息实现</param>
        public void AddToIn(IMessageSwitch obj)
        {
            if (log != null)
            {
                log(obj.GetTypeName()+"加入输入端");
            }
            obj.SetPushDelegate(PushMessage, SwitchType.In,log);
            inList.Add(obj);
        }

        /// <summary>
        /// 添加消息实现到输出端
        /// </summary>
        /// <param name="obj">消息实现</param>
        public void AddToOut(IMessageSwitch obj)
        {
            if (log != null)
            {
                log(obj.GetTypeName() + "加入输出端");
            }
            obj.SetPushDelegate(PushMessage, SwitchType.Out,log);
            outList.Add(obj);
        }

        /// <summary>
        /// 得到配置
        /// </summary>
        /// <returns>配置串</returns>
        public string  GetConfig()
        {
            List<ConfDto> confList = new List<ConfDto>();
            if (inList != null && inList.Count > 0)
            {
                foreach (IMessageSwitch s in inList)
                {
                    if (log != null)
                    {
                        log("获取:" + s.GetTypeName()+"配置");
                    }
                    ConfDto dto = new ConfDto();
                    dto.ConfStr = s.GetConfig();
                    dto.ReflectStr = s.GetReflectStr();
                    dto.Type = SwitchType.In;
                    confList.Add(dto);
                }
            }
            if (outList != null && outList.Count > 0)
            {
                foreach (IMessageSwitch s in outList)
                {
                    if (log != null)
                    {
                        log("获取:" + s.GetTypeName() + "配置");
                    }
                    ConfDto dto = new ConfDto();
                    dto.ConfStr = s.GetConfig();
                    dto.ReflectStr = s.GetReflectStr();
                    dto.Type = SwitchType.Out;
                    confList.Add(dto);
                }
            }
            return Util.Serializer(confList.GetType(), confList);
        }

        /// <summary>
        /// 设置配置
        /// </summary>
        /// <param name="confStr">配置串</param>
        /// <param name="addSw">回调方法</param>
        public void LoadConfig(string confStr,AddMessageSwitch addSw)
        {
            List<ConfDto> confList = Util.XmlDeserialize<List<ConfDto>>(confStr, Encoding.Default);
            if (confList != null && confList.Count > 0)
            {
                foreach (var v in confList)
                {
                    if (v.Type == SwitchType.In)
                    {
                        object obj = GetObjectByReflectStr(v.ReflectStr);
                        if (obj != null)
                        {
                            IMessageSwitch swi = obj as IMessageSwitch;
                            if (swi != null)
                            {
                                swi.SetConfig(v.ConfStr);
                                if (log != null)
                                {
                                    log("载入:" + swi.GetTypeName() + "对象到输入端");
                                }
                                AddToIn(swi);
                                //执行回调
                                if (addSw != null)
                                {
                                    addSw(swi, SwitchType.In);
                                }
                            }
                        }
                    }
                    if (v.Type == SwitchType.Out)
                    {
                        object obj = GetObjectByReflectStr(v.ReflectStr);
                        if (obj != null)
                        {
                            IMessageSwitch swi = obj as IMessageSwitch;
                            if (swi != null)
                            {
                                swi.SetConfig(v.ConfStr);
                                if (log != null)
                                {
                                    log("载入:" + swi.GetTypeName() + "对象到输出端");
                                }
                                AddToOut(swi);
                                //执行回调
                                if (addSw != null)
                                {
                                    addSw(swi, SwitchType.Out);
                                }
                            }
                        }
                    }
                }
            }
        }

        /// <summary>
        /// 通过反射串得到对象
        /// </summary>
        /// <param name="reflectStr">反射串</param>
        /// <returns></returns>
        public object GetObjectByReflectStr(string reflectStr)
        {
            try
            {
                if (log != null)
                {
                    log("准备解析处理对象:" + reflectStr);
                }
                string[] strArr = reflectStr.Replace(" ", "").Split(',');
                if (strArr.Length < 2)
                {
                    if (log != null)
                    {
                        log("配置的处理对象错误,请采用类全名,动态库名");
                    }
                }
                if ((strArr.Length) < 2)
                {
                    return null;
                }
                string path = System.IO.Path.Combine(System.AppDomain.CurrentDomain.BaseDirectory,strArr[1]);
                if (!path.Contains(".dll"))
                {
                    path = path + ".dll";
                }
                Assembly assembly = Assembly.LoadFile(path);
                object obj = assembly.CreateInstance(strArr[0], false);
                if (log != null)
                {
                    log("解析处理对象:" + reflectStr + "成功");
                }
                return obj;
            }
            catch (Exception ex)
            {
                if (log != null)
                {
                    log("解析处理对象出错:" + ex.Message);
                }
                return null;
            }
        }

        /// <summary>
        /// 开启转换器
        /// </summary>
        public void StartSwitch()
        {
            if (inList != null && inList.Count > 0)
            {
                foreach (IMessageSwitch s in inList)
                {
                    if (log != null)
                    {
                        log("开启输输入端:" + s.GetTypeName());
                    }
                    s.Start();
                }
            }
            if (outList != null && outList.Count > 0)
            {
                foreach (IMessageSwitch s in outList)
                {
                    if (log != null)
                    {
                        log("开启输出端:" + s.GetTypeName());
                    }
                    s.Start();
                }
            }
        }

        /// <summary>
        /// 停止转换器
        /// </summary>
        public void StopSwitch()
        {
            if (inList != null && inList.Count > 0)
            {
                foreach (IMessageSwitch s in inList)
                {
                    if (log != null)
                    {
                        log("停止输输入端:" + s.GetTypeName());
                    }
                    s.Stop();
                }
            }
            if (outList != null && outList.Count > 0)
            {
                foreach (IMessageSwitch s in outList)
                {
                    if (log != null)
                    {
                        log("停止输出端:" + s.GetTypeName());
                    }
                    s.Stop();
                }
            }
        }
    }

    /// <summary>
    /// 配置实体
    /// </summary>
    public class ConfDto
    {
        /// <summary>
        /// 反射用的串
        /// </summary>
        public string ReflectStr
        {
            get;
            set;
        }

        /// <summary>
        /// 类型
        /// </summary>
        public SwitchType Type
        {
            get;
            set;
        }

        /// <summary>
        /// 配置串
        /// </summary>
        public string ConfStr
        {
            get;
            set;
        }
    }

}

主转换器用户控件,主转换器界面实现
在这里插入图片描述

using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Drawing;
using System.Data;
using System.Linq;
using System.Text;
using System.Windows.Forms;

namespace MessageSwitch
{
    public partial class SwitchMainUI : UserControl
    {
        /// <summary>
        /// 转换器主对象
        /// </summary>
        private SwitchMian switchMian =null;

        /// <summary>
        /// 输入控件顶部
        /// </summary>
        private int InTop = 0;

        /// <summary>
        /// 输出控件顶部
        /// </summary>
        private int OutTop = 0;

        public SwitchMainUI()
        {
            InitializeComponent();
        }


        /// <summary>
        /// 添加输入端
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void btnAddIn_Click(object sender, EventArgs e)
        {
            object reflectObj = cmbType.SelectedValue;
            if (reflectObj == null)
            {
                MessageBox.Show("请选择要添加的类型!", "提示");
                return;
            }
            object swiObj = switchMian.GetObjectByReflectStr(reflectObj.ToString());
            IMessageSwitch swi = swiObj as IMessageSwitch;
            UserControl swiu = swiObj as UserControl;
            swiu.Top = InTop;
            InTop += swiu.Height+10;
            tabPageIn.Controls.Add(swiu);
            switchMian.AddToIn(swi);
        }

        /// <summary>
        /// 添加输出端
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void btnAddOut_Click(object sender, EventArgs e)
        {
            object reflectObj = cmbType.SelectedValue;
            if (reflectObj == null)
            {
                MessageBox.Show("请选择要添加的类型!", "提示");
                return;
            }
            object swiObj = switchMian.GetObjectByReflectStr(reflectObj.ToString());
            IMessageSwitch swi = swiObj as IMessageSwitch;
            UserControl swiu = swiObj as UserControl;
            swiu.Top = OutTop;
            OutTop += swiu.Height+10;
            tabPageOut.Controls.Add(swiu);
            switchMian.AddToOut(swi);
        }

        /// <summary>
        /// 保存配置
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void btnSaveConfig_Click(object sender, EventArgs e)
        {
            string confStr=switchMian.GetConfig();
            string path = System.IO.Path.Combine(System.AppDomain.CurrentDomain.BaseDirectory, "SwitchMian.conf");
            Util.WriteTxt(path, confStr);
            MessageBox.Show("配置保存在SwitchMian.conf!", "提示");
        }

        /// <summary>
        /// 启动转换
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void btnStart_Click(object sender, EventArgs e)
        {
            switchMian.StartSwitch();
        }

        /// <summary>
        /// 停止转换
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void btnStop_Click(object sender, EventArgs e)
        {
            switchMian.StopSwitch();
        }

        /// <summary>
        /// 加载方法
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void SwitchMainUI_Load(object sender, EventArgs e)
        {
            switchMian = new SwitchMian(new WriteLog(WritLogs));
            string path = System.IO.Path.Combine(System.AppDomain.CurrentDomain.BaseDirectory, "SwitchType.conf");
            string TypeConfStr = Util.ReadTxt(path);
            if (TypeConfStr != "")
            {
                TypeConfStr = TypeConfStr.Replace("rn",(char)0+"");
                string[] arr = TypeConfStr.Split((char)0);
                List<CmbDto> cmbList = new List<CmbDto>();
                foreach(string s in arr)
                {
                    if (s == "")
                    {
                        continue;
                    }
                    string[] arrOne = s.Split('^');
                    if (arrOne.Length != 2)
                    {
                        MessageBox.Show("配置格式不对!格式:名称^反射串", "提示");
                    }
                    else
                    {
                        CmbDto dto = new CmbDto();
                        dto.Name = arrOne[0];
                        dto.ReflectStr = arrOne[1];
                        cmbList.Add(dto);
                    }
                }
                cmbType.DataSource = cmbList;
                cmbType.ValueMember = "ReflectStr";
                cmbType.DisplayMember = "Name";
            }
            string pathConf = System.IO.Path.Combine(System.AppDomain.CurrentDomain.BaseDirectory, "SwitchMian.conf");
            if(System.IO.File.Exists(pathConf))
            {
                string conf=Util.ReadTxt(pathConf);
                switchMian.LoadConfig(conf, new AddMessageSwitch(AddMessageSwitchDo));
                switchMian.StartSwitch();
            }
        }

        /// <summary>
        /// 添加转换器
        /// </summary>
        /// <param name="sw"></param>
        public  void AddMessageSwitchDo(IMessageSwitch sw,SwitchType type)
        {
            UserControl swiu = sw as UserControl;
            if (type == SwitchType.In)
            {
                swiu.Top = InTop;
                InTop += swiu.Height + 10;
                tabPageIn.Controls.Add(swiu);
            }
            if (type == SwitchType.Out)
            {
                swiu.Top = OutTop;
                OutTop += swiu.Height + 10;
                tabPageOut.Controls.Add(swiu);
            }
        }


        /// <summary>
        /// 写日志
        /// </summary>
        /// <param name="log"></param>
        private void WritLogs(string log)
        {
            object [] para=new object[1];
            para[0]=log;
            this.Invoke(new WriteLog(WritLogsDo), para);
        }

        /// <summary>
        /// 写日志
        /// </summary>
        /// <param name="log"></param>
        private void WritLogsDo(string log)
        {
            txtInfo.Text += DateTime.Now.ToString("HH:mm:ss ")+ log + "n";
        }

        /// <summary>
        /// 下拉框实体
        /// </summary>
        class CmbDto
        {
            /// <summary>
            /// 显示名称
            /// </summary>
            public string Name
            {
                get;
                set;
            }

            /// <summary>
            /// 反射串
            /// </summary>
            public string ReflectStr
            {
                get;
                set;
            }
        }
    }
}

串口实现示例,对接串口消息
在这里插入图片描述

using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Drawing;
using System.Data;
using System.Linq;
using System.Text;
using System.Windows.Forms;
using System.IO.Ports;

namespace MessageSwitch
{
    ///<summary  NoteObject="Class">
    /// [功能描述:串口实现] <para/>
    /// [创建者:zlz] <para/>
    /// [创建时间:2021年11月13日] <para/>
    ///<说明>
    ///  [说明:串口实现]<para/>
    ///</说明>
    ///<修改记录>
    ///    [修改时间:本次修改时间]<para/>
    ///    [修改内容:本次修改内容]<para/>
    ///</修改记录>
    ///<修改记录>
    ///    [修改时间:本次修改时间]<para/>
    ///    [修改内容:本次修改内容]<para/>
    ///</修改记录>
    ///</summary>
    public partial class UCSerialPort : UserControl, IMessageSwitch
    {
        /// <summary>
        /// 串口对象
        /// </summary>
        private SerialPort comm = new SerialPort();

        /// <summary>
        /// 推送器
        /// </summary>
        private PushMessageToSwitch pushToSwitch;

        /// <summary>
        /// 类型
        /// </summary>
        SwitchType type;

        /// <summary>
        /// 写日志委托
        /// </summary>
        private WriteLog log;

        public UCSerialPort()
        {
            InitializeComponent();
        }

        /// <summary>
        /// 得到类型名字
        /// </summary>
        /// <returns></returns>
        public string GetTypeName()
        {
            return "串口";
        }

        /// <summary>
        /// 得到配置
        /// </summary>
        /// <returns></returns>
        public string GetConfig()
        {
            string confStr = cmbPortName.Text + "#" + cmbBaudrate.Text + "#" + cmbDataBits.Text + "#" + cmbStopBits.Text + "#" + cmbParity.Text;
            return confStr;
        }

        /// <summary>
        /// 设置配置
        /// </summary>
        /// <param name="confStr"></param>
        /// <returns></returns>
        public bool SetConfig(string confStr)
        {
            string[] arr = confStr.Split('#');
            if (arr.Length != 5)
            {
                MessageBox.Show("串口配置格式不对!应该是#分隔的5位", "提示");
                return false;
            }
            cmbPortName.Text = arr[0];
            cmbBaudrate.Text = arr[1];
            cmbDataBits.Text = arr[2];
            cmbStopBits.Text = arr[3];
            cmbParity.Text = arr[4];
            return true;
        }

        /// <summary>
        /// 设置推送委托
        /// </summary>
        /// <param name="push">推送器</param>
        /// <param name="ptype">类型</param>
        /// <param name="plog">日志委托</param>
        /// <returns></returns>
        public bool SetPushDelegate(PushMessageToSwitch push, SwitchType ptype, WriteLog plog)
        {
            pushToSwitch = push;
            type = ptype;
            log = plog;
            return true;
        }

        /// <summary>
        /// 启动串口
        /// </summary>
        /// <returns></returns>
        public bool Start()
        {
            //根据当前串口对象,来判断操作
            if (comm.IsOpen)
            {
                //打开时点击,则关闭串口
                comm.Close();
            }
            //关闭时点击,则设置好端口,波特率后打开
            comm.PortName = cmbPortName.Text;
            comm.BaudRate = int.Parse(cmbBaudrate.Text);
            comm.DataBits = int.Parse(cmbDataBits.Text);
            //停止位为枚举类型非用户自定义 
            switch (cmbStopBits.Text)
            {
                default:
                    comm.StopBits = StopBits.None;
                    break;
                case "1":
                    comm.StopBits = StopBits.One;
                    break;
                case "2":
                    comm.StopBits = StopBits.Two;
                    break;
                case "1.5":
                    comm.StopBits = StopBits.OnePointFive;
                    break;
            }
            //校验位同样非用户自定义 
            switch (cmbParity.Text)
            {
                default:
                    comm.Parity = Parity.None;
                    break;
                case "Odd":
                    comm.Parity = Parity.Odd;
                    break;
                case "Even":
                    comm.Parity = Parity.Even;
                    break;
                case "Mark":
                    comm.Parity = Parity.Mark;
                    break;
                case "Space":
                    comm.Parity = Parity.Space;
                    break;
            }
            try
            {
                comm.Open();
                if (log != null)
                {
                    log("启动:启动串口成功");
                    statePortName.ForeColor = Color.Blue;
                }
            }
            catch (Exception ex)
            {
                statePortName.ForeColor = Color.Red;
                //捕获到异常信息,创建一个新的comm对象,之前的不能用了。
                comm = new SerialPort();
                //初始化SerialPort对象
                comm.NewLine = "rn";
                comm.RtsEnable = true;//根据实际情况吧。
                //添加事件注册
                comm.DataReceived += cmb_DataReceived;
                //现实异常信息给客户。                    
                MessageBox.Show(ex.Message, "提示", MessageBoxButtons.OK);
                return false;
            }
            return true;
        }

        /// <summary>
        /// 停止串口
        /// </summary>
        /// <returns></returns>
        public bool Stop()
        {
            if (comm.IsOpen)
            {
                comm.Close();
                statePortName.ForeColor = Color.Red;
                if (log != null)
                {
                    log("停止:串口已关闭");
                    statePortName.ForeColor = Color.Blue;
                }
            }
            return true;
        }

        /// <summary>
        /// 给串口发消息
        /// </summary>
        /// <param name="msgByteArr"></param>
        /// <returns></returns>
        public bool SendMessage(byte[] msgByteArr)
        {
            //串口打开的话就转给串口
            if (comm.IsOpen)
            {
                //转发给串口
                comm.Write(msgByteArr, 0, msgByteArr.Count());
            }
            else
            {
                if (log != null)
                {
                    log("串口未打开,无法发送");
                    statePortName.ForeColor = Color.Blue;
                }
            }
            return true;
        }

        /// <summary>
        /// 得到反射串
        /// </summary>
        /// <returns></returns>
        public string GetReflectStr()
        {
            return "MessageSwitch.UCSerialPort,MessageSwitch.dll";
        }

        /// <summary>
        /// 加载函数
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void UCSerialPort_Load(object sender, EventArgs e)
        {
            //初始化下拉串口名称列表框
            string[] ports = SerialPort.GetPortNames();
            //没串口提示
            if (ports == null || ports.Count() == 0)
            {
                MessageBox.Show("检测到当前运行电脑没有串口!");
            }
            Array.Sort(ports);
            cmbPortName.Items.AddRange(ports);
            //波特率
            string[] baudrates = { "1200", "2400", "4800", "9600", "19200", "38400", "57600", "115200" };
            cmbBaudrate.Items.AddRange(baudrates);
            //数据位
            string[] databits = { "8", "7", "6", "5", "4" };
            cmbDataBits.Items.AddRange(databits);
            //停止位
            string[] stopbits = { "1", "1.5", "2" };
            cmbStopBits.Items.AddRange(stopbits);
            //校验位
            string[] parity = { "None", "Odd", "Even", "Mark", "Speace" };
            cmbParity.Items.AddRange(parity);

            //默认值设置
            cmbPortName.SelectedIndex = cmbPortName.Items.Count > 0 ? 0 : -1;
            cmbBaudrate.SelectedIndex = cmbBaudrate.Items.IndexOf("9600");
            cmbDataBits.SelectedIndex = cmbDataBits.Items.IndexOf("8");
            cmbStopBits.SelectedIndex = cmbStopBits.Items.IndexOf("1");
            cmbParity.SelectedIndex = cmbParity.Items.IndexOf("None");
            //下拉框只可选择不可编辑
            cmbPortName.DropDownStyle = ComboBoxStyle.DropDownList;
            cmbBaudrate.DropDownStyle = ComboBoxStyle.DropDownList;
            cmbDataBits.DropDownStyle = ComboBoxStyle.DropDownList;
            cmbStopBits.DropDownStyle = ComboBoxStyle.DropDownList;
            cmbParity.DropDownStyle = ComboBoxStyle.DropDownList;

            //初始化SerialPort对象
            comm.NewLine = "rn";
            //根据实际情况吧。
            comm.RtsEnable = true;
            //添加事件注册
            comm.DataReceived += cmb_DataReceived;
        }

        /// <summary>
        /// Com口接收数据
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        void cmb_DataReceived(object sender, SerialDataReceivedEventArgs e)
        {
            int n = comm.BytesToRead;//先记录下来,避免某种原因,人为的原因,操作几次之间时间长,缓存不一致
            byte[] buf = new byte[n];//声明一个临时数组存储当前来的串口数据
            comm.Read(buf, 0, n);//读取缓冲数据
            string lineStr = System.Text.Encoding.Default.GetString(buf);
            lineStr = Util.DealNotSeeToSeeChar(lineStr);
            if (pushToSwitch != null)
            {
                pushToSwitch(buf, type);
            }


        }

        /// <summary>
        /// 启动
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void btnStart_Click(object sender, EventArgs e)
        {
            Start();
        }

        /// <summary>
        /// 停止
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void btnStop_Click(object sender, EventArgs e)
        {
            Stop();
        }
    }
}

TCP服务端实现示例,实现TCP服务端接收客户端消息
在这里插入图片描述

using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Drawing;
using System.Data;
using System.Linq;
using System.Text;
using System.Windows.Forms;
using System.Net.Sockets;
using System.Net;
using System.Threading;

namespace MessageSwitch
{
    ///<summary  NoteObject="Class">
    /// [功能描述:TCP服务端实现] <para/>
    /// [创建者:zlz] <para/>
    /// [创建时间:2021年11月13日] <para/>
    ///<说明>
    ///  [说明:TCP服务端实现]<para/>
    ///</说明>
    ///<修改记录>
    ///    [修改时间:本次修改时间]<para/>
    ///    [修改内容:本次修改内容]<para/>
    ///</修改记录>
    ///<修改记录>
    ///    [修改时间:本次修改时间]<para/>
    ///    [修改内容:本次修改内容]<para/>
    ///</修改记录>
    ///</summary>
    public partial class UCTcpServer : UserControl, IMessageSwitch
    {
        /// <summary>
        /// 网络服务对象
        /// </summary>
        private Socket socket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);

        /// <summary>
        /// 是否打开
        /// </summary>
        private bool IsOpen = false;

        /// <summary>
        /// 推送器
        /// </summary>
        private PushMessageToSwitch pushToSwitch;

        /// <summary>
        /// 类型
        /// </summary>
        SwitchType type;

        /// <summary>
        /// 写日志委托
        /// </summary>
        private WriteLog log;

        /// <summary>
        /// 网络客户端
        /// </summary>
        public Socket mianClient = null;

        /// <summary>
        /// 构造函数
        /// </summary>
        public UCTcpServer()
        {
            InitializeComponent();
        }

        /// <summary>
        /// 得到类型名字
        /// </summary>
        /// <returns></returns>
        public string GetTypeName()
        {
            return "TCP服务端";
        }

        /// <summary>
        /// 得到配置
        /// </summary>
        /// <returns></returns>
        public string GetConfig()
        {
            string confStr = txtIp.Text + "#" + txtPort.Text;
            return confStr;
        }

        /// <summary>
        /// 设置配置
        /// </summary>
        /// <param name="confStr"></param>
        /// <returns></returns>
        public bool SetConfig(string confStr)
        {
            string[] arr = confStr.Split('#');
            if (arr.Length != 2)
            {
                MessageBox.Show("TCP配置格式不对!应该是#分隔的2位", "提示");
                return false;
            }
            txtIp.Text = arr[0];
            txtPort.Text = arr[1];
            return true;
        }

        /// <summary>
        /// 设置推送委托
        /// </summary>
        /// <param name="push">推送器</param>
        /// <param name="ptype">类型</param>
        /// <param name="plog">日志委托</param>
        /// <returns></returns>
        public bool SetPushDelegate(PushMessageToSwitch push, SwitchType ptype, WriteLog plog)
        {
            pushToSwitch = push;
            type = ptype;
            log = plog;
            return true;
        }

        /// <summary>
        /// 启动
        /// </summary>
        /// <returns></returns>
        public bool Start()
        {
            if (txtPort.Text == "")
            {
                if (log != null)
                {
                    log("启动:没有设置端口号");
                }
                return false;
            }
            if (txtIp.Text == "")
            {
                if (log != null)
                {
                    log("启动:没有设置端IP");
                }
                return false;
            }
            //先释放
            if (socket != null)
            {
                socket.Close();
                socket.Dispose();
                socket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
                IsOpen = false;
            }
            //绑定IP端口
            IPAddress hostIP = IPAddress.Any;
            IPAddress.TryParse(txtIp.Text, out hostIP);
            if (Util.PortInUse(Convert.ToInt32(txtPort.Text)))
            {
                if (log != null)
                {
                    log("启动TCP服务:当前电脑端口:" + txtPort.Text + "已经被占用");
                }
                //现实异常信息给客户。                    
                MessageBox.Show("当前电脑端口:" + txtPort.Text + "已经被占用", "提示", MessageBoxButtons.OK);
                return false;
            }
            IPEndPoint ep = new IPEndPoint(hostIP, Convert.ToInt32(txtPort.Text));
            socket.Bind(ep);
            try
            {
                //开始侦听 
                socket.Listen(20);
                if (log != null)
                {
                    log("启动TCP服务:启动TCP服务成功");
                }
                stateIp.ForeColor = Color.Blue;
                Thread tcpThread = new Thread(new ThreadStart(TcpListenClientConnect));
                IsOpen = true;
                tcpThread.Start();
                return true;
            }
            catch (Exception ex)
            {
                IsOpen = false;
                //现实异常信息给客户。                    
                MessageBox.Show(ex.Message, "提示", MessageBoxButtons.OK);
                return false;
            }
        }

        /// <summary>
        /// 停止
        /// </summary>
        /// <returns></returns>
        public bool Stop()
        {
            if (socket != null)
            {
                socket.Close();
                stateIp.ForeColor = Color.Red;
                socket.Dispose();
                socket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
                IsOpen = false;
                if (log != null)
                {
                    log("停止TCP服务:TCP已释放");
                }
            }
            return true;
        }

        /// <summary>
        /// 给tcp发送消息
        /// </summary>
        /// <param name="msgByteArr"></param>
        /// <returns></returns>
        public bool SendMessage(byte[] msgByteArr)
        {
            //转发给TCP
            if (mianClient != null)
            {
                mianClient.Send(msgByteArr);
                return true;
            }
            else
            {
                if (log != null)
                {
                    log("TCP无连接客户端,无法发送,确认防火墙关闭!");
                }
                return false;
            }
        }

        /// <summary>
        /// 得到反射串
        /// </summary>
        /// <returns></returns>
        public string GetReflectStr()
        {
            return "MessageSwitch.UCTcpServer,MessageSwitch.dll";
        }

        /// <summary>
        /// 加载方法
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void UCTcpServer_Load(object sender, EventArgs e)
        {
            txtIp.Text = Util.GetIpv4Str();
        }

        /// <summary>
        /// 监听客户端连接
        /// </summary>
        private void TcpListenClientConnect()
        {
            //死循环侦听客户端连接
            while (true)
            {
                try
                {
                    if (IsOpen == false)
                    {
                        break;
                    }
                    //有客户端连接过来
                    Socket oneClient = socket.Accept();
                    string clentIP = ((IPEndPoint)oneClient.RemoteEndPoint).Address.ToString();
                    string clientPort = ((IPEndPoint)oneClient.RemoteEndPoint).Port.ToString();
                    if (log != null)
                    {
                        log("客户端:" + clentIP + ":" + clientPort + "连接了本服务");
                    }
                    //服务只接收一个客户端
                    if (mianClient == null)
                    {
                        if (log != null)
                        {
                            log("客户端:" + clentIP + ":" + clientPort + "作为主客户端");
                        }
                        mianClient = oneClient;
                        Thread newThread = new Thread(ListenClientData);
                        newThread.Start();
                    }
                    else
                    {
                        if (log != null)
                        {
                            log("已有客户端连接本服务,将断开当前客户端请求");
                        }
                        //有客户端连着就不让其他客户端连接了
                        byte[] byteArray = System.Text.Encoding.Default.GetBytes("已有连接连上该端口了!");
                        oneClient.Send(byteArray);
                        oneClient.Close();
                    }
                }
                catch (Exception ex)
                {
                    if (log != null)
                    {
                        log("服务端侦听异常:" + ex.Message);
                    }
                }
            }
        }


        /// <summary>
        /// 侦听客户端消息
        /// </summary>
        public void ListenClientData()
        {
            byte[] bytes = new byte[83886080];
            //数量
            int datai = -1;
            try
            {
                //死循环侦听客户端发来的数据
                while (true)
                {
                    if (IsOpen == false)
                    {
                        break;
                    }
                    datai = mianClient.Receive(bytes);
                    if (datai > 0)
                    {
                        if (pushToSwitch != null)
                        {
                            byte[] buf = new byte[datai];
                            Array.Copy(bytes, buf, datai);
                            pushToSwitch(buf, type);
                        }
                    }

                }
            }
            catch (System.Exception ex)
            {
                if (log != null)
                {
                    log("侦听客户端消息异常:" + ex.Message);
                }
            }
            finally
            {
                if (mianClient != null)
                {
                    mianClient.Close();
                    mianClient.Dispose();
                    mianClient = null;
                }
            }
        }

        /// <summary>
        /// 启动
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void btnStart_Click(object sender, EventArgs e)
        {
            Start();
        }

        /// <summary>
        /// 停止
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void btnStop_Click(object sender, EventArgs e)
        {
            Stop();
        }
    }
}

工具类,共用方法

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.IO;
using System.Xml.Serialization;
using System.Net.NetworkInformation;
using System.Net;

namespace MessageSwitch
{
    ///<summary  NoteObject="Class">
    /// [功能描述:工具类] <para/>
    /// [创建者:zlz] <para/>
    /// [创建时间:2021年11月13日] <para/>
    ///<说明>
    ///  [说明:工具类]<para/>
    ///</说明>
    ///<修改记录>
    ///    [修改时间:本次修改时间]<para/>
    ///    [修改内容:本次修改内容]<para/>
    ///</修改记录>
    ///<修改记录>
    ///    [修改时间:本次修改时间]<para/>
    ///    [修改内容:本次修改内容]<para/>
    ///</修改记录>
    ///</summary>
    public class Util
    {
        /// <summary>
        /// 反序列化数据
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="path"></param>
        /// <returns></returns>
        public static T ReadLocalData<T>(string path)
        {
            if (File.Exists(path))
            {
                try
                {
                    string str = ReadTxt(path);
                    byte[] arr = Convert.FromBase64String(str);
                    str = System.Text.Encoding.Default.GetString(arr);
                    return XmlDeserialize<T>(str, Encoding.UTF8);
                }
                catch (Exception e)
                {
                    throw e;
                }
            }
            else
            {
                return default(T);
            }
        }

        /// <summary>
        /// 序列化数据
        /// </summary>
        /// <param name="data"></param>
        /// <param name="path"></param>
        /// <returns></returns>
        public static bool WriteLocalData(object data, string path)
        {
            if (File.Exists(path))
            {
                File.Delete(path);
                try
                {
                    string str = Serializer(data.GetType(), data);
                    byte[] arr = System.Text.Encoding.Default.GetBytes(str);
                    str = Convert.ToBase64String(arr);
                    WriteTxt(path, str);
                    return true;
                }
                catch (Exception e)
                {
                    throw e;
                }
            }
            else
            {
                try
                {
                    string str = Serializer(data.GetType(), data);
                    byte[] arr = System.Text.Encoding.Default.GetBytes(str);
                    str = Convert.ToBase64String(arr);
                    WriteTxt(path, str);
                    return true;
                }
                catch (Exception e)
                {
                    throw e;
                }
            }
        }

        /// <summary>
        /// 删除本地数据
        /// </summary>
        /// <param name="id">Id</param>
        /// <param name="pat">路径,不给的话有方法默认决定</param>
        /// <returns>结果</returns>
        public static bool DeleteLocalData(string id, string pat)
        {
            string path = Path.Combine(System.Environment.GetFolderPath(Environment.SpecialFolder.MyDocuments), id + ".aup");
            if (pat != null && pat != string.Empty)
            {
                path = Path.Combine(pat, id + ".aup");
            }
            if (File.Exists(path))
            {
                File.Delete(path);
            }
            return true;
        }

        /// <summary>
        /// 序列化
        /// </summary>
        /// <param name="type">类型</param>
        /// <param name="obj">对象</param>
        /// <returns></returns>
        public static string Serializer(Type type, object obj)
        {
            MemoryStream Stream = new MemoryStream();
            XmlSerializer xml = new XmlSerializer(type);
            try
            {
                //序列化对象
                xml.Serialize(Stream, obj);
            }
            catch (InvalidOperationException)
            {
                throw;
            }
            Stream.Position = 0;
            StreamReader sr = new StreamReader(Stream);
            string str = sr.ReadToEnd();

            sr.Dispose();
            Stream.Dispose();

            return str;
        }

        /// <summary>
        /// 从XML字符串中反序列化对象
        /// </summary>
        /// <typeparam name="T">结果对象类型</typeparam>
        /// <param name="s">包含对象的XML字符串</param>
        /// <param name="encoding">编码方式</param>
        /// <returns>反序列化得到的对象</returns>
        public static T XmlDeserialize<T>(string s, Encoding encoding)
        {
            try
            {
                if (string.IsNullOrEmpty(s))
                {
                    throw new ArgumentNullException("s");
                }
                if (encoding == null)
                {
                    throw new ArgumentNullException("encoding");
                }

                XmlSerializer mySerializer = new XmlSerializer(typeof(T));
                using (MemoryStream ms = new MemoryStream(encoding.GetBytes(s)))
                {
                    using (StreamReader sr = new StreamReader(ms, encoding))
                    {
                        return (T)mySerializer.Deserialize(sr);
                    }
                }
            }
            catch (Exception ex)
            {
                return default(T);
            }
        }

        /// <summary>
        /// 读取文件数据
        /// </summary>
        /// <param name="path">文件全路径</param>
        /// <returns></returns>
        public static string ReadTxt(string path)
        {
            //文件流
            FileStream fs = null;
            try
            {
                //创建文件流
                fs = new FileStream(path, FileMode.Open, FileAccess.Read, FileShare.ReadWrite);
                //创建字符串读取器
                StreamReader sr = new StreamReader(fs);
                //读到尾
                string str = sr.ReadToEnd();
                return str;
            }
            finally
            {
                if (fs != null)
                {
                    fs.Close();
                }
            }
        }

        /// <summary>
        /// 写入数据到指定文件
        /// </summary>
        /// <param name="path">文件全路径</param>
        /// <param name="str">数据</param>
        /// <param name="isReplace">是否提换,默认为替换,否则为添加</param>
        /// <returns></returns>
        public  static bool WriteTxt(string path, string str)
        {
            FileStream fs = null;
            StreamWriter sw1 = null;
            try
            {
                if (File.Exists(path))
                {
                    File.Delete(path);
                }
                //创建文件流
                fs = new FileStream(path, FileMode.CreateNew);
                //创建字符串写入器
                sw1 = new StreamWriter(fs);
                //写入字符串
                sw1.Write(str);
                return true;
            }
            catch (Exception ex)
            {
                throw ex;
            }
            finally
            {
                if (sw1 != null)
                {
                    sw1.Close();
                }
                if (fs != null)
                {
                    fs.Close();
                }
            }
        }

        /// <summary>
        /// 处理不可见字符
        /// </summary>
        /// <param name="data">数据</param>
        /// <returns>返回</returns>
        public static string DealNotSeeToSeeChar(string data)
        {
            for (int i = 0; i <= 31; i++)
            {
                if (i == 6)
                {
                    data = data.Replace((char)i + "", "[ACK]");
                }
                else if (i == 5)
                {
                    data = data.Replace((char)i + "", "[ENQ]");
                }
                else if (i == 4)
                {
                    data = data.Replace((char)i + "", "[EOT]");
                }
                else if (i == 3)
                {
                    data = data.Replace((char)i + "", "[ETX]");
                }
                else if (i == 2)
                {
                    data = data.Replace((char)i + "", "[STX]");
                }
                else
                {
                    data = data.Replace((char)i + "", "[" + i + "]");
                }
            }
            data.Replace((char)127 + "", "[127]");
            return data;
        }

        /// <summary>
        /// 判断端口是否被占用
        /// </summary>
        /// <param name="port">端口</param>
        /// <returns></returns>
        public static bool PortInUse(int port)
        {
            bool inUse = false;

            IPGlobalProperties ipProperties = IPGlobalProperties.GetIPGlobalProperties();
            IPEndPoint[] ipEndPoints = ipProperties.GetActiveTcpListeners();

            foreach (IPEndPoint endPoint in ipEndPoints)
            {
                if (endPoint.Port == port)
                {
                    inUse = true;
                    break;
                }
            }
            return inUse;
        }

        /// <summary>
        /// 获得当前机器的ip4串
        /// </summary>
        /// <returns></returns>
        public static string GetIpv4Str()
        {
            System.Net.IPAddress[] arrIPAddresses = System.Net.Dns.GetHostAddresses(System.Net.Dns.GetHostName());
            foreach (System.Net.IPAddress ip in arrIPAddresses)
            {
                if (ip.AddressFamily.Equals(System.Net.Sockets.AddressFamily.InterNetwork))
                {
                    return ip.ToString();
                }
            }
            return string.Empty;
        }
    }
}

运行效果-串口转串口和TCP服务
在这里插入图片描述

这样就可以任意组合转换器两端的协议了。可以串口转串口,串口转tcp,tcp转tcp,tcp转tdp,串口转udp====。而且每端可以配置多种协议。所以把这个称为超级转换器。分为转换中心和转换实现。

善于抽取共性,就能大大降低复杂度,实现灵活又强大的功能,面向接口编程的好处。

本图文内容来源于网友网络收集整理提供,作为学习参考使用,版权属于原作者。
THE END
分享
二维码
< <上一篇
下一篇>>