Skip to content

Instantly share code, notes, and snippets.

@stanroze
Created April 23, 2014 14:14
Show Gist options
  • Save stanroze/11216794 to your computer and use it in GitHub Desktop.
Save stanroze/11216794 to your computer and use it in GitHub Desktop.
simple async server and client.
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