im new to c# and i was wondering , what is the simplest way to send an object using tcpClient , itried the following code but it throws a wierd error client code :
TcpClient client = new TcpClient(ip, port);
StreamWriter writer = new StreamWriter(client.GetStream());
NetworkStream strm = client.GetStream();
BinaryFormatter formatter = new BinaryFormatter();
Transaction tx = new transaction();
string msg = string.Empty;
msg = "transaction";
writer.WriteLine(msg);
writer.Flush();
formatter.Serialize(strm,tx);
and on the receiving end
Server Code :
while(true){
TcpClient client = server.AcceptTcpClient();
IFormatter formatter = new BinaryFormatter();
NetworkStream strm = client.GetStream();
StreamReader reader = new StreamReader(client.GetStream());
string msg = string.Empty;
while (!((msg = reader.ReadLine()).Equals("exit"))){
Transaction tx = (Transaction)formatter.Deserialize(strm);
}
it produces this error on the server
input stream is not a valid ibinary format intital content is :0c-02-00-00 .....
so can someone please help me or if there another simple and clean way of sending objects using tcpclient ?
The underlying problem here is tied into the way that you are mixing two different mechanisms to read and write to a stream, specifically: using StreamReader
and a separate stream-based parser. It is also a bad idea to do this with StreamWriter
, but ... I think you would get away with it sort-of, although it is still a bad idea.
The problem here is that StreamReader
is greedy. When you ask it for a line, it doesn't read from the stream byte-by-byte looking for a \r
or \n
- it grabs a buffer of data from the stream, and then processes it as you ask for it. In this way, it assumes that it is now the sole owner of the stream.
So; when you do this:
while (!((msg = reader.ReadLine()).Equals("exit"))){
Transaction tx = (Transaction)formatter.Deserialize(strm);
}
the reader consumes more than just "transaction\r\n
" - it consumes that line and some undefined number of bytes from whatever comes after. Then, when BinaryFormatter
tries to read the stream, it finds itself half way through a message, and it explodes in a shower of sparks.
Ideally, limit yourself to one serialization mechanism. Meaning: lose StreamReader
/StreamWriter
completely here.
If I could propose an alternative mechanism using protobuf-net and inheritance:
[ProtoContract]
[ProtoInclude(1, typeof(ShutdownMessage))]
[ProtoInclude(2, typeof(TransactionMessage))]
public abstract class MessageBase {}
[ProtoContract]
public sealed class ShutdownMessage : MessageBase {}
[ProtoContract]
public sealed class TransactionMessage : MessageBase {
// your data here
}
and now you can send any number of messages with:
public void Send(MessageBase message) {
Serializer.SerializeWithLengthPrefix(strm, message, PrefixStyle.Base128);
}
and receive any number of messages with:
while (true) {
var msg = Serializer.DeserializeWithLengthPrefix<MessageBase>(strm, PrefixStyle.Base128);
if (msg is null || msg is ShutdownMessage) break; // all done
switch (msg)
{
case TransactionMessage tx: ProcessTransaction(tx); break;
// etc
}
}