C#: Waiting for specific character sent from RS485 - c#

In my app I am sending data to microcontroller. I send data, microcontroller do program, and send character ("K"). My application should wait for this character.After receiving this char, it should send data again.
I got problem with receiving this character. Is function BytesToRead right to reading character? My program always fall when it reach this my function wait
static void wait()
{
SerialPort COMport = new SerialPort();
int znak;
COMport.PortName = "COM6"; //
COMport.BaudRate = 1200;
COMport.DataBits = 8;
COMport.Parity = Parity.None;
COMport.StopBits = StopBits.One;
COMport.Open();
do
{
znak = COMport.BytesToRead;
} while (znak != 75); // ASCII K = 75
COMport.Close();
return;
}

The BytesToRead property of the COMport returns the number of characters that have been received by the COMport so your loop will continue until there are exactly 75 characters read. Take a look at the documentation for the SerialPort class. It will show you a good example of how to read/write characters from/to your COMport.

Why not just use while(COMport.ReadChar() != 'K') { /* Do Stuff */ }?

Related

SerialPort.BytesToRead return random number of bytes with EOL

I have an Arduino that constantly writes three hex's to the serial. My C# code defines two hex's (0x0d 0x0a) or \r\n as the EOL. I was expecting each ReadLine() reads out 1 byte of data (0x01) plus 2 bytes of EOL (0x0d and 0x0a), hence BytesToRead() should return 3 (1 data byte + 2 EOL bytes). DiscardInBuffer() then clears the OS's serial buffer to receive the next around of 3 fresh bytes {0x01, 0x0d, 0x0a}, supposedly.
But my BytesToRead() is returning some random count of bytes (e.g. 5,4,3,1,2,5,1,1,2,4...) while I was expecting them to be all 3's. If I remove or comment out serialPort.Newline (not giving the serial port a EOL), my BytesToRead() return all zeros (e.g. 0,0,0,0,0...). So clearly the EOL of \r\n (or 0x0d 0x0a) is doing something here but not in any predictable way.
Could someone explain why I'm getting this random byte counts? I could probably write a function to manually concatenate and chop up the serial data with each EOL and store the bytes in an array, but I want to understand what is happening here and why I can't do this with just the SerialPort class.
Arduino:
void loop() {
Serial.write(0x01);
Serial.write(0x0d); // Carriage Return, CR, \r
Serial.write(0x0a); // Line Feed, LF, \n
}
C#:
SerialPort serialPort = new SerialPort();
serialPort.BaudRate = 9600;
serialPort.PortName = "COM3";
serialPort.Parity = Parity.None;
serialPort.DataBits = 8;
serialPort.StopBits = StopBits.One;
serialPort.NewLine = "\r\n";
serialPort.DataReceived += new SerialDataReceivedEventHandler(serialPort_OnReceiveData);
private void serialPort_OnReceiveData(object sender, SerialDataReceivedEventArgs e)
{
SerialPort sp = (SerialPort)sender;
string a = sp.ReadLine();
int length = sp.BytesToRead;
sp.DiscardInBuffer();
Dispatcher.Invoke(new Action(() =>
{
tbLogger.AppendText(length.ToString() + ",");
}));
}

Parity error not always being detected in serial port

