views:

1000

answers:

5

For my current project, I need to request XML data over a tcp/ip socket connection. For this, I am using the TcpClient class:

Dim client As New TcpClient()
client.Connect(server, port)

Dim stream As NetworkStream = client.GetStream()
stream.Write(request)
stream.Read(buffer, 0, buffer.length)

// Output buffer and return results...

Now this works fine and dandy for small responses. However, when I start receiving larger blocks of data, it appears that the data gets pushed over the socket connection in bursts. When this happens, the stream.Read call only reads the first burst, and thus I miss out on the rest of the response.

What's the best way to handle this issue? Initially I tried to just loop until I had a valid XML document, but I found that in between stream.Read calls the underlying stream would sometimes get shut down and I would miss out on the last portion of the data.

+2  A: 

You create a loop for reading.

Stream.Read returns int for the bytes it read so far, or 0 if the end of stream is reached.

So, its like:

int bytes_read = 0;
while (bytes_read < buffer.Length)
   bytes_read += stream.Read(buffer, bytes_read, buffer.length - bytes_read);

EDIT: now, the question is how you determine the size of the buffer. If your server first sends the size, that's ok, you can use the above snippet. But if you have to read until the server closes the connection, then you have to use try/catch (which is good idea even if you know the size), and use bytes_read to determine what you received.

int bytes_read = 0;
try
{
   int i = 0;
   while ( 0 < (i = stream.Read(buffer, bytes_read, buffer.Length - bytes_read) )
      bytes_read += i;
}
catch  (Exception e)
{
//recover
}
finally
{
if (stream != null)
   stream.Close();
}
Sunny
That's what I did, but the underlying stream was getting closed in between reads. How does one account for that?
Kevin Pang
Put it in try/catch block. If the stream is closed, most probably your server does not perform properly.
Sunny
Also, I don't know how many bytes I should be receiving for each response.
Kevin Pang
The server is fine. I traced the response using a program and it is indeed sending the full response. The problem is the connection will only support a certain number of bytes per transmission. After it's done transmitting, it closes the underlying stream.
Kevin Pang
Read can return 0 on EOF -- I think it would be better to check for that in the loop because either it will keep returning 0 or throw.
Lou Franco
+1  A: 

Read is not guaranteed to fully read the stream. It returns the number of actual bytes read and 0 if there are no more bytes to read. You should keep looping to read all of the data out of the stream.

Lou Franco
A: 

This is a possible way to do that and get in "response" the response string. If you need the byte array, just save ms.ToArray().

string response;

TcpClient client = new TcpClient();
client.Connect(server, port);
using (NetworkStream ns = c.GetStream())
using (MemoryStream ms = new MemoryStream())
{
    ns.Write(request);

    byte[] buffer = new buffer[512];
    int bytes = 0;

    while(ns.DataAvailable)
    {
        bytes = ns.Read(buffer,0, buffer.Length);
        ms.Write(buffer, 0, bytes);
    }

    response = Encoding.ASCII.GetString(ms.ToArray());
}
Matías
A: 

if i had to enable SSL on this how would I do it ?

Rahul
A: 

I strongly advice you to try WCF for such tasks. It gives you, after a not so steep learning curve, many benefits over raw socket communications. For the task at hand, I agree with the preceeding answers, you should use a loop and dynamically allocate memory as needed.

pomarc