111,102
社区成员




接收的数据的数组变量您字节写一个把,长度您定,把轮询的中间数据放在一个数组里或二维数组,或泛型集合都有。
还请大佬们help一下。
算了,还是给个例子把。我随手写了一个demo
using System;
using System.Buffers;
using System.Collections.Generic;
using System.IO.Pipelines;
using System.Linq;
using System.Text;
using System.Threading.Channels;
using System.Threading.Tasks;
namespace ConsoleApp4
{
internal class Program
{
static void Main(string[] args)
{
//环境:为了表明这些东西是net通用技术,我这里特地不使用netcore
//所以环境是net4.62的控制台,
//nuget包:system.io.pipelines //官方管道流--数据流--用来处理流式数据的本地环形内存处理
//System.Threading.Channels //因为读是读,写是写,分析是分析。自然而然我需要把结果通知也独立出来
//我们先写测试,为了表示这些是通用技术,我先不打算对接串口,也不打算对接tcp,只用byte数组
//我们先来模拟流式接收数据这种大小不定的情况
//这里理论上是2条完整封包数据,我特地拆成3条大小不一的数据块
List<byte[]> lst = new List<byte[]>();
lst.Add(new byte[] {0x5a,0x54,0x01});
lst.Add(new byte[]{0x5a,0xfe,0x5a});
lst.Add(new byte[]{0x54,0x02,0x5a,0xfe});
Pipe pipe = new Pipe();
PipeWriter writer = pipe.Writer;
PipeReader reader = pipe.Reader;
Task.Run(async () =>
{
while (true)
{
var package = await channel.Reader.ReadAsync();
Console.WriteLine("已解析出封包:"+BitConverter.ToString(package));
}
});
Task.WhenAll(writerTask(lst, writer), ReaderTask(reader));
Console.ReadKey();
}
static void ttt(ref ReadOnlySequence<byte> da)
{
var a = da;
a = a.Slice(0, 1);
da = a;
}
private static Channel<byte[]> channel = Channel.CreateUnbounded<byte[]>();
static async Task writerTask(List<byte[]> source,PipeWriter writer)
{
foreach (var bytes in source)
{
Console.WriteLine("写入数据:"+BitConverter.ToString(bytes));
writer.Write(bytes); //我特地选一个默认不带刷新的
writer.FlushAsync();//自己刷新数据通知reader去处理
//为了模拟随机不定长,我这里用延时模拟
await Task.Delay(1000);
}
}
static async Task ReaderTask(PipeReader reader)
{
//为了只描述核心逻辑,我这里不写那些异常控制
while (true)
{
var result = await reader.ReadAsync();
var buffer = result.Buffer;
while (TryParsePackage(ref buffer,out var package))
{
channel.Writer.WriteAsync(package.Value.ToArray());
}
reader.AdvanceTo(buffer.Start, buffer.End);
}
}
static byte[] head = new byte[] { 0x5a, 0x54 };
static byte[] end = new byte[] { 0x5a, 0xfe};
static bool TryParsePackage(ref ReadOnlySequence<byte> buffer, out ReadOnlySequence<byte>? package)
{
package = null;
var _buffer = buffer;
var p1 = TryGetLette(ref _buffer, head);
if (p1 == null)
return false;
var p2 = TryGetLette(ref _buffer, end);
if (p2 == null)
return false;
var endp= buffer.GetPosition(end.Length, p2.Value);
package = buffer.Slice(p1.Value,endp);
buffer = buffer.Slice(package.Value.End);
return true;
}
static SequencePosition? TryGetLette(ref ReadOnlySequence<byte> buffer, ReadOnlySpan<byte> delimiters,
bool advancePastDelimiter = true)
{
var _buffer = buffer;
SequencePosition? p = null;
while (_buffer.Length>delimiters.Length)
{
var p1 = _buffer.PositionOf(delimiters[0]);
if(p1==null)
break;
_buffer = _buffer.Slice(p1.Value);
if(_buffer.Length<delimiters.Length)
break;
var t = buffer.Slice(p1.Value, delimiters.Length);
//演示,就不讲究性能,直接用内存直接比较,toarray会重新分配内存
//不过问题不大,通常delimiters标识位长度就几位,重新分配问题也不大
var res = t.ToArray().SequenceEqual(delimiters.ToArray());
if (res)
{
p = p1;
break;
}
}
if (p != null && advancePastDelimiter)
{
buffer = buffer.Slice(buffer.GetPosition(1,p.Value));
}
return p;
}
}
}
using System;
using System.Collections.Generic;
using System.IO.Ports;
using System.Text;
namespace WpfApp1.Read
{
public class ReadSW
{
private bool isopen = false;
public delegate void BarCodeDelegate(byte[] data);
public event BarCodeDelegate BarCodeEvent;
private SerialPort locksp;
volatile private bool isread = false;
List<byte> data = null;//数据
//记录未处理的接收数据,主要考虑接收数据分段
byte[] m_btAryBuffer = new byte[10];
//记录未处理数据的有效长度
int m_nLenth = 0;
volatile private bool isdata = false;//是否是数据
public bool Isopen { get => isopen; }
public bool openPort(string com, int btl)
{
try
{
if (isopen == false)
{
isread = false;
data = new List<byte>();
locksp = new SerialPort(com, btl);
locksp.StopBits = StopBits.One;
locksp.DataBits = 8;
locksp.Parity = Parity.None;
locksp.ReadTimeout = -1;
locksp.RtsEnable = true;
locksp.ReceivedBytesThreshold = 1;
locksp.DataReceived += new SerialDataReceivedEventHandler(sp_DataReceived);
locksp.Open();
isopen = true;
return isopen;
}
else
{
return isopen;
}
}
catch
{
return isopen;
}
}
public void closePort()
{
if (isopen)
{
isopen = false;
locksp.Close();
locksp.Dispose();
}
}
private void sp_DataReceived(object sender, EventArgs e)
{
try
{
if (!isread)
{
isread = true;
do
{
if (locksp.BytesToRead > 0)
{
try
{
//将收到的数据放入数组btAryReceiveData
byte[] btAryReceiveData = new byte[locksp.BytesToRead];
locksp.Read(btAryReceiveData, 0, btAryReceiveData.Length);
//将收到的数据和上次剩余的数据组成新数据
int nCount = btAryReceiveData.Length;
byte[] btAryBuffer = new byte[nCount + m_nLenth];
if (m_nLenth != 0)
{
Array.Copy(m_btAryBuffer, btAryBuffer, m_nLenth);
}
Array.Copy(btAryReceiveData, 0, btAryBuffer, m_nLenth, btAryReceiveData.Length);
m_btAryBuffer = new byte[10];
m_nLenth = 0;
for (int nLoop = 0; nLoop < btAryBuffer.Length; nLoop++)
{
if (isdata == false)//判断现在接收的是否为中间数据默认为false
{
if (btAryBuffer[nLoop] == 0x5A)//判断第一位是否是0x5A 是就判断下一位,不是就丢弃
{
if (nLoop + 1 < btAryBuffer.Length)//没有下一位了就把0x5A放入零时数据
{
if (btAryBuffer[nLoop + 1] == 0x54)//判断头的第二位是否为0x54 如果不是就丢弃
{
isdata = true;
nLoop++;
}
}
else
{
m_nLenth = btAryBuffer.Length - nLoop;
Array.Copy(btAryBuffer, nLoop, m_btAryBuffer, 0, m_nLenth);
break;
}
}
}
else
{
if (btAryBuffer[nLoop] == 0x5A)//判断结束符号第一位是否是0x5A,如果是就有可能是结束符号,在判断第二位,不是就放入带返回的data中
{
if (nLoop + 1 < btAryBuffer.Length)//没有下一位了,没有就把当前放入零时数据中,等有新数据了在判断
{
if (btAryBuffer[nLoop + 1] == 0xFE)//判断结束符号的第二位是否为0xFE 如果不是就丢弃
{
isdata = false;//说明一整数据接收完整了
nLoop++;
BarCodeEvent?.Invoke(data.ToArray());//返回一个完整数据
data.Clear();
}
else//说明还是数据
{
data.Add(btAryBuffer[nLoop]);
}
}
else
{
m_nLenth = btAryBuffer.Length - nLoop;
Array.Copy(btAryBuffer, nLoop, m_btAryBuffer, 0, m_nLenth);
break;
}
}
else
{
data.Add(btAryBuffer[nLoop]);
}
}
}
}
catch
{
}
}
}
while (locksp.BytesToRead > 0);
isread = false;
}
}
catch
{
}
}
public bool sendMessage(byte[] msg)
{
if (isopen)
{
try
{
locksp.Write(msg, 0, msg.Length); //发送数据内容
return true;
}
catch
{
return false;
}
}
else
{
return false;
}
}
}
}
已经回复过了,看样子你还是坚持非园子不用策略
你要的东西其实呢就是system.io.pipelins,那个玩意就是你口里“缓冲”,你口里的“数组”,你口里“拼接”
来看看原来那个文章的代码在干嘛
async Task ProcessLinesAsync(Socket socket)
{
//生成一个管道,他就是你要的“缓存”
var pipe = new Pipe();
Task writing = FillPipeAsync(socket, pipe.Writer); //writer 是数据写入器
Task reading = ReadPipeAsync(pipe.Reader); //reader 数据读取器
await Task.WhenAll(reading, writing); //启动两个任务,一个不定从socket读取数据,当然你可以替换成从串口读取数据
}
async Task FillPipeAsync(Socket socket, PipeWriter writer)
{
const int minimumBufferSize = 512;
//这里的while就是不停从串口读取数据,这里是while,你可以替换成串口的触发事件
while (true)
{
// Allocate at least 512 bytes from the PipeWriter.
Memory<byte> memory = writer.GetMemory(minimumBufferSize);
try
{
int bytesRead = await socket.ReceiveAsync(memory, SocketFlags.None);
if (bytesRead == 0)
{
break;
}
// Tell the PipeWriter how much was read from the Socket.
//把读取到的数据写到管道里,当然这里有好几个重载,你可以按需替换
//目前这代码内置了上个帖子有人告诉你的通知,不过有些方法没有内置,需要你手动 flush一下通知reader有新数据来了
writer.Advance(bytesRead);
}
catch (Exception ex)
{
LogError(ex);
break;
}
// Make the data available to the PipeReader.
FlushResult result = await writer.FlushAsync();
if (result.IsCompleted)
{
break;
}
}
//这个结合上面代码的意思,如果有异常出现,通知整个管道结束,让reader停止
// By completing PipeWriter, tell the PipeReader that there's no more data coming.
await writer.CompleteAsync();
}
async Task ReadPipeAsync(PipeReader reader)
{
//这里你你要的“拼接后的数据”
while (true)
{
//这里是异步等待操作,writer通知你有数据了,他就会往下执行,没有数据则异步等待在此行
ReadResult result = await reader.ReadAsync();
ReadOnlySequence<byte> buffer = result.Buffer;
while (TryReadLine(ref buffer, out ReadOnlySequence<byte> line))
{
// Process the line.
ProcessLine(line); //分析数据,按下面代码,这个封包按\n分割,你可以替换成你自己的分析过程
}
// Tell the PipeReader how much of the buffer has been consumed.
//这句也是你要了,上面已经分析出一行大小,这里就是告诉管道有多少个是我已经确定分析完毕并提取了
//假设里面有10字节,我已经分析出前7个是我要了,那么这句话就是告诉管道请把前7个移除,我不需要了。此时管道里还剩下3个字节
//留待和下一次的数据拼接到一起分析
reader.AdvanceTo(buffer.Start, buffer.End);
// Stop reading if there's no more data coming.
if (result.IsCompleted)
{
break;
}
}
// Mark the PipeReader as complete.
await reader.CompleteAsync();
}
bool TryReadLine(ref ReadOnlySequence<byte> buffer, out ReadOnlySequence<byte> line)
{
// Look for a EOL in the buffer.
SequencePosition? position = buffer.PositionOf((byte)'\n');
if (position == null)
{
line = default;
return false;
}
// Skip the line + the \n.
line = buffer.Slice(0, position.Value);
buffer = buffer.Slice(buffer.GetPosition(1, position.Value));
return true;
我字儿也丑,但我努力学习编程来吃饭,其中一项重要的技能就是打字要快.
了解一下有限状态自动机
使用
ReadSW sw = new ReadSW();
sw.BarCodeEvent += Sw_BarCodeEvent;
sw.openPort("COM1", 9600);
private void Sw_BarCodeEvent(byte[] data)
{
这里每次接收到的就是一串中间数据
}