I have a serial port class in netcore - it just listens to the port and tries to detect parity errors. Parity is set to space, and all incoming bytes are being sent with parity=mark which should result in a parity error.
Unfortunately, this is being detected only about a 1/3 of times. I need this detection because this is how the protocol states the beginning of a message.
bytes (80 and 81) are being sent every 1 second so the buffer should always have 1 byte.
What am I doing wrong?
// Use this code inside a project created with the Visual C# > Windows Desktop > Console Application template.
// Replace the code in Program.cs with this code.
using System;
using System.Collections.Generic;
using System.IO.Ports;
using System.Threading;
using SASComms;
public class PortChat
{
static bool _continue;
static SASSerialPort _serialPort;
static object msgsLock = new object();
static Queue<byte[]> msgs = new Queue<byte[]>();
static Queue<byte> receiveQeue = new Queue<byte>();
public static void Main()
{
string name;
string message;
StringComparer stringComparer = StringComparer.OrdinalIgnoreCase;
Thread readThread = new Thread(Read);
// Create a new SerialPort object with default settings.
_serialPort = new MachineSerialPort();
// Allow the user to set the appropriate properties.
_serialPort.PortName = "COM2";
_serialPort.BaudRate = 19200;
_serialPort.ParityReplace = (byte)'\0' ;
_serialPort.ReadBufferSize = 128;
_serialPort.Parity = Parity.Space;
_serialPort.DataBits = 8;
_serialPort.StopBits = StopBits.One;
_serialPort.Handshake = Handshake.None;
// Set the read/write timeouts
_serialPort.ReadTimeout = 5;
_serialPort.WriteTimeout = 5;
_serialPort.ErrorReceived += new SerialErrorReceivedEventHandler(sp_SerialErrorReceivedEventHandler);
_serialPort.Open();
_continue = true;
readThread.Start();
}
public static void sp_SerialErrorReceivedEventHandler(Object sender, SerialErrorReceivedEventArgs e)
{
if (e.EventType == SerialError.RXParity)
{
Console.WriteLine("Parity error");
}
}
public static void Read()
{
while (_continue)
{
try
{
while (_serialPort.BytesToRead > 0)
{
receiveQeue.Enqueue((byte)_serialPort.ReadByte());
}
if (receiveQeue.Count > 0)
{
foreach (byte r in receiveQeue)
{
Console.Write(r.ToString("X")+" " );
Console.WriteLine();
}
}
}
receiveQeue.Clear();
}
catch (TimeoutException) { }
}
}
}
The console is outputing this:
80
Parity error
81
80
Parity error
81
Parity error
80
Parity error
81
80
Parity error
81
80
81
80
81
Parity error
80
Parity error
81
80
Parity error
And I'm expecting "Parity error" after each byte.
Achieved much better result by moving the read logic into the SerialError handler.
Removed the Read() function and the readThread thread.
I had the exact same issue. I was getting only 30-50% of Mark bytes being flagged with parity errors. In my case the problem was the USB RS-232 Serial cable I was using. When I switched to true RS-232 over a DB-9 cable the serial errors were raised 100% correctly.

USB to Serial (to and fro) communication using C# and C language

