Created
April 23, 2014 14:14
-
-
Save stanroze/11216794 to your computer and use it in GitHub Desktop.
simple async server and client.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
using System; | |
using System.Collections.Generic; | |
using System.Linq; | |
using System.Net; | |
using System.Text; | |
using System.Threading.Tasks; | |
namespace ConsoleApplication1 | |
{ | |
class Program | |
{ | |
static void Main(string[] args) | |
{ | |
using (var server = new SimpleServer("http://localhost:1000/")) | |
{ | |
server.ListenAsync(); | |
var client = new SimpleClient(); | |
//sends 100 requests in parallel | |
client.SendManyAsync("http://localhost:1000/", 100); | |
Console.WriteLine("Waiting.."); | |
//the only blocking | |
Console.ReadKey(); | |
} | |
} | |
} | |
public class SimpleServer : IDisposable | |
{ | |
private readonly string _uri; | |
private readonly HttpListener _httpListener; | |
private bool _stop; | |
public SimpleServer(string uri) | |
{ | |
_uri = uri; | |
_httpListener = new HttpListener(); | |
_httpListener.Prefixes.Add(uri); | |
} | |
public async void ListenAsync() | |
{ | |
_httpListener.Start(); | |
Console.WriteLine("Listening on {0}", _uri); | |
//this listening loop could be done on a bunch of seperate tasks to increase throughput | |
//each task would get a CancellationToken so on Stop that token would be raised | |
//and the task would automatically exit. | |
while (!_stop) | |
{ | |
HttpListenerContext ctx = null; | |
try | |
{ | |
//doesn't block a thread | |
ctx = await _httpListener.GetContextAsync(); | |
} | |
catch (HttpListenerException ex) | |
{ | |
if (ex.ErrorCode == 995) | |
return; | |
} | |
if (ctx == null) continue; | |
Console.WriteLine("Recieved a request on {0}", ctx.Request.Url); | |
SendOkayAsync(ctx); | |
} | |
} | |
private async void SendOkayAsync(HttpListenerContext context) | |
{ | |
//this will execute and let the while loop keep going. | |
await Task.Run(async () => | |
{ | |
try | |
{ | |
//got a request | |
const string json = @"{ ""status"": ""Ok"" }"; | |
using (var response = context.Response) | |
{ | |
response.Headers.Add(HttpResponseHeader.CacheControl, "private, no-store"); | |
response.ContentType = "application/json"; | |
response.StatusCode = (int) HttpStatusCode.OK; | |
var messageBytes = Encoding.UTF8.GetBytes(json); | |
response.ContentLength64 = messageBytes.Length; | |
await response.OutputStream.WriteAsync(messageBytes, 0, messageBytes.Length); | |
response.OutputStream.Close(); | |
} | |
} | |
catch (Exception ex) | |
{ | |
//close the connection | |
context.Response.Abort(); | |
} | |
}); | |
//will only execute after the response was sent | |
Console.WriteLine("Sent response"); | |
} | |
public void Stop() | |
{ | |
_stop = true; | |
_httpListener.Stop(); | |
} | |
public void Dispose() | |
{ | |
this.Stop(); | |
} | |
} | |
public class SimpleClient | |
{ | |
public async void SendManyAsync(string target, int count) | |
{ | |
var requestTasks = new List<Task<string>>(); | |
for (int i = 0; i < count; i++) | |
requestTasks.Add(SendAsync("http://localhost:1000/")); | |
//waits for all tasks to complete without blocking a thread. | |
string[] responses = await Task.WhenAll(requestTasks); | |
//all requests are back | |
Console.WriteLine("Receieved {0} responses.", responses.Count(response => response != null)); | |
} | |
public async Task<string> SendAsync(string target) | |
{ | |
var request = WebRequest.CreateHttp(target); | |
try | |
{ | |
request.Method = "GET"; | |
request.ContentType = "text/plain"; | |
var responseRaw = await request.GetResponseAsync().WithTimeout(5000); | |
if (responseRaw != null) | |
{ | |
using (var response = (HttpWebResponse)responseRaw) | |
{ | |
var responseStream = response.GetResponseStream(); | |
if (responseStream == null) | |
return null; | |
var buffer = new byte[response.ContentLength]; | |
await responseStream.ReadAsync(buffer, 0, (int) response.ContentLength); | |
var responseStr = Encoding.Default.GetString(buffer); | |
Console.WriteLine("Response: {0}", responseStr); | |
return responseStr; | |
} | |
} | |
request.Abort(); | |
return null; | |
} | |
catch (Exception ex) | |
{ | |
request.Abort(); | |
return null; | |
} | |
} | |
} | |
public static class TaskExtenstions | |
{ | |
public static async Task<T> WithTimeout<T>(this Task<T> task, int timeout) | |
{ | |
//this waits on any task to complete, the 2nd task is similar to Thread.Sleep | |
//if the 1st task completes WhenAny returns that as the completed task | |
if (await Task.WhenAny(task, Task.Delay(timeout)) == task) | |
return await task; | |
return default(T); | |
} | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment