TorbenJ
TorbenJ

Reputation: 4582

Send string over network, Client only gets a part

I'm working on a client/server application.
The server sometimes sends a string to all connected clients.

To do this I defined a packet:
byte <- packet id
int <- length of the string byte[] <- byte array containing the string

And now how I send this packet:

byte[] buf = { ID };
buf = buf.Concat(BitConverter.GetBytes(StringData.Length)).ToArray();
buf = buf.Concat(Encoding.UTF8.GetBytes(StringData)).ToArray();

stream.Write(buf, 0, buf.Length);

Sometimes the string is quite small (about 200 bytes) but sometimes it is a bit larger (>20k bytes). Usually the smaller packets are sent without problems reading them on clientside but the larger ones are only partially received by the client.
So typical scenario on clientside:

packet id -> 0x01
length -> 20000
string -> "this is an example stri"

I know "this is an example string" is not 20000 bytes long but it is just an example.
In my case the string is a serialized JObject (Json.Net).

On clientside I read the packet like this:

byte[] buf = new byte[1];
stream.Read(buf, 0, buf.Length);

// Some packet id evaluation

buf = new byte[4];
stream.Read(buf, 0, buf.Length);
int length = BitConverter.ToInt32(buf, 0);

buf = new byte[length];
stream.Read(buf, 0, length);
string jsonString = Encoding.UTF8.GetString(buf, 0, length);

I dont really know what I've done wrong.
Any suggestions where to look for errors or have I done something wrong with sending the data?

Thanks in advance!

Upvotes: 0

Views: 260

Answers (2)

Guffa
Guffa

Reputation: 700562

There is two problems with the code:

First, you are using the length of the string instead of the length of the encoded data in the packet. Convert the string first, so you can use the length in the packet:

byte[] data = Encoding.UTF8.GetBytes(StringData);
byte[] buf = { ID };
buf = buf.Concat(BitConverter.GetBytes(data.Length)).ToArray();
buf = buf.Concat(data).ToArray();

Second, the Read method doesn't guarantee that it returns the number of bytes requested. It may put fewer bytes in the buffer, and it returns the number of bytes actually put in the buffer.

Use the return value of the Read call to check if you need to call it multiple times to get the data. Example:

buf = new byte[4];
int offset = 0;
while (offset < buf.length) {
  int len = stream.Read(buf, offset, buf.Length - offset);
  offset += len;
}

Note: This implementation doesn't handle the case where the stream ends prematurely. Then the Read method would return zero, and the code goes into an eternal loop. You might want to make a method to use for reading from the stream, where you handle all possible errors.

Upvotes: 2

keeehlan
keeehlan

Reputation: 8054

I can't guarantee this will work, but give it a shot for reading your data:

var ms = new System.IO.MemoryStream();

byte[] buffer = new byte[4096];
int bytesRead = 0;

do
{
    bytesRead = stream.Read(buffer, 0, buffer.Length);

    ms.Write(buffer, 0, bytesRead);
}
while (stream.DataAvailable);

string jsonString = System.Text.Encoding.UTF8.GetString(ms.ToArray());

Adjust it as necessary for your needs. Let me know how it works.

Upvotes: 0

Related Questions