This is the 1st time am posting my query. I am in need of help. Any help is appreciated.
As i agree that i have given my prob as long story. But am sorry i am not getting how to make it short and my intention is give complete information regarding my prob.
Problem :
I have to communicate between two laptops using USB-to-Serial adapter on windows platform. I have written 2 programs one for sending and another for receiving. Programs were written in both C and C# programming languages.
Using C language :
I am able to successfully communicate using C-Programs mentioned below. But the problem is speed. It takes around 1 hour(60min) for just to pass 150MB. Anyone plz help me in improving the performance of this programs or you may suggest me other approaches which is robust and give high performance. I also mention some comments along with programs for self understanding.
Sender File on laptop with serial port :
#include <stdio.h>
#include <bios.h>
#include <conio.h>
#define COM1 0
#define DATA_READY 0x100
#define TRUE 1
#define FALSE 0
#define SETTINGS ( 0xE0 | 0x03 | 0x00 | 0x00)
int main(void)
{
int in, out, status, DONE = FALSE,i=0;
char c;
FILE *fp,*fp1;
unsigned long count = 0,shiftcount = 0;
clrscr();
fp = fopen("C:/TC/pic.jpg","rb"); //opened for reading actual content
fp1 = fopen("C:/TC/pic.jpg","rb"); //opened for reading the size of file
fseek(fp1,0L,2);
count = ftell(fp1) + 1; // file size
bioscom(0, SETTINGS, COM1); // initializing the port
printf("No. of Characters = %lu\n",count);
// since bioscom function can send or receive only 8bits at a time, am sending file size in
4 rounds so that we can send at max of 4GB file.
bioscom(1,count,COM1); // sneding 1st lower 8bits
bioscom(1,count>>8,COM1); // sending 2nd set of lower 8bits
bioscom(1,count>>16,COM1); // sending 3rd set of lower 8bits
bioscom(1,count>>24,COM1); // sending upper 8 bits
cprintf("... BIOSCOM [ESC] to exit ...\n");
while (!DONE)
{
status = bioscom(3, 0, COM1);// get the status of port
//printf("%d",status);
if (status & DATA_READY) //checks if data is ready
{
out = bioscom(2, 0, COM1); // receives the ack
if(!feof(fp))
{
c = fgetc(fp); //read character by character from file
bioscom(1,c,COM1);//send character to receiver
putch(c);//display
}
}
//to interrupt
if (kbhit())
{
if ((in = getch()) == '\x1B')
DONE = TRUE;
}
}
fclose(fp);
return 0;
}
Receiving file on laptop with USB port :
#include <stdio.h>
#include <bios.h>
#include <conio.h>
#define COM4 3
#define DATA_READY 0x100
#define TRUE 1
#define FALSE 0
#define SETTINGS ( 0xE0 | 0x03 | 0x00 | 0x00)
int main(void)
{
int in, out, status;
char c;
FILE *fp;
unsigned long shiftcount1=0,shiftcount2=0,shiftcount3=0,shiftcount4=0;
unsigned long count = 0, DONE = 1;
clrscr();
fp = fopen("C:/TC/pic1.jpg","wb");// file opened for writing
bioscom(0, SETTINGS, COM4);//initialize tyhe port
cprintf("... BIOSCOM [ESC] to exit ...\n");
//receives all the 32 bits of file size sent from sender
shiftcount1 = bioscom(2,0,COM4);
shiftcount2 = bioscom(2,0,COM4);
shiftcount3 = bioscom(2,0,COM4);
shiftcount4 = bioscom(2,0,COM4);
//send an ack
bioscom(1,'x',COM4);
count = shiftcount1 | (shiftcount2<<8) | (shiftcount3<<16) | (shiftcount4<<24);
printf("shift4 = %lu\tshift3 = %lu\tshift2 = %lu\tshift1 = %lu\n",shiftcount4,shiftcount3,shiftcount2,shiftcount1);
printf("File Size = %lu\n",count);
//loop till the size of the file
while (DONE < count)
{
status = bioscom(3, 0, COM4);// check the status
// printf("%d",status);
if (status & DATA_READY)//check for data ready at the port
{
out = bioscom(2, 0, COM4);//receive the data
DONE++;
fputc(out,fp);
putch(out);
bioscom(1,'x',COM4);//send an ack
}
if (kbhit())
{
if ((in = getch()) == '\x1B')
break;
}
}
fclose(fp);
return 0;
}
Sender file on laptop with USB port:
#include <stdio.h>
#include <bios.h>
#include <conio.h>
#include<dos.h>
#include<stdlib.h>
#include<time.h>
#define RTS 0x02
#define COM1 0
#define COM4 3
#define CURRCOM COM4
#define DATA_READY 0x100
#define TRUE 1
#define FALSE 0
#define SETTINGS ( 0xE0 | 0x03 | 0x00 | 0x00)
int main(void)
{
int in, out, status, DONE = FALSE,nextfile = 1;
char c;
FILE *fp,*fp1;
unsigned long count = 0,shiftcount = 0;
clock_t start,end;
start = clock();
clrscr();
fp = fopen("C:/TC/pic.jpg","rb");
fp1 = fopen("C:/TC/pic.jpg","rb");
fseek(fp1,0L,2);
count = ftell(fp1) + 1;
bioscom(0, SETTINGS, CURRCOM);
/* while(!feof(fp1))
{
c = fgetc(fp1);
count++;
} */
printf("No. of Cheracters = %lu\n",count);
bioscom(1,count,CURRCOM);
bioscom(1,count>>8,CURRCOM);
bioscom(1,count>>16,CURRCOM);
bioscom(1,count>>24,CURRCOM);
cprintf("\n... BIOSCOM [ESC] to exit ...\n");
while (!DONE)
{
status = bioscom(3, 0, CURRCOM);
if (status & DATA_READY)
{
out = bioscom(2,0,CURRCOM);
if(!feof(fp))
{
c = fgetc(fp);
bioscom(1,c,CURRCOM);
putch(c);
}
}
if (kbhit())
{
if ((in = getch()) == '\x1B')
DONE = TRUE;
}
}
fclose(fp);
end = clock();
printf("\nTotal time = %d\n",(end - start)/CLK_TCK);
return 0;
}
Receiver file on laptop with serial port :
#include <stdio.h>
#include <bios.h>
#include <conio.h>
#include<time.h>
#define COM1 0
#define DATA_READY 0x100
#define TRUE 1
#define FALSE 0
#define SETTINGS ( 0xE0 | 0x03 | 0x00 | 0x00)
int main(void)
{
int in, out, status;
char c;
FILE *fp;
int y = 0,esc;
unsigned long count=0,shiftcount1 = 0,shiftcount2 = 0,shiftcount3 = 0,shiftcount4 = 0, DONE = 1;
clock_t start,end;
start = clock();
clrscr();
fp = fopen("C:/TC/pic1.jpg","wb");
bioscom(0, SETTINGS, COM1);
cprintf("... BIOSCOM [ESC] to exit ...\n");
shiftcount1 = bioscom(2,0,COM1);
shiftcount2 = bioscom(2,0,COM1);
shiftcount3 = bioscom(2,0,COM1);
shiftcount4 = bioscom(2,0,COM1);
bioscom(1,'x',COM1);
count = shiftcount1 | (shiftcount2<<8) | (shiftcount3<<16) | (shiftcount4<<24);
printf("shift4 = %lu\tshift3 = %lu\tshift2 = %lu\t shift1 = %lu\n",shiftcount4,shiftcount3,shiftcount2,shiftcount1);
printf("file size = %lu\n",count);
while (DONE < count)
{
status = bioscom(3, 0, COM1);
//printf("%d",status);
if (status & DATA_READY)
{
out = bioscom(2, 0, COM1);
DONE++;
fputc(out,fp);
putch(out);
bioscom(1,'x',COM1);
}
if (kbhit())
{
if ((in = getch()) == '\x1B')
break;
}
}
fclose(fp);
end = clock();
printf("\nTotal time = %f\n",(end - start)/CLK_TCK);
return 0;
}
The above 4 programs behaves as, sender send a character and receives an ack for every character. I have followed this approach, bcoz other approaches were not working fine (in the sense the complete data is not sent, the amount of data sent is not judgeable, bcoz it will different every tym). when i used this approach it worked fine.
Using C# language :
Below two programs are written in C# using visual studio. I have used SerilaPort class , its properties and methods for communication. Using this, am able to communicate text and xml files on both the sides successfully.Also image files with .jpg extention, can be transferred from USB to serial end withot any loss of data(successful transmission), but if i transfer from serial to usb end, am able receive image with some data loss, even with the data loss am able to see the image.
Sender file on laptop with serial port :
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.IO.Ports;
using System.IO;
using System.Threading;
namespace Communication
{
class Program
{
static void Main(string[] args)
{
string name;
string message;
StringComparer stringComparer = StringComparer.OrdinalIgnoreCase;
//Thread readThread = new Thread(Read);
FileStream fs = new FileStream("C:/text.xml", FileMode.Open);
//StreamReader sr = new StreamReader(fs);
BinaryReader br = new BinaryReader(fs);
// Create a new SerialPort object with default settings.
SerialPort _serialPort = new SerialPort();
// Allow the user to set the appropriate properties.
_serialPort.PortName = "COM1";
_serialPort.BaudRate = 115200;
_serialPort.Parity = Parity.None;
_serialPort.DataBits = 8;
_serialPort.StopBits = StopBits.One;
_serialPort.Handshake = Handshake.None;
// Set the read/write timeouts
_serialPort.ReadTimeout = 500;
_serialPort.WriteTimeout = 500;
_serialPort.Open();
bool _continue = true;
//readThread.Start();
int len = (int)fs.Length;
char[] data = new char[len+1];
br.Read(data, 0, len);
for (int i = 0; i < len+1; i++)
{
_serialPort.Write(data, i, 1);
//Console.Write(data,i,1);
}
br.Close();
fs.Close();
_serialPort.Close();
}
}
}
Receiver file on laptop with USB port :
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.IO.Ports;
using System.IO;
using System.Threading;
using System.Collections;
namespace Communication
{
class Program
{
static void Main(string[] args)
{
SerialComm comm = new SerialComm();
comm.Init();
comm.ReadSerial();
comm.WriteToFile();
comm.ResClose();
Console.ReadKey();
}
}
class SerialComm
{
FileStream fs = null;
BinaryWriter file = null;
ArrayList al = null;
public Boolean Init()
{
if (fs == null)
{
fs = new FileStream("C:/text1.txt", FileMode.OpenOrCreate);
}
if (file == null)
{
file = new BinaryWriter(fs);
}
if (al == null)
{
al = new ArrayList();
}
return true;
}
public void ResClose()
{
file.Close();
fs.Close();
}
public Boolean ReadSerial()
{
SerialPort port;
StreamWriter sw;
ConsoleKeyInfo ck;
port = new SerialPort();
port.PortName = "COM4";
port.BaudRate = 115200;
port.DataBits = 8;
port.Parity = Parity.None;
port.StopBits = StopBits.One;
port.Handshake = Handshake.None;
port.Open();
port.BaseStream.Flush();
port.DiscardInBuffer();
int c = 1;
while (c != 0)
{
c = port.ReadByte();
al.Add((byte)c);
}
return true;
}
public void WriteToFile()
{
int i = 0;
byte[] message = al.ToArray(typeof(byte)) as byte[];
file.Write(message, 0, message.Length - 1);
}
}
}
Please help me.
Thanks in advance.
Transmission speed:
Serial ports are simple, not fast. I am assuming that you are using 230kbps which is already more than many hardware can handle. Compression is only thing that might help, but compressing already compressed data (like .mp3) won't help much.
Data loss:
Serial ports are simple, not robust. Data loss is common, and only thing you can do about it is to have protocol to detect errors on incoming frames, and have ability to retry send if there is an error.
Conclusion:
Use TCP/IP instead.
Way too long a question. I've found only one actual question, and that's the performance bit.
Just use Ethernet or WiFi. "Serial port" (you probably mean RS-232) speeds are low. 0.1 Mbit/second is considered fast by RS-232 standards. You clock 1200 Mbit/3600 seconds, which is 0.3 Mbit/second. That is ultra-fast. I'm in fact surprised that you achieve that, your C# program is explicitly setting the speed to 0.1 Mbit/second.

