views:

56

answers:

5

Guys, please help me 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

+2  A: 

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?

Grzenio
I assume the `MemoryStream` is used because the object has already been read from the `clientStream`. @Alan will need to reconsider his protocol design.
Randolpho
well, i was going to use after this line:Command command = (Command)bf1.Deserialize(memoryStream);but the problem occurs in this line
Alan
@Alan, how do you want to deserialize an object from an empty stream??? The memory stream is empty when you run this line.
Grzenio
The server has a more serious problem though, as there's nothing to distinguish/check whether his `clientStream.Read(..)` call read 1 or 2, or e.g. 2 and a half objects.
nos
+1  A: 

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.

Randolpho
i will see this WCF thanks!but I didn't understand what you mean about the first question..the client send the bytes to the server and then the server read it. When I debug at Server, the bytes were 381 or something. If I didn't understand you, please show me some pseudo-code. Thank you
Alan
+1  A: 

I also recommend WCF. But if you continue using sockets, the key element that you're missing in your protocol is message framing.

Stephen Cleary
I will check the WCF and message framing! Thank you!
Alan
A: 

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.

feroze
A: 

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.

jp2code