之前實作的檢驗虛擬盒子雖然實作了串口轉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("\r\n",(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 = "\r\n";
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 = "\r\n";
//根據實際情況吧,
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====,而且每端可以配置多種協議,所以把這個稱為超級轉換器,分為轉換中心和轉換實作,
善于抽取共性,就能大大降低復雜度,實作靈活又強大的功能,面向介面編程的好處,
轉載請註明出處,本文鏈接:https://www.uj5u.com/ruanti/356941.html
標籤:其他
上一篇:可靠資料傳輸原理詳細圖解
下一篇:網路技術——路由器及其配置
