Reputation: 599
I am testing a very simple web server code (either copied it from MSDN sample or somewhere I forgot), I am running it on Raspberry Pi 3 Model B with IoT Core v.10.0.16299.309. I did everything properly including disposing the socket on each request and the code works most of the time.
I only have ONE client, sending 3 very simple HTTP GET requests to this web service every 30 seconds, just a simple URL like this: http://serverIP/get?id=123 It returns the result properly but RANDOMLY during the hour, the request object is empty for some strange reason. I couldn't figure it out why.
private StreamSocketListener _listener;
private int _bufferSize = 8192;
public async void Run(IBackgroundTaskInstance taskInstance)
{
taskInstance.GetDeferral();
_listener = new StreamSocketListener();
_listener.ConnectionReceived += HandleRequest;
await _listener.BindServiceNameAsync("9080");
}
public async void HandleRequest(StreamSocketListener sender, StreamSocketListenerConnectionReceivedEventArgs args)
{
try
{
StringBuilder request = new StringBuilder()
using (IInputStream input = args.Socket.InputStream)
{
byte[] data = new byte[_bufferSize];
IBuffer buffer = data.AsBuffer();
uint bytesRead = _bufferSize;
while (bytesRead == _bufferSize)
{
await input.ReadAsync(buffer, _bufferSize, InputStreamOptions.Partial);
request.Append(Encoding.UTF8.GetString(data, 0, data.Length));
bytesRead = buffer.Length;
// Some other code here to process the request object
// and respond it back to the client. They are irrelevant.
} // while
} // using
} // catch
catch (Exception ex)
{
// Log here
}
finally
{
args.Socket.Dispose();
}
} // method
I thought that there is something wrong with Threading, so I disabled most of the async and await. Interestingly, the result is A LOT BETTER. The request object is 99% fine. Within 12 hours, there were only 3-4 times where the request was empty. I don't think this is the right solution... but I really get stuck on this one and couldn't figure out why. Any help is appreciated.
private StreamSocketListener _listener;
private int _bufferSize = 8192;
public async void Run(IBackgroundTaskInstance taskInstance)
{
taskInstance.GetDeferral();
_listener = new StreamSocketListener();
_listener.ConnectionReceived += HandleRequest;
_listener.BindServiceNameAsync("9080"); // ** No "await"
}
// ** No "async"
public void HandleRequest(StreamSocketListener sender, StreamSocketListenerConnectionReceivedEventArgs args)
{
try
{
StringBuilder request = new StringBuilder()
using (IInputStream input = args.Socket.InputStream)
{
byte[] data = new byte[_bufferSize];
IBuffer buffer = data.AsBuffer();
uint bytesRead = _bufferSize;
while (bytesRead == _bufferSize)
{
// ** No "await"
input.ReadAsync(buffer, _bufferSize, InputStreamOptions.Partial);
request.Append(Encoding.UTF8.GetString(data, 0, data.Length));
bytesRead = buffer.Length;
// Some other code here to process the request object
// and respond it back to the client. They are irrelevant.
} // while
} // using
} // catch
catch (Exception ex)
{
// Log here
}
finally
{
args.Socket.Dispose();
}
} // method
Upvotes: 0
Views: 142
Reputation: 599
Ran out of idea, I changed my code to use Socket.InputStream.AsStreamForRead() instead, then use .ReadLineAsync() just to read the first line only for testing. It seemed to be stable, passing 12+ hours there wasn't an empty request!!!
For what I need, this is actually fine because I am not really building a full function web server, so I don't need to get the complete request. All I need is to get the HTTP GET QueryString and pass the parameters to the GPIO of my Raspberry Pi.
As Vincent suggested, .ReadAsync() has some very interesting behavior. I will definitely try his suggestion later.
public async void HandleRequest(StreamSocketListener sender, StreamSocketListenerConnectionReceivedEventArgs args)
{
string requestString;
try
{
Stream inStream = args.Socket.InputStream.AsStreamForRead();
StreamReader reader = new StreamReader(inStream);
requestString = await reader.ReadLineAsync();
// Some other code here to process the request object
// and respond it back to the client. They are irrelevant
} // catch
catch (Exception ex)
{
// Log here
}
finally
{
args.Socket.Dispose();
}
}
Upvotes: 0
Reputation: 3746
The IInputStream.ReadAsync() documentation has some interesting information that might help:
Always read data from the buffer returned in the IAsyncOperationWithProgress(IBuffer, UInt32). Don't assume that the input buffer contains the data. Depending on the implementation, the data that's read might be placed into the input buffer, or it might be returned in a different buffer. For the input buffer, you don't have to implement the IBuffer interface. Instead, you can create an instance of the Buffer class.
The provided buffer might not contain the data.
You can either use the return buffer and extract the data from it or use DataReader.ReadBuffer() (or DataReader.ReadString() in your case) to get the data from the stream.
In general, it is easier to use a DataReader to read the data from a stream than using the low level API.
You code becomes
byte[] data = new byte[_bufferSize];
IBuffer buffer = data.AsBuffer();
uint bytesRead = _bufferSize;
while (bytesRead == _bufferSize)
{
var readFromStreamBuffer = await input.ReadAsync(buffer, _bufferSize, InputStreamOptions.Partial);
var readBytes = readFromStreamBuffer.ToArray();
request.Append(Encoding.UTF8.GetString(readBytes, 0, readBytes.Length));
bytesRead = readFromStreamBuffer.Length;
}
Or with DataReader.ReadBuffer():
byte[] data = new byte[_bufferSize];
IBuffer buffer = data.AsBuffer();
uint bytesRead = _bufferSize;
var dataReader = new DataReader(input);
while (bytesRead == _bufferSize)
{
await dataReader.LoadAsync(_bufferSize);
var readFromStreamBuffer = dataReader.ReadBuffer();
var readBytes = readFromStreamBuffer.ToArray();
request.Append(Encoding.UTF8.GetString(readBytes, 0, readBytes.Length));
bytesRead = readFromStreamBuffer.Length;
}
Or with DataReader.ReadString():
byte[] data = new byte[_bufferSize];
IBuffer buffer = data.AsBuffer();
uint bytesRead = _bufferSize;
var dataReader = new DataReader(input);
dataReader.UnicodeEncoding = UnicodeEncoding.Utf8;
while (bytesRead == _bufferSize)
{
await dataReader.LoadAsync(_bufferSize);
request.Append(dataReader.ReadString(dataReader.UnconsumedBufferLength));
bytesRead = readFromStreamBuffer.Length;
}
Upvotes: 1