Sending standard input to Arduino C#

I am trying to take the standard input from the console and send it to my Arduino Uno as plain ASCII.
I get the Input and strip \r\n from it using this code:
String Input = Console.Read().ToString().Replace("\r",string.Empty).Replace("\n",string.Empty);
When I perform doConsole.WriteLine(Input);, it outputs "72" which is correct, but when I do serialPort.Write(Input); the Arduino returns "55", which it does for everything.
What am I doing wrong?
My code for the C# side (host/PC):
String Input = Console.Read().ToString().Replace("\r", string.Empty).Replace("\n",string.Empty);
//Console.WriteLine(Input);
//serialPort.Write(Input);
char[] InputChar = Input.ToCharArray();
serialPort.Write(InputChar,0,1);
//byte[] InputByte = Encoding.ASCII.GetBytes(Input);
//Console.WriteLine(Input);
//serialPort.WriteLine(Input);
Thread.Sleep(25); //Wait 0.025 second.
//***************************************************************//
// Read anything from the serial port. //
//***************************************************************//
numBytes = serialPort.BytesToRead;
for (int i = 0; i < numBytes; i++)
rxPacket[i] = (byte)serialPort.ReadByte();
result = new char[numBytes];
for (int i = 0; i < numBytes; i++)
result[i] = (char)rxPacket[i];
Console.Write("Read this from Arduino:");
Console.WriteLine(result);
Console.WriteLine("press Enter to continue");
Console.ReadKey(); //Read nothing.
And my Arduino sketch:
const int ledPin = 13; // The pin that the LED is attached to.
int incomingByte; // A variable to read incoming serial data into.
void setup() {
// Initialize serial communication:
Serial.begin(9600);
// Initialize the LED pin as an output:
pinMode(ledPin, OUTPUT);
}
void loop() {
// see if there's incoming serial data:
if (Serial.available() > 0) {
// Read the oldest byte in the serial buffer:
incomingByte = Serial.read();
// If it's a capital H (ASCII 72), turn on the LED:
if (incomingByte == 'H') {
digitalWrite(ledPin, HIGH);
Serial.print(incomingByte);
}
// If it's an L (ASCII 76), turn off the LED:
else if (incomingByte == 'L') {
digitalWrite(ledPin, LOW);
Serial.print(incomingByte);
}
else{
Serial.print(incomingByte);
}
}
}
Edit: changed the code to the following. Still no luck; I am getting same reply.
String Input = Console.Read().ToString().Replace("\r",string.Empty).Replace("\n",string.Empty);
Console.Write(Input,0,1);
//serialPort.Write(Input);
byte[] inputByte = Encoding.ASCII.GetBytes(Input);
serialPort.Write(inputByte,0,1);
Well, I looked it up... As it turns out, ASCII code 55 = 7.
7 is the first digit of 72.
Hmm, so perhaps your sending decimal numbers to the Arduino here and the Arduino sees a 7 first. May I suggest to convert your byte and send it as a byte (a byte can only contain 0..255), but it is a single ASCII code.
Maybe for the Arduino to think about, but maybe it is not related to this. Instead of
int incomingByte; // ints are made of 2 bytes an int isn't an incomming byte
try
Byte incomingByte;
So I managed to get it working
Basically, converting it to a byte took me a while to work out.
this is the code I ended up with
String Input = Console.Read().ToString().Replace("\r", string.Empty).Replace("\n",string.Empty);
Console.Write(Input,0,1);
byte[] inputByte = new byte[1];
inputByte[0] = Convert.ToByte(Input);
serialPort.Write(inputByte, 0, 1);
//byte[] inputByte = Encoding.ASCII.GetBytes(Input);
//serialPort.Write(inputByte,0,2);
//String num = inputByte.ToString();
//serialPort.WriteLine(num);
//Console.WriteLine(Input);
//serialPort.Write(InputByte,0,1);
Thread.Sleep(25); //Wait 0.025 second.
//***************************************************************//
// Read anything from the serial port. //
//***************************************************************//
numBytes = serialPort.BytesToRead;
for (int i = 0; i < numBytes; i++)
rxPacket[i] = (byte)serialPort.ReadByte();
result = new char[numBytes];
for (int i = 0; i < numBytes; i++)
result[i] = (char)rxPacket[i];
Console.Write("Read this from Arduino:");
Console.WriteLine(result);
Console.WriteLine("press Enter to continue");
Console.ReadKey(); //Read nothing.
Seems to work Perfectly now.

