I write byte arrays to binary file and each array starts with number 313
or 331
, but when I read it I have arrays which start with number 28164
. What have went wrong? Code below.
Question history
I receive data from SerialPort. DataReceived
raised on secondary thread, so all my data processing runs on that thread. I check the data before serialization and during serialization and everything is ok, no problems there, but when I start read this file (when there is no more writing) I read not that data, that I've written (you can read question history bellow for more information). I've find out that if every serialization call I would write in different files that in no one will be errors and that's why I suppose that reason for error is accessing File
/BinaryWriter
from different threads. I've tried to use locks, but it didn't help. What I should do?
lock try:
private static object locker = new object();
public void Serialize() {
lock(locker) {
// some logic here
}
}
I have ConcurrentQueue<byte[]>
as Buffer
. When I receive an array of bytes from SerialPort
I check CRC and if it's okay, I add call next function (short description further):
public void PassData(byte[] data) {
const int recordLen = 18;
const int unusedBytesLen = 6; // 2 for command, 2 for index, and 2 for CRC
const int addressLen = 2;
const int nonRecordDataLen = unusedBytesLen + addressLen;
int recordDataLen = data.Length - nonRecordDataLen;
byte recordsCount = (byte)(recordDataLen / recordLen);
if(recordDataLen % recordLen != 0) {
return;
}
if(recordDataLen == 0) {
return;
}
int serializeLen = data.Length - unusedBytesLen + 1; // plus one for record count
totalCount += serializeLen;
byte[] temp = new byte[serializeLen];
Array.Copy(data, 0, temp, 0, addressLen); // copy address
temp[addressLen] = recordsCount; // record count after address
Array.Copy(data, 6, temp, 3, recordDataLen); // copy records
Buffer.Enqueue(temp);
int addr = BitConverter.ToUInt16(data, 0);
if(addr!=331 && addr != 313) {
throw new Exception("Incorrect data");
}
}
So, as you can see I don't add an array to buffer if
data.Length-6
is not divisible by 18)data.Length==6
).The array consists of the address, records count, and records (group of 18 bytes).
And if the ushort from the first two bytes is not one of my addresses I throw Exception
.
After some conditions, I begin to serialize.
public void Serialize() {
if(Buffer.Count == 0)
return;
using(var stream = File.Open(fileName, FileMode.OpenOrCreate)) {
using(var writer = new BinaryWriter(stream)) {
while(Buffer.Count > 0) {
byte[] bOut;
bool success = Buffer.TryDequeue(out bOut);
if(bOut[2] == 0) {
// will be never thrown cause of the second reason
throw new Exception("Incorrect data");
}
if(bOut.Length != bOut[2] * 18 + 3) {
// will be never thrown cause of the first reason
throw new Exception("Incorrect data");
}
if(success) {
writer.Write(bOut);
}
else {
return;
}
}
}
}
}
But when I try to read this file (after it was written) I have some incorrect records. The first several are okay, but then I receive an address
like 12751 (which is impossible cause I throw an exception if I recieve not my address). Why does this happen? Read function below:
using(var stream = File.Open(fileName, FileMode.Open)) {
using(var reader = new BinaryReader(stream)) {
int length = (int)reader.BaseStream.Length;
while(reader.BaseStream.Position != length) {
uint readerAddr = reader.ReadUInt16();
byte count = reader.ReadByte();
for(int i = 0; i < count; i++) {
uint markAddr = reader.ReadUInt16();
byte cmp0 = reader.ReadByte();
byte cmp1 = reader.ReadByte();
byte cmp2 = reader.ReadByte();
byte cmp3 = reader.ReadByte();
byte cmp4 = reader.ReadByte();
byte cmp5 = reader.ReadByte();
byte cmp6 = reader.ReadByte();
byte cmp7 = reader.ReadByte();
byte cmp8 = reader.ReadByte();
char fec = reader.ReadChar();
uint tim = reader.ReadUInt32();
byte freq = reader.ReadByte();
byte rssi = reader.ReadByte();
sb.Append($"{readerAddr} {markAddr} {cmp0} {cmp1} {cmp2} {cmp3} {cmp4} {cmp5} {cmp6} {cmp7} {cmp8} {fec} {tim} {freq} {rssi}\n");
}
Console.WriteLine(sb.ToString());
sb.Length = 0;
Console.ReadLine();
}
}
}
P.S. I'm awfully sorry if my question is cumbersome and unclear, I have been fighting with this bug all day long. Please, leave comments and I'll try to give some clarifications.
EDIT / Clarification:
Why do I use ConcurrentQueue<byte>
? This is my attempt to fix this bug.
I receive data from SerialPort in the next way: I have a SerialPort.DataReceived
event handler that enables a Timer
for 15ms. If the event is triggered in this time interval I restart Timer
(just timer.Stop(); timer.Start();
). On Timer.Elapsed
event handler I check CRC and pass the data if everything is okay.
So, I've thought the reason for the errors is that SerialPort.DataReceived
raised on a secondary thread and so I have some problems with concurrency.
On my first try, I used List<byte>
as Buffer
which is not thread-safe.
UPD: I've tried to write each group of data in separate file and problem gone. So maybe there is some problems with calling Serialize()
from different Threads?.. I've tried to lock
work with file, but it didn't help.
You (I) should use FileMode.Append
instead of FileMode.OpenOrCreate
. If you use the last one BinaryWriter
will start to rewrite file instead of adding new data to the end. Lock is redundant (in my case) cause I have significant time delay between two serialization iterations.