Serialized data on tcpclient needs to state amount? - c#

I have sent data as byte using TcpClient and I wanted to send my own class instead bytes of data.
By bytes of data, what I meant is that I am sending the data converted into bytes like this:
using (MemoryStream bufferStream = new MemoryStream())
{
using (BinaryWriter bufferData = new BinaryWriter(bufferStream))
{
// Simple PONG Action
bufferData.Write((byte)10);
}
_logger.Info("Received PING request, Sending PONG");
return bufferStream.ToArray();
}
And instead I would like to send it like this, without having to declare its size or w/e
public class MyCommunicationData
{
public ActionType Action { get; set; }
public Profile User { get; set; }
...
}
Normally, when I send my data as bytes the first 5 bytes I use to indicate the action and the message size.
But if I migrate to serialize all the data as a single class, do I still need to send what action and size it is or using serialized messages the client and server would know what to read etc or is there a way to do so I can send it without having to specify things out of the serialization object ?
Not sure if this matters here, I am using AsyncCallback to read and write to the network stream:
_networkStream = _client.tcpClient.GetStream();
_callbackRead = new AsyncCallback(_OnReadComplete);
_callbackWrite = new AsyncCallback(_OnWriteComplete);
Let me know if you need me to post any other functions.

If you use a text based serializer(for ex, Json), you can utilize StreamReader's ReadLine and StreamWriter's WriteLine (created from tcpClient.GetStream).
Your code would be something like
writer.WriteLine(JsonConvert.SerializeObject(commData))
and to get the data on the other end
var myobj = JsonConvert.DeserializeObject<MyCommunicationData>(reader.ReadLine())
--EDIT--
//**Server**
Task.Factory.StartNew(() =>
{
var reader = new StreamReader(tcpClient.GetStream());
var writer = new StreamReader(tcpClient.GetStream());
while (true)
{
var myobj = JsonConvert.DeserializeObject<MyCommunicationData>(reader.ReadLine());
//do work with obj
//write response to client
writer.WriteLine(JsonConvert.SerializeObject(commData));
}
},
TaskCreationOptions.LongRunning);

Related

getting different results from converting an object to a byte array

I am trying to create a messenger using asp.net core and WebSockets.I want the client to convert a Message object to byte[] and send it to the server by a WebSocket. But on the server-side, I can't deserialize the object that the client serialized. after some debugging, I found some differences between serialized buffers from the same objects on both sides.
actully, the serialized byte[] from msg object on the server-side is not the same as serialized byte[] from msg object on the client-side.
Here is my code on the server-side:
Message msg = new Message()
{
Text="Test"
};
var test = ObjectToByteArray(msg);
public static byte[] ObjectToByteArray(Object obj)
{
BinaryFormatter bf = new BinaryFormatter();
using (var ms = new MemoryStream())
{
bf.Serialize(ms, obj);
return ms.ToArray();
}
}
[Serializable]
public class Message
{
public string Text { get; set; }
}
And exactly the same code on the client-side (my server-side is asp.net core 5 API and my client-side is Avalonia dotnet 6)
but here is peace of var test on the server-side (note that it has 170 bytes):
and here is peace of var test on the client-side (note that it has 160 bytes):
why they are different? how I can fix this bug?

C# FlatBufferBuilder create String from Stream

