1

I have a TCP connection between two computers using windows forms. I have an array of integers which I am serializing using the XmlSerializer class and sending it using the StreamWriter and receiving using StreamReader.
Sending:

NetworkStream stream = m_client.GetStream();   //m_client is a TCPclient
StreamWriter m_writer = new StreamWriter(stream);
int[] a = new int[3] { 10,20,30 };
XmlSerializer serializer = new XmlSerializer(typeof(int[]));
serializer.Serialize(m_writer, a);
//m_writer.Flush();       //Doesn't help
//m_writer.Close();

On the receiving end:

TcpClient client = (TcpClient)p_client;
NetworkStream stream = client.GetStream();
StreamReader reader = new StreamReader(stream);
if (stream.CanRead)
{
    XmlSerializer serializer = new XmlSerializer(typeof(int[]));
    int[] numbers = (int[])serializer.Deserialize(reader);
    MessageBox.Show(numbers[0].ToString());    //Is not reached
    MessageBox.Show(numbers[1].ToString());
    MessageBox.Show(numbers[2].ToString());
}

The problem is that the reader on the receiver does not stop reading unless I terminate the connection or close the m_writer on the senders end (in which case I can't use it for sending again). And if it doesn't stop reading, the next line (namely, MessageBox.Show(numbers[0].ToString())) will not work.

I need suggestions on how I can inform the receiver to stop reading after 30 or how can it understand when to stop reading?

Edit:
I got the XMLserializer idea from How to send integer array over a TCP connection in c#
I found an answer which requires terminating the connection, which I'd rather not do unless it is the only way possible. XmlSerializer Won't Deserialize over NetworkStream

Community
  • 1
  • 1
Sanketh. K. Jain
  • 489
  • 1
  • 9
  • 24
  • 1
    Have you tried flushing the `NetworkStream` after writing? If the data isn't being sent over the network, the reader will wait indefinitely for the data. – Pharap Feb 17 '15 at 10:41
  • I have tried using m_writer.flush(). Still doesn't work. – Sanketh. K. Jain Feb 17 '15 at 10:43
  • 1
    You should not be using TCP at all to send messages. HTTP would be more appropriate because it handles this for you. Webservices even better since they handle the serialization as well. – usr Feb 17 '15 at 10:55
  • Sorry, I seem to have forgotten to mention that this is a windows form – Sanketh. K. Jain Feb 17 '15 at 12:51

2 Answers2

1

NetworkStream will end only when the underlying socket is closed.

So you need to read the data as much as you need; Create a MemoryStream out of it and deserialize with it as shown in the linked post.

Sriram Sakthivel
  • 72,067
  • 7
  • 111
  • 189
  • But how do I know how much to read? I mean, yes it is all in my control, but the data that I send in every iteration will vary – Sanketh. K. Jain Feb 17 '15 at 10:49
  • 1
    @Sanketh.K.Jain You need to send it along with the data you send. Usually it will be sent in the beginning of the message being sent. Refer [this](http://blogs.msdn.com/b/joncole/archive/2006/03/20/simple-message-framing-sample-for-tcp-socket.aspx) for more info. – Sriram Sakthivel Feb 17 '15 at 10:52
1

When sending data via TCP, if there are any boundaries within the stream of bytes, it is up to you to define and implement them.

In your particular example, it seems reasonable that you could precede the XML data with a byte count. It would also be reasonable to use a null byte to terminate the data (since the XML itself should never have a null byte). Either way would work but IMHO the length-based approach is somewhat simpler (doesn't require buffering extra unprocessed data).

That might look something like this:

NetworkStream stream = m_client.GetStream();   //m_client is a TCPclient
using (MemoryStream buffer = new MemoryStream())
using (StreamWriter writer = new StreamWriter(buffer))
{
    int[] a = new int[3] { 10,20,30 };
    XmlSerializer serializer = new XmlSerializer(typeof(int[]));
    serializer.Serialize(writer, a);

    // This code assumes you aren't sending more than 2GB of XML.
    // This allows the other end to use int instead of long for the
    // length to receive.
    byte[] lengthBytes = BitConverter.GetBytes((int)buffer.Length);

    stream.Write(lengthBytes, 0, lengthBytes.Length);
    buffer.Position = 0;
    buffer.CopyTo(stream);
}

Then to receive:

TcpClient client = (TcpClient)p_client;
NetworkStream stream = client.GetStream();

// Using BinaryReader is easier than implementing a correct, blocking read
// of fixed numbers of bytes -- TCP can return as little as a single byte for
// any given receive operation, but BinaryReader insulates us from that.
// Leave the stream open so that we can read more later.
using (BinaryReader binary = new BinaryReader(stream, Encoding.UTF8, true))
{
    int length = binary.ReadInt32();
    byte[] buffer = binary.ReadBytes(length);

    using (MemoryStream streamBuffer = new MemoryStream(buffer))
    using (StreamReader reader = new StreamReader(streamBuffer))
    {
        XmlSerializer serializer = new XmlSerializer(typeof(int[]));
        int[] numbers = (int[])serializer.Deserialize(reader);
        MessageBox.Show(numbers[0].ToString());    //Is not reached
        MessageBox.Show(numbers[1].ToString());
        MessageBox.Show(numbers[2].ToString());
    }
}

(As an aside: technically one doesn't need using for MemoryStream, or to dispose of a MemoryStream object at all. But I use using anyway; it adds very little overhead to the code, and it keeps me in the habit of always disposing streams that I create).

Peter Duniho
  • 68,759
  • 7
  • 102
  • 136
  • At the time of reading, the byte[] buffer fails to read because it gives a memory overflow error. And it makes sense because you the length (from binary.readInt32) that I'm getting is 1.7GB... – Sanketh. K. Jain Feb 18 '15 at 17:08
  • @Sanketh.K.Jain: ah, sorry. Typo in my code example. Passed `stream` to the serializer instead of `buffer`. Please see edited post. (Just goes to show the value of a [_good_, minimal, complete code example](http://stackoverflow.com/help/mcve)...if I'd had that to start with, the code in my answer would have been based on that and actually testable, instead of just being browser-edited :) ). – Peter Duniho Feb 18 '15 at 17:45