views:

59

answers:

2

Hello Everyone,

I am connecting to a server which sends updates about financial information. The code I post below will work correctly for a few minutes then usually blows up when it tries to do an EndRead (where I have noted), because there was 0 bytes at that moment to be read. Obviously in the financial world some things can stay the same price for a few minutes or more which seems to be the source of my problem. Also I suspect there is a better way to do this than what I am showing below. If you have a more efficient or elegant way to achieve the same result I am all ears. Basically the code I have now is assembled from random snippets of code from here and elsewhere. I just can't seem to find a sample that puts all the pieces together the way I need them.

Thanks in advance,

Bob

Edit: I should have clarified that I basically understand what is going wrong, I just can't find the reason why the stream is being closed. I think the best answer will be a different implementation that does the same thing. I tried making WebClient do this but didn't have any luck with that.

public IAsyncResult BeginStreamingData()
    {
        postUrl = "https://" + "myfinancialbytestreamsource.com/";
        byte[] buffer = Encoding.ASCII.GetBytes(postdata);
        HttpWebRequest httpWebRequest = (HttpWebRequest)WebRequest.Create(postUrl);
        httpWebRequest.AutomaticDecompression = DecompressionMethods.GZip;
        httpWebRequest.Method = "POST";
        httpWebRequest.ContentType = "application/x-www-form-urlencoded";
        httpWebRequest.ContentLength = buffer.Length;
        httpWebRequest.Timeout = 6000000;
        httpWebRequest.ReadWriteTimeout = 6000000;
        Stream PostData = httpWebRequest.GetRequestStream();
        PostData.Write(buffer, 0, buffer.Length);
        PostData.Close();

        IAsyncResult result =
          (IAsyncResult)httpWebRequest.BeginGetResponse(new AsyncCallback(ResponseCallback), new AsyncState
          {
              request = httpWebRequest
          });

        return new CompletedAsyncResult<string>("Completed stream.");
    }

internal void ResponseCallback(IAsyncResult asynchronousResult)
    {
        AsyncState state = asynchronousResult.AsyncState as AsyncState;
        HttpWebRequest httpWebRequest = state.request;
        state.response = (HttpWebResponse)httpWebRequest.EndGetResponse(asynchronousResult);

        Stream responseStream = state.response.GetResponseStream();
    byte[] buffer = new byte[1024 * 10];

    var completedEvent = new ManualResetEvent(false);

    responseStream.BeginRead(buffer, 0, buffer.Length,
                            AsyncRead,
                            new AsyncState
                            {
                                b = buffer,
                                s = responseStream,
                                e = completedEvent
                            });

    completedEvent.WaitOne();
    }

private void AsyncRead(IAsyncResult ar)
    {
        AsyncState state = ar.AsyncState as AsyncState;
        int read = 0;

//BLOWS UP HERE (IOException) WHEN IT ENDS A READ THAT HAD 0 BYTES IN IT.

read = state.s.EndRead(ar);

            if (read == 0)
            {
                // signal completion
                state.e.Set();
                return;
            }
            else
            {
                //this is where I'm parsing the bytes into .net objects.
                ParseBytes(state.b, read);

            }
            // read again
            state.s.BeginRead(state.b, 0, state.b.Length, AsyncRead, state);
    }

//Here is the class that stores the state.
private class AsyncState
    {
        public Stream s;
        public ManualResetEvent e;
        public byte[] b;
        public HttpWebRequest request;
        public HttpWebResponse response;
    }
A: 

0 bytes means the stream has ended. The APM methods do not timeout, and the documentation clearly states that a read of zero byes indicates that the connection is closed.

Streams return zero (0) only at the end of the stream

Are you sure you're troubleshooting in the right place? The IOException almost certainly contains more info which would be useful.

spender
A: 

According per documentation, IOException is only thrown if the stream is closed. So it appears that the socket connection has been disconnected and socket is closed.

IOException 
The stream is closed or an internal error has occurred.

I suggest you track the error and re-open the connection.

http://msdn.microsoft.com/en-us/library/system.io.stream.endread.aspx

Aliostad
I would have to do that every 2-3 minutes which just wouldn't be reasonable for a real time finance app.
Beaker
Awe both said, there is nothing you can do at the client side when sever has closed the connection. Also using http is a very odd choice if not a bad one. Http is a request response protocol and u need to use tcp for keeping the con open. Http uses tcp but u have to set the keepalive but serve can igonre it. Opening a new connecion by the way is not such a bad thing, in HTTP it can always happen.
Aliostad
Another thing coming to mind is that if you are using HTTP, you just need to do a request response and leave the connection to the TCP stack to handle. If you set keep alive, connection will stay open if you go back to the server again.
Aliostad
If HttpWebRequest possibly isn't the best choice, can you recommend a specific .net class which I could use for this purpose instead?
Beaker
Depends on your server's implementation. Client and server go hand in hand, can you change the server or you are just writing the client?
Aliostad
I can only work on the client side.
Beaker
So if the server is HTTP, then you need to poll the service but by setting the KeepAlive to true, underlying TCP connection will not be closed - unless server runs out of connections and closes the connection. Just use a Webclient to contact the server (use DownloadData) and do this every how many seconds you have to.
Aliostad
Usually, graceful socket disconnects by the server should be handled by the client without any problems. If you are getting IOException, this could mean that there is either a bug in the HttpWebRequest connection management, or the server is prematurely closing the connection when it is not supposed to - either due to a bug on the server side, or high load or something else. The best way to debug this is to create a system.net tracelog (see http://ferozedaud.blogspot.com/2009/08/tracing-with-systemnet.html ) and also to get a wireshark trace at the same time that might show the server abort.
feroze