Suppose you need to read a large string from a stream and you want to put that string into a flatbuffer.
Currently what I do is read the stream into a string and then use the FlatbufferBuilder.CreateString(string s) function.
This works fine but it does have as a drawback that the string is copied and loaded into memory twice: once by reading it from the stream into the string; and then a second time the string is copied into the flatbuffer.
I was wondering if there is a way to fill the flatbuffer string directly from a stream?
For a more concrete example:
Suppose your flatbuffer schema looks like:
table Message
{
_Data: string;
}
root_type Message;
We can then create a flatbuffer like this (with myData a string)
var fbb = new FlatBufferBuilder(myData.Length);
var dataOffset = fbb.CreateString(myData);
var message = Message.CreateMessage(fbb, dataOffset);
Message.FinishMessageBuffer(fbb, message);
So the question is can we somehow do the same thing, where myData is a System.IO.Stream?
Obviously the following works, but I'd like to avoid first reading the Stream into memory.
using (var reader = new StreamReader(myStream)
{
var myData = reader.ReadToEnd();
var fbb = new FlatBufferBuilder(myData.Length);
var dataOffset = fbb.CreateString(myData);
var message = Message.CreateMessage(fbb, dataOffset);
Message.FinishMessageBuffer(fbb, message);
}
There is currently no way to avoid that copy twice, afaik.. it should be relatively simple to implement a version of CreateString that takes a stream and reduces it to one copy. You could have a go at that and open a PR on github with the result.

Read variables of a message that is sent by a server using socket

I am working on Passenger information system PIS(Train).So trains send their location to my server PIS using socket on port 8080 .So i should get their locations and show them to passengers . The message that comes from the trains has a template that i should follow that. As you can see here :
So as you can see here we have 6 variables .every integer is 4 byte. The first variable is(4 byte) message source and etc.
In my server i have to detect these variable but i don't know how can i detect them from the message .
static void Listeners()
{
Socket socketForClient = tcpListener.AcceptSocket();
if (socketForClient.Connected)
{
Console.WriteLine("Client:" + socketForClient.RemoteEndPoint + " now connected to server.");
NetworkStream networkStream = new NetworkStream(socketForClient);
System.IO.StreamWriter streamWriter =
new System.IO.StreamWriter(networkStream);
System.IO.StreamReader streamReader =
new System.IO.StreamReader(networkStream);
while (true)
{
TimeTableRepository objTimeTableRepository = new TimeTableRepository();
SensorRepository objSensorRepository = new SensorRepository();
ArrivalTimeRepository objArrivalTimeRepository=new ArrivalTimeRepository();
TrainRepository objTrainRepository = new TrainRepository();
// OnlineTrainRepository ObjOnlineTrainrepository = new OnlineTrainRepository();
//-----
string theString = streamReader.ReadLine();
}
}
}
Here is my listener to port 8080 and theString is the message that is send by trains.My problem is how can i detect this parameters (Message source,message destination and etc) from theString?I mean i need the value of them to store in database .
best regards
Looks like you don't need to detect anything and definitely should not be slapping this in a string to try and parse.
You already know you are getting a bunch of integers back. You even know what order they are in. Use a BinaryReader to get you your numbers and proceed from there. Once you load your reader up, it should be as simple as calling BinaryReader.ReadInt32() to read the message's numbers one after another.
I must also highly recommend you to look into using statements for your streams.
using (var reader = new BinaryReader(networkStream))
{
var messageSource = reader.ReadInt32();
var messageDestination = reader.ReadInt32();
... and so on ...
}

C# Why doesn't "Flush" force the bytes down the network stream?

I have a project where I'm trying to send a serialized object to the server, then wait for an "OK" or "ERROR" message to come back.
I seem to be having a similar problem to th poster of : TcpClient send/close problem
The issue is that the only way I seem to be able to send the original object is to close the connection, but then (of course) I can't wait to see if the object was processed successfully by the server.
private void button4_Click(object sender, EventArgs e)
{
RequestPacket req = new RequestPacket();
/// ... Fill out request packet ...
/// Connect to the SERVER to send the message...
TcpClient Client = new TcpClient("localhost", 10287);
using (NetworkStream ns = Client.GetStream())
{
XmlSerializer xml = new XmlSerializer(typeof(RequestPacket));
xml.Serialize(ns, req);
/// NOTE: This doesn't seem to do anything....
/// The server doesn't get the object I just serialized.
/// However, if I use ns.Close() it does...
/// but then I can't get the response.
ns.Flush();
// Get the response. It should be "OK".
ResponsePacket resp;
XmlSerializer xml2 = new XmlSerializer(typeof(ResponsePacket));
resp = (ResponsePacket)xml2.Deserialize(ns);
/// ... EVALUATE RESPONSE ...
}
Client.Close()
}
UPDATE: In response to one commenter, I don't think the client can be at fault. It is simply waiting for the object, and the object never comes until I close the socket.... however, if I'm wrong, I'll GLADLY eat crow publicly. =) Here's the client:
static void Main(string[] args)
{
// Read the port from the command line, use 10287 for default
CMD cmd = new CMD(args);
int port = 10287;
if (cmd.ContainsKey("p")) port = Convert.ToInt32(cmd["p"]);
TcpListener l = new TcpListener(port);
l.Start();
while (true)
{
// Wait for a socket connection.
TcpClient c = l.AcceptTcpClient();
Thread T = new Thread(ProcessSocket);
T.Start(c);
}
}
static void ProcessSocket(object c)
{
TcpClient C = (TcpClient)c;
try
{
RequestPacket rp;
//// Handle the request here.
using (NetworkStream ns = C.GetStream())
{
XmlSerializer xml = new XmlSerializer(typeof(RequestPacket));
rp = (RequestPacket)xml.Deserialize(ns);
}
ProcessPacket(rp);
}
catch
{
// not much to do except ignore it and go on.
}
}
Yeah.... it's that simple.
Uh oh, you can blame Nagle's algorithm. It has nothing to do with C# though, it is a default behavior for TCP/IP stack. Enable NoDelay socket option using SetSocketOption method. But be careful, disabling Nagle's algorithm will downgrade the throughput.
I'm also not sure about that stream you are using on top of the socket, as I am not a C# developer at all, but try to drop its instance so it does write for sure :-)
The short version is apparently, when using XmlSerializer (or any other big blob) to shove data down a NetworkStream, it will simply hold the line open indefinitely waiting for more information to be written. It only flushes the connection once you close it. This creates a situation where this method is great for sending, but not receiving. Or vice-versa. It becomes a one-way communication, and useless for continued back-and-forth communication over the same connection.
It's kind of crappy that I had to work around something that seemed so elegant on the surface, but dropping back to my old C days, I've resorted to sending a "number of bytes" packet first, then the actual packet. This enables me to READ at the other end the exact number of bytes so I never get caught in a blocking pattern.
To simplify my life, I created a class that holds some static methods for both sending and receiving. This class can send ANY XML-serializable class across the network, so it does what I need it to do.
If anyone has a more elegant solution, I'd be open to hearing it.
public class PacketTransit
{
public static void SendPacket(TcpClient C, object Packet)
{
MemoryStream ms = new MemoryStream();
XmlSerializer xml = new XmlSerializer(Packet.GetType());
xml.Serialize(ms, Packet);
ms.Position = 0;
byte[] b = ms.GetBuffer();
ms.Dispose();
byte [] sizePacket = BitConverter.GetBytes(b.Length);
// Send the 4-byte size packet first.
C.Client.Send(sizePacket, sizePacket.Length, SocketFlags.None);
C.Client.Send(b, b.Length, SocketFlags.None);
}
/// The string is the XML file that needs to be converted.
public static string ReceivePacket(TcpClient C, Type PacketType)
{
byte [] FirstTen = new byte[1024];
int size = 0;
byte[] sizePacket = BitConverter.GetBytes(size);
// Get the size packet
int sp = C.Client.Receive(sizePacket, sizePacket.Length, SocketFlags.None);
if (sp <= 0) return "";
size = BitConverter.ToInt32(sizePacket, 0);
// read until "size" is met
StringBuilder sb = new StringBuilder();
while (size > 0)
{
byte[] b = new byte[1024];
int x = size;
if (x > 1024) x = 1024;
int r = C.Client.Receive(b, x, SocketFlags.None);
size -= r;
sb.Append(UTF8Encoding.UTF8.GetString(b));
}
return sb.ToString();
}
/// The XML data that needs to be converted back to the appropriate type.
public static object Decode(string PacketData, Type PacketType)
{
MemoryStream ms = new MemoryStream(UTF8Encoding.UTF8.GetBytes(PacketData));
XmlSerializer xml = new XmlSerializer(PacketType);
object obj = xml.Deserialize(ms);
ms.Dispose();
return obj;
}
public static RequestPacket GetRequestPacket(TcpClient C)
{
string str = ReceivePacket(C, typeof(RequestPacket));
if (str == "") return new RequestPacket();
RequestPacket req = (RequestPacket) Decode(str, typeof(RequestPacket));
return req;
}
public static ResponsePacket GetResponsePacket(TcpClient C)
{
string str = ReceivePacket(C, typeof(ResponsePacket));
if (str == "") return new ResponsePacket();
ResponsePacket res = (ResponsePacket)Decode(str, typeof(ResponsePacket));
return res;
}
}
To use this class, I simply need to call PacketTransit.SendPacket(myTcpClient, SomePacket) to send any given XML-Serializable object. I can then use PacketTransit.GetResponsePacket or PacketTransit.GetRequestPacket to receive it at the other end.
For me, this is working very well, but it was alot more of a workout than originally expected.
you should use a StreamWriter/Reader linked to your network stream, .Flush does nothing on a NetworkStream, see here:
http://www.c-sharpcorner.com/UploadFile/dottys/SocketProgDTRP11222005023030AM/SocketProgDTRP.aspx
I believe the real problem here may be that the XmlDeserializer may not return until it has read EOS from the stream. You may need to shutdown the sending stream for output to force this to happen.

Sending and receiving custom objects using Tcpclient class in C#

I have a client server application in which the server and the client need to send and receive objects of a custom class over the network. I am using TcpClient class for transmitting the data. I am serializing the object at the sender side and sending the resulting stream of bytes to the receiver. But at the receiver, when I try to de-serialize the bytes received, it throws Serialization Exception and the details are :
The input stream is not a valid
binary format. The starting contents
(in bytes) are:
0D-0A-00-01-00-00-00-FF-FF-FF-FF-01-00-00-00-00-00
...
My server code that serializes the object is:
byte[] userDataBytes;
MemoryStream ms = new MemoryStream();
BinaryFormatter bf1 = new BinaryFormatter();
bf1.Serialize(ms, new DataMessage());
userDataBytes = ms.ToArray();
netStream.Write(userDataBytes, 0, userDataBytes.Length);
The client code that de-serializes it is:
readNetStream.Read(readMsgBytes, 0, (int)tcpServer.ReceiveBufferSize);
MemoryStream ms = new MemoryStream(readMsgBytes);
BinaryFormatter bf1 = new BinaryFormatter();
ms.Position = 0;
object rawObj = bf1.Deserialize(ms);
DataMessage msgObj = (DataMessage)rawObj;
Please help me to solve this problem and possibly suggest any other method to transmit objects of custom classes across network using TcpClient in C#.
Thanks,
Rakesh.
Have a look at this code. It takes a slightly different approach.
Example given by the link above: - Note: there was another problem he was facing which he solved here (keep-alive). It's in the link after the initial sample code.
Object class to send (remember the [Serializable]):
[serializable]
public class Person {
private string fn;
private string ln;
private int age;
...
public string FirstName {
get {
return fn;
}
set {
fn=value;
}
}
...
...
public Person (string firstname, string lastname, int age) {
this.fn=firstname;
...
}
}
Class to send object:
using System;
using System.Net;
using System.Net.Sockets;
using System.Runtime.Serialization;
using System.Runtime.Serialization.Formatters.Binary;
class DataSender
{
public static void Main()
{
Person p=new Person("Tyler","Durden",30); // create my serializable object
string serverIp="192.168.0.1";
TcpClient client = new TcpClient(serverIp, 9050); // have my connection established with a Tcp Server
IFormatter formatter = new BinaryFormatter(); // the formatter that will serialize my object on my stream
NetworkStream strm = client.GetStream(); // the stream
formatter.Serialize(strm, p); // the serialization process
strm.Close();
client.Close();
}
}
Class to receive object:
using System;
using System.Net;
using System.Net.Sockets;
using System.Runtime.Serialization;
using System.Runtime.Serialization.Formatters.Binary;
class DataRcvr
{
public static void Main()
{
TcpListener server = new TcpListener(9050);
server.Start();
TcpClient client = server.AcceptTcpClient();
NetworkStream strm = client.GetStream();
IFormatter formatter = new BinaryFormatter();
Person p = (Person)formatter.Deserialize(strm); // you have to cast the deserialized object
Console.WriteLine("Hi, I'm "+p.FirstName+" "+p.LastName+" and I'm "+p.age+" years old!");
strm.Close();
client.Close();
server.Stop();
}
}
When receiving on client side you do not know how much data you want to read.
You are only relying on the ReceiveBufferSize, while your data can be larger
or smaller then that.
I think the best approach here is to send 4 bytes that tells your client about the length of incoming data:
byte[] userDataLen = BitConverter.GetBytes((Int32)userDataBytes.Length);
netStream.Write(userDataLen, 0, 4);
netStream.Write(userDataBytes, 0, userDataBytes.Length);
and on the recieving end you first read the data length and then read
exact amount of data.
byte[] readMsgLen = new byte[4];
readNetStream.Read(readMsgLen, 0, 4);
int dataLen = BitConverter.ToInt32(readMsgLen);
byte[] readMsgData = new byte[dataLen];
readNetStream.Read(readMsgData, 0, dataLen);
Infact, I just realized, that you might has to do a little more to assure you read all data (just an idea because I haven't tried it, but just incase you run into problem again you can try this).
The NetworkStream.Read() method returns a number indicating the amount of data it has read. It might be possible that the incoming data is larger then the RecieveBuffer. In that case you have to loop until you read all of the data. You have to do something like this:
SafeRead(byte[] userData, int len)
{
int dataRead = 0;
do
{
dataRead += readNetStream.Read(readMsgData, dataRead, len - dataRead);
} while(dataRead < len);
}
TCP is stream-based protocol (as opposed to datagram protocol) so it's possible to receive only part of sended data via Read method call.
To solve this problem you may use DataLength field (as cornerback84 suggested) or you may use your own "application-level packet" structure.
For example, you may use something like this
|-------------------------------|
|Begin|DataLength| Data |End|
| 4b | 4b | 1..MaxLen|4b |
|-------------------------------|
where
Begin - start packet identifier (for example 0x0A, 0x0B, 0x0C, 0x0D)
DataLength - Data field length (for example, from 0 to MaxLength)
Data - actual data (serialized Person class or some other data)
End - end packet identifier (for example 0x01, 0x05, 0x07, 0x0F).
That is, on client side you would wait not only for incoming data, after receiving data you would search you Application level packets, and you may deserialized Data part only after receiving valid packet.

Categories

Resources