C# TcpClient closes the connection on the Server when sending world information with an exception

I am trying to send all the tiles (which are referred to as Particles, don't confuse them with client particle effects) in my world (500x350 2d array in my World class) through a NetworkStream hooked up with my Client both on the Server and Clientside.
The code is as follows :
public int ServerSendWorldData(NetworkStream networkStream, Point from, Point size, bool reportBytesSent, Networking.Client toClient)
{
int bytesOfAcknowledgementSent = 0;
int totalBytesSent = 0;
int tilesSentThisAcknowledgement = 0;
int tilesToSendForThisAcknowledgement = 20;
for (int x = from.X; x < size.X; x++)
{
for (int y = from.Y; y < size.Y; y++)
{
Thread.Sleep(0);
//Handle acknowledgement if needed.
if (tilesSentThisAcknowledgement >= tilesToSendForThisAcknowledgement)
{
//Wait for Client acknowledgement.
bytesOfAcknowledgementSent += Networking.NetworkingHelp.SendMessageFromByte((byte)Networking.MessageType.ServerSendWorldMapDataWaitAcknowledgement, networkStream);
//Handle here.
if (networkStream.ReadByte() == (byte)Networking.MessageType.ClientAcknowledgeWorldMapData)
tilesSentThisAcknowledgement = 0;
else
throw new Exception("Client did not acknowledge data!");
}
if (world.worldParticles[x, y] != null)
{
//Send Particle Data
totalBytesSent += Networking.NetworkingHelp.SendMessageFromByte((byte)Networking.MessageType.ServerSendWorldMapDataParticle, networkStream);
totalBytesSent += Networking.NetworkingHelp.SendMessageFromInt(x, networkStream);
totalBytesSent += Networking.NetworkingHelp.SendMessageFromInt(y, networkStream);
totalBytesSent += Networking.NetworkingHelp.SendMessageFromInt(world.worldParticles[x, y].ID, networkStream);
//totalBytesSent += Networking.NetworkingHelp.SendMessageFromInt(world.worldParticles[x, y].spriteIndex, networkStream);
totalBytesSent += Networking.NetworkingHelp.SendMessageFromBool(world.worldParticles[x, y].draw, networkStream);
totalBytesSent += Networking.NetworkingHelp.SendMessageFromBool(world.worldParticles[x, y].collisionWithEntities, networkStream);
tilesSentThisAcknowledgement++;
}
}
}
if (reportBytesSent)
{
Statistics.Console.WriteLine("Sent " + totalBytesSent + " bytes of World data to Client " + toClient.name + " and " + bytesOfAcknowledgementSent + " bytes of acknowledgement was exchanged!");
}
return totalBytesSent;
}
The NetworkingHelp class is basically just networkStream.Write with either a BitConverter converting data such as ints to bytes or an Encoding converting data such as strings to bytes in UTF8.
The code sends a total of 0.76 MB of map data to the Client.
The problem I am having is not present when connecting to the server using localhost / IPAddress.Loopback, but the problem appears when sending data to Clients through the internet - the data is sent extremely slowly (probably due to the file size, I'm unsure though) and without the "Handle Acknowledgement" bit of code ...
//Handle acknowledgement if needed.
if (tilesSentThisAcknowledgement >= tilesToSendForThisAcknowledgement)
{
//Wait for Client acknowledgement.
bytesOfAcknowledgementSent += Networking.NetworkingHelp.SendMessageFromByte((byte)Networking.MessageType.ServerSendWorldMapDataWaitAcknowledgement, networkStream);
//Handle here.
if (networkStream.ReadByte() == (byte)Networking.MessageType.ClientAcknowledgeWorldMapData)
tilesSentThisAcknowledgement = 0;
else
throw new Exception("Client did not acknowledge data!");
}
... the Client recieves about 20 world tiles and throws the exception "Unable to write data to the transport connection: An existing connection was forcibly closed by the remote host", this seems to happen because the Client gets a 1 byte message with byte 0 as its contents.
It seems that the pause created by the "Handle Acknowledgement" code solves this problem, but makes the world data transfer a lot slower.
Help would be much appreciated, and I'm aware my code is kind of messy (open to suggestions on how to improve it, it's my first time sending this much data). Here is the block of code for reading the particular message for the Client.
//WorldParticlesMapDataParticle
else if (recievedMessage[0] == (byte)Networking.MessageType.ServerSendWorldMapDataParticle)
{
//Read the position of the new data.
recievedMessageBytes = networkStream.Read(recievedMessage, 0, 4);
int newParticleX = BitConverter.ToInt32(recievedMessage, 0);
recievedMessageBytes = networkStream.Read(recievedMessage, 0, 4);
int newParticleY = BitConverter.ToInt32(recievedMessage, 0);
//Read the particle ParticleID.
recievedMessageBytes = networkStream.Read(recievedMessage, 0, 4);
int newParticleID = BitConverter.ToInt32(recievedMessage, 0);
//Read the particle SpriteID.
//recievedMessageBytes = networkStream.Read(recievedMessage, 0, 4);
//int newSpriteID = BitConverter.ToInt32(recievedMessage, 0);
//Read the particle draw.
recievedMessageBytes = networkStream.Read(recievedMessage, 0, 1);
bool newParticleDraw = BitConverter.ToBoolean(recievedMessage, 0);
//Read the particle collision.
recievedMessageBytes = networkStream.Read(recievedMessage, 0, 1);
bool newParticleCollision = BitConverter.ToBoolean(recievedMessage, 0);
//Set particle.
try
{
world.worldParticles[newParticleX, newParticleY] = World.Particle.ParticleManager.particleArray[newParticleID];
//world.worldParticles[newParticleX, newParticleY].spriteIndex = newSpriteID;
world.worldParticles[newParticleX, newParticleY].draw = newParticleDraw;
world.worldParticles[newParticleX, newParticleY].collisionWithEntities = newParticleCollision;
}
catch (Exception ex)
{
Statistics.Console.WriteLine("Server requested new Particle at " + newParticleX + "," + newParticleY + ", but Client failed to place it due to an exception : " + ex.Message);
try
{
world.worldParticles[newParticleX, newParticleY] = null;
}
catch
{ }
}
}
You ignore the return value from NetworkStream.Read. I think you are assuming that it will always read the number of bytes you asked it to read unless there's an error. In fact, the number of bytes you specify is just the maximum it will read and it will not read more bytes than are currently available when you call it.
"This method reads data into the buffer parameter and returns the number of bytes successfully read. If no data is available for reading, the Read method returns 0. The Read operation reads as much data as is available, up to the number of bytes specified by the size parameter. If the remote host shuts down the connection, and all available data has been received, the Read method completes immediately and return zero bytes." - NetworkStream.Read
You have made the classic mistake of assuming TCP will somehow 'glue together' data just because you sent it in a single call. In fact, TCP has no such mechanism.
you are sending one particle at a time try making a big array of what you want to send and send it in one chunk
To complement David Schwartz's answer, you would be wise to read Stephen Cleary's TCP/IP FAQ, especially the message framing section

Categories

Resources