I implemented a Client and Server model that uses Socket with thread
When I want to pass only a string from Client to the Server, it works. But I want to pass an object and it throws this error:
"Attempting to deserialize an empty stream"
Here is the code:
Client:
ASCIIEncoding asen = new ASCIIEncoding();
MemoryStream memoryStream = new MemoryStream();
BinaryFormatter binaryFormatter = new BinaryFormatter();
Command command = new Command("meuLogin", "minhaSenha");
binaryFormatter.Serialize(memoryStream, command);
stream.Write(memoryStream.ToArray(), 0, memoryStream.ToArray().Length);
Server:
byte[] message = new byte[4096];
int bytesRead = 0;
bytesRead = clientStream.Read(message, 0, 4096);
MemoryStream memoryStream = new MemoryStream(bytesRead);
BinaryFormatter bf1 = new BinaryFormatter();
memoryStream.Position = 0;
Command command = (Command)bf1.Deserialize(memoryStream);
Another question: I copied the class Command from Client and pasted at Server to deserialize. Is this correct?
Thank you
I also recommend WCF. But if you continue using sockets, the key element that you're missing in your protocol is message framing.
You never use the message that you read from the stream. The memory stream you are reading from is thus empty.
On a side note, why do you use these intermediate MemoryStreams?
To answer your second question: for maximum maintainability, the class Command should be in a separate assembly that both Client and Server reference.
To answer your first question: you are attempting to deserialize from an empty stream on your server, just as the exception tells you. You need to copy the bytes you read from the clientStream into the memoryStream before you deserialize from the memoryStream. Alternatively, use the clientStream directly rather than using the memoryStream; this may require reconsidering your protocol.
Finally, I wholeheartedly agree with #Andrey: consider using WCF. It's way way way better than raw sockets.
If you change your server code to use a different MemoryStream constructor, the problem will go away.
MemoryStream memoryStream = new MemoryStream(message, 0, bytesRead);
However, I agree with Stephen and others. Either use WCF, or use Message framing.
Another question: I copied the class Command from Client and pasted at Server to deserialize. Is this correct?
No. The namespace for your Client and Server are likely different, so the Server will be trying to deserialize a stream that matches its namespace.
You should create your Command Class using its own namespace and reference that from both your Client and the Server.
After that...
Client:
static void StreamToServer(TcpClient client, Command obj) {
using (NetworkStream ns = client.GetStream()) {
using (MemoryStream ms = new MemoryStream()) {
BinaryFormatter formatter = new BinaryFormatter();
formatter.Serialize(ms, obj);
byte[] buf = ms.ToArray();
ns.Write(buf, 0, buf.Length);
}
}
}
Server:
static Command ReadStream(TcpListener listener) {
Command obj = null;
using (TcpClient client = listener.AcceptTcpClient()) { // waits for data
using (NetworkStream ns = client.GetStream()) {
byte[] buf = new byte[client.ReceiveBufferSize];
int len = ns.Read(buf, 0, buf.Length);
using (MemoryStream ms = new MemoryStream(buf, 0, len)) {
BinaryFormatter formatter = new BinaryFormatter();
obj = formatter.Deserialize(ms) as Command;
}
}
}
return obj;
}
WCF or Message framing may be easier, but I don't often have the opportunity at work to sit around and read a book.
Instead of passing bytesRead to the MemoryStream which is actually the length of the byte stream, you should pass 'message', as it is the actual stream of bytes. Like,
MemoryStream memoryStream = new MemoryStream(message);
As you are passing an integer variable, the compiler is throwing exception that the stream is empty.
As for WCF, it is remarkable framework, but for applications that require low latency and high performance, WCF is a horrible answer because of its overheads, it is built upon sockets. So if you use sockets, that will be the lowest level implementation and thus, the fastest. That depends on your application which paradigm you should choose...
Related
Using TCPClient's NetworkStream and protobuf-net I send and receive protobuf messages via TCP.
Saw a similar question: How to properly handle incoming protobuf message with a NetworkStream?
But in my case there can only be one message type so i dont think i need a resolver.
So i serialized my object and send it using tcp/ip, on my server i try to deserialize it and get io exception: Unable to read data from the transport connection.
Client:
...
using (var ms = new MemoryStream())
{
Serializer.Serialize(ms, person);
data = ms.ToArray();
}
NetworkStream stream = client.GetStream();
stream.Write(data, 0, data.Length);
Server:
...
Byte[] bytes = new Byte[256];
String data = null;
while(true)
{
Console.Write("Waiting for a connection... ");
TcpClient client = server.AcceptTcpClient();
Console.WriteLine("Connected!");
data = null;
NetworkStream stream = client.GetStream();
Person newPerson = Serializer.Deserialize<Person>(stream);<--- exeption
}
I think the short version here is: use SerializeWithLengthPrefix and DeserializeWithLengthPrefix. The default protobuf behaviour is "read to the end of the stream". You shouldn't need the MemoryStream when serializing, btw; you should be fine to Serialize directly to the NetworkStream. If you need the MemoryStream for other reasons, you can save yourself a copy of the data by using:
stream.Write(ms.GetBuffer(), 0, (int)ms.Length);
I'm using PUT method to update some data. But my below code is not working.
The code:
var schemaRequest = WebRequest.Create(new Uri(SchemaUri)) as HttpWebRequest;
schemaRequest.Method = "PUT";
schemaRequest.ContentType = "text/xml";
schemaRequest.Credentials = CredentialCache.DefaultNetworkCredentials;
schemaRequest.Proxy = WebRequest.DefaultWebProxy;
schemaRequest.AddRange(1024);
string test = "<ArrayOfUpdateNodeRequest> <UpdateNodeRequest> <Description>vijay</Description> <Name>Publishing</Name></UpdateNodeRequest></ArrayOfUpdateNodeRequest>";
byte[] arr = new byte[1024];
arr = System.Text.Encoding.UTF8.GetBytes(test);
schemaRequest.ContentLength = arr.Length;
using (var dataStream = schemaRequest.GetRequestStream())
{
dataStream.Write(arr, 0, arr.Length);
}
I'm getting the exception "This stream does not support seek operations." at GetRequestStream().
The exception is pretty clear, the steam doesn't support seeking. Looking at the object in the debugger does not mean you need to seek--if you do, please provide an example. You should simply be able to write to the stream for it to be sent to the host. It doesn't make sense to be able to seek when you're sending a stream to a host (e.g, how do you seek back before a byte you've already sent over the wire to the host?).
If you need to seek locally, before sending to the host, create a memory stream and seek that way. For example:
using (MemoryStream memoryStream new MemoryStream())
{
// ... writes
memoryStream.Seek(0, SeekOrigin.Begin);
//... writes
memoryStream.CopyTo(schemaRequest.GetRequestStream());
}
I have now an issue with deserializing an object sent over TCP.
When deserializing, I get the following SerializationException (code below):
Additional information: Binary stream '0' does not contain a valid
BinaryHeader. Possible causes are invalid stream or object version
change between serialization and deserialization.
Serialization code:
public static void SerializeRO(Stream stream, ReplicableObject ro) {
MemoryStream serializedObjectStream = new MemoryStream();
Formatter.Serialize(serializedObjectStream, ro);
BinaryWriter bw = new BinaryWriter(stream);
bw.Write(serializedObjectStream.Length);
serializedObjectStream.WriteTo(stream);
serializedObjectStream.Close();
bw.Close();
}
Deserialization code:
public static List<ReplicableObject> ParseStreamForObjects(Stream stream) {
List<ReplicableObject> result = new List<ReplicableObject>();
while (true) {
if (!(stream as NetworkStream).DataAvailable) break;
BinaryReader br = new BinaryReader(stream);
int length = br.ReadInt32();
byte[] bytes = br.ReadBytes(length);
MemoryStream ms = new MemoryStream(bytes);
ms.Position = 0;
// ERROR OCCURS ON THE LINE BELOW
result.Add((ReplicableObject) Formatter.Deserialize(ms));
ms.Close();
br.Close();
}
return result;
}
The objects are being serialized at runtime, so I don't think it's a versioning issue. I am new to streaming etc, so I may have missed something obvious.
I'd like to suggest what I think it could be, but I'm really stuck. :)
Thanks.
serializedObjectStream.Length is a long.
You're writing a 64-bit value to the network, but you're trying to read it as a 32-bit int.
I'm trying to send a JSON request to a remote device that then returns a JSON response.
The code I've used is this:
TcpClient client = new TcpClient();
client.Connect(IPAddress.Parse("someip"), someport);
NetworkStream stream = client.GetStream();
byte[] myWriteBuffer = Encoding.ASCII.GetBytes("some JSON");
stream.Write(myWriteBuffer, 0, myWriteBuffer.Length);
BinaryReader r = new BinaryReader(stream);
Console.WriteLine(r.ReadString())
This code successfully sends the JSON string, receives the response, but that response only shows 123 characters, meaning that it cuts some chars...
What am I doing wrong
BinaryReader / BinaryWriter are not necessarily the right tools for writing to an arbitrary stream; in particular, they choose a specific way of encoding strings, with a length-prefix. If this is not what your remote device is expecting, it will fail.
I would just use the Stream directly, with Read and Write.
In particular, { is 123 in ASCII, so it looks BinaryReader is incorrectly taking the "length" from the opening JSON brace.
Probably an encoding/decoding issue, I would change your code like so
TcpClient client = new TcpClient();
client.Connect(IPAddress.Parse("someip"), someport);
NetworkStream stream = client.GetStream();
byte[] myWriteBuffer = Encoding.ASCII.GetBytes("some JSON");
stream.Write(myWriteBuffer, 0, myWriteBuffer.Length);
byte[] readBuffer = stream.GetBuffer();
Console.WriteLine(Encoding.ASCII.GetString(bytes));
I have this scenario:
DownloadLibrary.GetData(Stream targetStream);
SaveLibrary.WriteData(Stream sourceStream);
I want to send the data that targetStream collects to the sourceStream. I've come up with some solutions but I cannot find a way to connect those streams directly.
What I'm trying to achieve is send the data from targetStream to sourceStream without buffer the targetStream first.
How can it be done?
Thanks in advance.
There is built in support (from .Net 4.0) in Stream for copying one stream to another via CopyTo, e.g.:
stream1.CopyTo(stream2)
Example:
[Test]
public void test()
{
string inString = "bling";
byte[] inBuffer = Encoding.ASCII.GetBytes(inString);
Stream stream1 = new MemoryStream(inBuffer);
Stream stream2 = new MemoryStream();
//Copy stream 1 to stream 2
stream1.CopyTo(stream2);
byte[] outBuffer = new byte[inBuffer.Length];
stream2.Position = 0;
stream2.Read(outBuffer, 0, outBuffer.Length);
string outString = Encoding.ASCII.GetString(outBuffer);
Assert.AreEqual(inString, outString, "inString equals outString.");
}
The built-in CopyTo method referred to in chibacity's answer is available from .NET 4.0.
For earlier versions look at this question.