Having below reference code for Async TCP server, I want to pass CancellationToken to OnDataReceived:
public sealed class TcpServer : IDisposable
{
private readonly TcpListener _listener;
private CancellationTokenSource _tokenSource;
private CancellationToken _token;
private bool _listening;
public event EventHandler<ConnectedEventArgs> OnDataReceived;
public TcpServer(IPAddress address, int port)
{
_listener = new TcpListener(address, port);
}
public async Task StartAsync(CancellationToken? token = null)
{
_tokenSource = CancellationTokenSource.CreateLinkedTokenSource(token ?? new CancellationToken());
_token = _tokenSource.Token;
_listener.Start();
_listening = true;
try
{
while (!_token.IsCancellationRequested)
{
await Task.Run(async () =>
{
var tcpClientTask = _listener.AcceptTcpClientAsync();
var tcpClient = await tcpClientTask;
OnDataReceived?.Invoke(tcpClient, new ConnectedEventArgs(tcpClient.GetStream()));
}, _token);
}
}
finally
{
_listener.Stop();
_listening = false;
}
}
public void Stop()
{
_tokenSource?.Cancel();
}
public void Dispose()
{
Stop();
}
}
public class ConnectedEventArgs : EventArgs
{
public NetworkStream Stream { get; private set; }
public ConnectedEventArgs(NetworkStream stream)
{
Stream = stream;
}
}
The following code, shows how I am consuming the above code, currently in ReadStreamBytesAsync I am passing new CancellationToken() instead I want to pass the token argument from MainAsync:
class Program
{
static void Main(string[] args)
{
InitAsyncWork();
Console.WriteLine("ended.");
Console.ReadKey();
}
static void InitAsyncWork()
{
var cts = new CancellationTokenSource();
Console.CancelKeyPress += (s, e) =>
{
e.Cancel = true;
cts.Cancel();
};
MainAsync(cts.Token).GetAwaiter().GetResult();
}
static async Task MainAsync(CancellationToken token)
{
using (var server = new TcpServer(IPAddress.Any, 54001))
{
server.OnDataReceived += TcpClientOnlyReceive;
await server.StartAsync(token);
}
}
private static async void TcpClientOnlyReceive(object sender, ConnectedEventArgs e)
{
try
{
using (TcpClient client = (TcpClient)sender)
using (NetworkStream stream = e.Stream)
{
while (Utilities.IsTcpClientConnected(client))
{
if (client.Available == 0)
{
await Task.Delay(50);
continue;
}
byte[] rawX = await ReadStreamBytesAsync(stream, client.ReceiveBufferSize, new CancellationToken());
}//while(client.Connected)
}//using(client)using(stream)
}
catch (Exception ex)
{
Utilities.logger.Error("TcpClientOnlyReceive():Exception!!\r\nMsg: {1}\r\nStack: {2}", ex.Message, ex.StackTrace);
}
}
private static async Task<byte[]> ReadStreamBytesAsync(NetworkStream stream, int maxBytesToRead, CancellationToken token)
{
var bytesRead = 0;
var totalBytesRead = 0;
var clientMsg = new byte[maxBytesToRead];
byte[] contentBytes;
using (MemoryStream ms = new MemoryStream())
{
do
{
bytesRead = await stream.ReadAsync(clientMsg, totalBytesRead, clientMsg.Length - totalBytesRead, token);
await ms.WriteAsync(clientMsg, totalBytesRead, bytesRead, token);
totalBytesRead += bytesRead;
}
while (bytesRead > 0 && stream.DataAvailable);
contentBytes = ms.ToArray();
}
return contentBytes;
}
}
Should I define cts as global variable? Or modify ConnectedEventArgs class so that it accept CancellationToken as property ?
Clarifications:
Based on the received comments and 1 vote to close the question!
My main objective is to close the application once the user press ctrl+c, currently that's prevent accepting new connections but it won't cancel the established connections.
I never heard of Tasks meant to replace events, if that's true, please share a reference.
It will be great to share better version of the code, if its BAD design! personally I think its clean and robust.
Related
I have a problem: the server doesn't receive any data from the client.
Here is server initialization:
public void Start()
{
var listener = new TcpListener(IPAddress.Any, Port);
listener.Start();
Task.Run(
async () =>
{
while (!this.cancellationToken.IsCancellationRequested)
{
var client = await listener.AcceptTcpClientAsync();
var stream = client.GetStream();
string request = await ReceiveRequestAsync(stream);
await RequestHandlerAsync(request, stream);
}
listener.Stop();
}, this.cancellationToken);
}
Here is requesting client code (it is from unit test so server is initialized right here):
var server = new SimpleFtpServer();
server.Start();
using (TcpClient client = new TcpClient(RequestUri, Port))
{
NetworkStream stream = client.GetStream();
StreamWriter writer = new StreamWriter(stream)
{
AutoFlush = true,
};
writer.Write("zapros");
using (StreamReader reader = new StreamReader(stream))
{
Console.Writeline(reader.ReadToEnd());
}
}
server.Stop();
It worth saying that I have started learning async/await in C# really recently so probably the problem is in usage of them.
Thank you in advance!
its probably not perfect however I am in the same situation as you and created a Async TCP Client/Server for practice and experimentation.
The below is an excerpt of my implementation, it works s
Server:
public class AsyncServerDemo
{
private CancellationTokenSource cancel;
private readonly TcpListenerEx listener;
private Task WaitingForConnections;
private Timer timerCallAcceptClients;
public bool IsRunning { get; private set; }
public AsyncServerDemo(int port)
{
cancel = new CancellationTokenSource();
listener = new TcpListenerEx(IPAddress.Any, port);
}
private Task<string> WaitForMessageAsync(TcpClient client, CancellationToken token)
{
return Task.Run(() =>
{
StringBuilder sb = new StringBuilder();
bool dataAvailable = false;
while (!token.IsCancellationRequested)
{
while (client.Client.Available > 0)
{
dataAvailable = true;
int buffered = client.Client.Available;
byte[] buffer = new byte[buffered];
client.Client.Receive(buffer);
sb.Append(Encoding.ASCII.GetString(buffer));
}
if (dataAvailable)
{
dataAvailable = false;
return sb.ToString();
}
};
return string.Empty; //timeout
});
}
private Task AcceptClientAsync()
{
return Task.Factory.StartNew(async () =>
{
IsRunning = true && !cancel.IsCancellationRequested;
while (!cancel.IsCancellationRequested)
{
if (!listener.Pending())
{
continue;
}
TcpClient newClient = await listener.AcceptTcpClientAsync().ConfigureAwait(false);
Stopwatch timeout = new Stopwatch();
timeout.Restart();
string message = await WaitForMessageAsync(newClient, new CancellationTokenSource(TimeSpan.FromSeconds(5)).Token);
if (message != null)
{
//TODO: Message recieved
}
timeout.Stop();
}
});
}
public void Start()
{
listener.Start();
timerCallAcceptClients = new Timer(new TimerCallback((state) =>
{
AcceptClientAsync();
}), null, 0, (int)TimeSpan.FromSeconds(1).TotalMilliseconds);
}
public async void Stop()
{
if (!IsRunning) return;
using (cancel)
cancel.Cancel();
timerCallAcceptClients.Dispose();
if (WaitingForConnections != null)
await WaitingForConnections;
cancel = null;
listener.Stop();
IsRunning = false;
cancel = new CancellationTokenSource();
}
}
Client:
public class ClientExDemo
{
private Task<string> WaitForMessage;
private NetworkStream currentStream;
private CancellationTokenSource messageToken;
public EventHandler<ClientEx> OnServerFound;
public TcpClient Connection;
public EventHandler<string> OnMessage;
public async Task StartListenAsync(CancellationTokenSource token = null)
{
if (token == null)
messageToken = new CancellationTokenSource();
else
messageToken = token;
currentStream = Connection.GetStream();
string message = "";
if (message.Length > 0)
OnMessage?.Invoke(this, message);
if (!messageToken.IsCancellationRequested)
{
await StartListenAsync(token);
}
Timeout();
}
protected virtual void Timeout()
{
}
public async Task WaitForServerAsync(string ip, int port)
{
do
{
try
{
await Connection.ConnectAsync(ip, port);
}
catch (SocketException x)
{
}
await Task.Delay(50);
} while (!Connection.Connected);
}
public void StopListen()
{
using (messageToken)
{
messageToken.Cancel();
}
try
{
WaitForMessage.GetAwaiter().GetResult();
}
catch (AggregateException)
{
}
currentStream.Close();
messageToken = null;
currentStream = null;
WaitForMessage = null;
}
public ClientExDemo()
{
Connection = new TcpClient();
OnServerFound += ServerFound;
}
private void ServerFound(object sender, ClientEx args)
{
}
public void Send(string message)
{
Connection.Client.Send(Encoding.ASCII.GetBytes(message));
}
}
You can send messages from the client in a simple console application:
ClientEx client= new ClientEx();
await client.WaitForServerAsync(ip, port);
string msg = string.Empty;
do
{
Console.Write("Send Message: ");
msg = Console.ReadLine();
shell.Send(msg);
} while (msg != "q");
Console.WriteLine();
Console.WriteLine("BYE");
Console.ReadKey();
Update: FOund the error. my code was working as intended but my colleague added a small code to the device i was communicating with. The result was that it ignored my second command because it was busy doing something else.
Fixed it and now its working again.
At the beginning of the week I asked a question about restricting access to the serial port to just one instance.
Someone really helped me there and mentioned the SemaphoreSlim to me.
This really did the trick at that time and I thought that would be it.
Now a few days later I finally fixed aynother major problem and while testing it I noticed the semaphore not working anymore.
I rolled back my code to a previous version where I just implemented the semaphore and where it was definitely working.
But even that code is not working anymore.
I didn't change anything in my Visual Studio, my PC or my Raspberry...
Does anybody have an idea why something which should theoretically work doesn't work anymore?
Would really appreciate the help here :)
Added a very short version of the code I'm using.
Normally I would process the data I get into Int or string arrays.
I would also have a CRC16 verification and everything would be contained in try/catch blocks to catch exceptions when they occur.
But testwise this is everything I need so far.
I will try to provide more detailed information if needed. Just let me know.
Current behavior:
First task starts and works fine.
Second task doesnt start and every new task after that doesn't start either.
Expected behavior:
Start first task and complete it.
After the first task is done load second task and finish it.
When I start another Task after that it should also run that.
Mainpage.xaml
public MainPage()
{
this.InitializeComponent();
InitPort();
}
private async void getData_Click(object sender, RoutedEventArgs e)
{
// Call the async operations.
// I usually don't call the same operation twice at the same time but it's just a test here.
// In normal usage the data contains different sizes.
await getData();
await getData();
}
private async Task getData()
{
ReadData readData = new ReadData();
byte[] readOutput = await readData.ReadParaBlock();
DisplayTextBox.Text = BitConverter.ToString(readOutput);
}
public async void InitPort()
{
string success = await ReadWriteAdapter.Current.Init();
DisplayTextBox.Text = success;
}
ReadData.cs
public class ReadData
{
private ReadBlock readBlock = new ReadBlock();
public async Task<byte[]> ReadParaBlock()
{
// Load task into the semaphore
await ReadWriteAdapter.Current.semaphore.WaitAsync();
// start writing to device
await readBlock.Write();
// dropped check of recieved checksum because obsolete for test
// start reading from device
byte[] recievedArray = await readBlock.Read();
// release the task from semaphore
ReadWriteAdapter.Current.semaphore.Release();
return recievedArray;
}
}
ReadBlock.cs
public class ReadBlock
{
public async Task<uint> Write()
{
// Command sent to device to get data
byte[] WriteArray = System.Text.Encoding.ASCII.GetBytes("getdata");
return await ReadWriteAdapter.Current.WriteAsync(WriteArray);
}
public async Task<byte[]> Read()
{
byte[] ListenOut = await ReadWriteAdapter.Current.Listen(100);
// dropped data conversion because obsolete for test
return ListenOut;
}
}
ReadWriteAdapter.cs
public class ReadWriteAdapter
{
public SemaphoreSlim semaphore { get; private set; }
private static readonly Object lockObject = new object();
private static ReadWriteAdapter instance;
private DataWriter dataWriter = null;
private DataReader dataReader = null;
private SerialDevice serialPort = null;
public static ReadWriteAdapter Current
{
get
{
if (instance == null)
{
lock (lockObject)
{
if (instance == null)
{
instance = new ReadWriteAdapter();
}
}
}
return instance;
}
}
private ReadWriteAdapter()
{
this.semaphore = new SemaphoreSlim(1, 1);
}
// initialize the serial port and configure it
public async Task<string> Init()
{
string aqs = SerialDevice.GetDeviceSelector();
DeviceInformationCollection devices = await DeviceInformation.FindAllAsync(aqs, null);
if (devices.Any())
{
string deviceId = devices[0].Id;
serialPort = await SerialDevice.FromIdAsync(deviceId);
serialPort.WriteTimeout = TimeSpan.FromMilliseconds(1000);
serialPort.ReadTimeout = TimeSpan.FromMilliseconds(1000);
serialPort.BaudRate = 19200;
serialPort.Parity = SerialParity.None;
serialPort.StopBits = SerialStopBitCount.One;
serialPort.DataBits = 8;
serialPort.Handshake = SerialHandshake.None;
dataWriter = new DataWriter(serialPort.OutputStream);
dataReader = new DataReader(serialPort.InputStream);
}
return "found port";
}
// start to listen to the serial port
public async Task<byte[]> Listen(uint BufferLength)
{
byte[] listen = new byte[BufferLength];
dataReader = new DataReader(serialPort.InputStream);
listen = await ReadAsync(BufferLength);
if (dataReader != null)
{
dataReader.DetachStream();
dataReader.Dispose();
dataReader = null;
}
return listen;
}
// function to read and interpret the data from serial port
private async Task<byte[]> ReadAsync(uint ReadBufferLength)
{
Task<uint> loadAsyncTask;
byte[] returnArray = new byte[ReadBufferLength];
dataReader.InputStreamOptions = InputStreamOptions.Partial;
loadAsyncTask = dataReader.LoadAsync(ReadBufferLength).AsTask();
uint bytesRead = await loadAsyncTask;
if (bytesRead > 0)
{
dataReader.ReadBytes(returnArray);
}
return returnArray;
}
// write the data using the serial port
public async Task<uint> WriteAsync(byte[] data)
{
dataWriter.WriteBytes(data);
Task<uint> storeAsyncTask = dataWriter.StoreAsync().AsTask();
return await storeAsyncTask;
}
}
This looks like it should work, and indeed, a version modified to use a MemoryStream instead of a serial port works just fine. I don't have access to the I/O APIs you're using, so I don't know anything about how they're supposed to be used. The semaphore, however, seems to be doing its job just fine. Below you'll find a simplified version of your example, with buttons to run GetData() in serial or in parallel.
If there is a problem with the semaphore, it could only be that you are not calling Release() from inside a finally. Otherwise, your use of the semaphore looks fine.
To see that the semaphore is working, you need only change the initializer to new SemaphoreSlim(8, 8) and see the threads clobber each other.
ReadBlock.cs:
public class ReadBlock {
private static int nextString;
private static readonly string[] strings = {
"ONE ", "TWO ", "THREE", "FOUR ",
"FIVE ", "SIX ", "SEVEN", "EIGHT"
};
public static async Task<byte[]> ReadParaBlock() {
var id = Interlocked.Increment(ref nextString) - 1;
var name = strings[id % strings.Length];
try
{
await ReadWriteAdapter.Current.Semaphore.WaitAsync();
Trace.WriteLine($"[{name.Trim()}] Entered Semaphore");
await Write(Encoding.ASCII.GetBytes("TEST_" + name));
return await Read();
}
finally {
Trace.WriteLine($"[{name.Trim()}] Exiting Semaphore");
ReadWriteAdapter.Current.Semaphore.Release();
}
}
public static async Task<uint> Write(byte[] bytes) =>
await ReadWriteAdapter.Current.WriteAsync(bytes);
public static async Task<byte[]> Read() => await ReadWriteAdapter.Current.Listen(10);
}
ReadWriteAdapter.cs:
public class ReadWriteAdapter {
private static ReadWriteAdapter instance;
public static ReadWriteAdapter Current
=> LazyInitializer.EnsureInitialized(
ref instance,
() => new ReadWriteAdapter());
private readonly MemoryStream stream = new MemoryStream();
public SemaphoreSlim Semaphore { get; } = new SemaphoreSlim(1, 1);
public async Task<string> Init()
=> await Task.FromResult("found port");
public async Task<byte[]> Listen(uint bufferLength)
=> await ReadAsync(bufferLength);
private async Task<byte[]> ReadAsync(uint readBufferLength) {
await Task.CompletedTask;
var returnArray = new byte[readBufferLength];
await stream.ReadAsync(returnArray, 0, returnArray.Length);
return returnArray;
}
public async Task<uint> WriteAsync(byte[] data) {
stream.SetLength(stream.Capacity);
stream.Position = 0;
await Task.Delay(1);
await stream.WriteAsync(data, 0, data.Length);
stream.SetLength(data.Length);
stream.Position = 0;
return (uint)data.Length;
}
}
MainWindow.xaml.cs:
public partial class MainWindow {
private class TextBoxTraceListener : TraceListener {
private readonly TextBoxBase _textBox;
public TextBoxTraceListener(TextBoxBase textBox)
=> _textBox = textBox;
public override void WriteLine(string message)
=> Write(message + Environment.NewLine);
public override void Write(string message) {
_textBox.Dispatcher.BeginInvoke(
DispatcherPriority.Normal,
new Action(() => _textBox.AppendText(message)));
}
}
public MainWindow() {
TaskScheduler.UnobservedTaskException +=
(s, e) => Trace.TraceError(e.ToString());
InitializeComponent();
Trace.Listeners.Add(new TextBoxTraceListener(textBox));
InitPort();
}
private async void InitPort() {
textBox.AppendText(await ReadWriteAdapter.Current.Init());
}
private async void OnGetDataInSerialClick(object sender, RoutedEventArgs e) {
textBox.Clear();
for (var i = 0; i < 8; i++) await GetData();
}
private async void OnGetDataInParallelClick(object sender, RoutedEventArgs e) {
textBox.Clear();
await Task.WhenAll(Enumerable.Range(0, 8).Select(i => GetData()));
}
private async Task GetData() {
await Task.Delay(50).ConfigureAwait(false); // Get off the UI thread.
Trace.WriteLine(Encoding.ASCII.GetString(await ReadBlock.ReadParaBlock()));
}
}
MainWindow.xaml:
<Window x:Class="WpfTest2.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
<DockPanel LastChildFill="True">
<Button DockPanel.Dock="Bottom" Click="OnGetDataInSerialClick"
Content="Load Data in Serial" />
<Button DockPanel.Dock="Bottom" Click="OnGetDataInParallelClick"
Content="Load Data in Parallel" />
<TextBox x:Name="textBox" TextWrapping="Wrap" />
</DockPanel>
</Window>
I am having an issue working with async methods - namely nested async.
I start off a background task:
public sealed class StartupTask : IBackgroundTask
{
public async void Run(IBackgroundTaskInstance taskInstance)
{
taskInstance.GetDeferral();
ServerWorkers.WebServer server = new ServerWorkers.WebServer();
await ThreadPool.RunAsync(workItem =>
{
AnotherSync.Get();
server.Start();
});
}
public static async void Get()
{
using (var client = new HttpClient())
{
client.BaseAddress = new Uri(Shared.URL);
client.DefaultRequestHeaders.Accept.Clear();
client.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue(Shared.HeaderType));
using (var response = await client.GetAsync(route + "?" + GeneralTags.COMPANY_REF + "=" + ApplicationObject.CompanyRef)) //.Result)
{
if (response.IsSuccessStatusCode)
{
ApplicationObject.PrintData = JsonConvert.DeserializeObject<Model.Print>(response.Content.ReadAsStringAsync().Result);
}
else
{
evError(new Exception(String.Format("{0}: {1}", (int)response.StatusCode, response.ReasonPhrase)), ErrorTags.PRINT_GET);
}
}
}
}
internal class WebServer
{
private const uint BufferSize = 8192;
public void Start()
{
StreamSocketListener listener = new StreamSocketListener();
listener.BindServiceNameAsync("8080");
listener.ConnectionReceived += async (sender, args) =>
{
StringBuilder request = new StringBuilder();
using (IInputStream input = args.Socket.InputStream)
{
byte[] data = new byte[BufferSize];
IBuffer buffer = data.AsBuffer();
uint dataRead = BufferSize;
while (dataRead == BufferSize)
{
await input.ReadAsync(buffer, BufferSize, InputStreamOptions.Partial);
request.Append(Encoding.UTF8.GetString(data, 0, data.Length));
dataRead = buffer.Length;
}
}
using (IOutputStream output = args.Socket.OutputStream)
{
using (Stream response = output.AsStreamForWrite())
{
byte[] bodyArray = Encoding.UTF8.GetBytes("<html><body>Hello, World!</body></html>");
var bodyStream = new MemoryStream(bodyArray);
var header = "HTTP/1.1 200 OK\r\n" +
$"Content-Length: {bodyStream.Length}\r\n" +
"Connection: close\r\n\r\n";
byte[] headerArray = Encoding.UTF8.GetBytes(header);
await response.WriteAsync(headerArray, 0, headerArray.Length);
await bodyStream.CopyToAsync(response);
await response.FlushAsync();
}
}
};
}
}
Then my app acts a web server.. It does not exit out.
If I add this so I have:
public async void Run(IBackgroundTaskInstance taskInstance)
{
taskInstance.GetDeferral();
ServerWorkers.WebServer server = new ServerWorkers.WebServer();
await ThreadPool.RunAsync(workItem =>
{
AnotherSync.Get();
AnotherSync.Get();
server.Start();
});
}
internal class AnotherSync
{
public static event delError evError;
private const string route = "/api/Print";
static wsPrint.IPrint wsPrint = new wsPrint.PrintClient();
public static void Get()
{
using (var client = new HttpClient())
{
client.BaseAddress = new Uri(Shared.URL);
client.DefaultRequestHeaders.Accept.Clear();
client.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue(Shared.HeaderType));
using (var response = client.GetAsync(route + "?" + GeneralTags.COMPANY_REF + "=" + ApplicationObject.CompanyRef).Result)
{
if (response.IsSuccessStatusCode)
{
ApplicationObject.PrintData = JsonConvert.DeserializeObject<Model.Print>(response.Content.ReadAsStringAsync().Result);
}
else
{
evError(new Exception(String.Format("{0}: {1}", (int)response.StatusCode, response.ReasonPhrase)), ErrorTags.PRINT_GET);
}
}
}
}
}
then the application exists out.
I may be mistaken but is it because I am using nested async methods?
This is an example of what a thread should look like,
public sealed class StartupTask : IBackgroundTask
{
//has to be declared here to keep the handle alive and prevent garbage collection
private ServerWorkers.WebServer server = new ServerWorkers.WebServer();
public async void Run(IBackgroundTaskInstance taskInstance)
{
//Start the server running no need for await as its an synchronous
server.Start();
//don't exit until work is compelted
while(server.Running)
{
//give up processor and allow other work to occur while you are waiting
await Task.Yield();
}
}
internal class WebServer
{
private const uint BufferSize = 8192;
private StreamSocketListener listener;
public bool Running { get; private set;}= false;
public void Start()
{
Lock(this){//prevent any other code interupting
if(!Running )//prevent multiple starts
{
listener = new StreamSocketListener();
listener.BindServiceNameAsync("8080");
listener.ConnectionReceived += listener_ConnectionReceived;
Running = true;
}
}
}
public void Stop()
{
Lock(this){//prevent any other code interupting
listener.Close();
listener.Dispose();
listener = null;
Running = false;
}
}
public void listener_ConnectionReceived(StreamSocketListener sender, StreamSocketListenerConnectionReceivedEventArgs args)
{
//process data from listerner's event
}
}
}
Note i'm not using the Windows Application Framework so might be different there
I have problem with HttpListenerResponse class when client close connection I can´t copy response to outputStream it is logical because stream is interrupted. Is there some easy way how can fix it or handle this exceptions. I know HttpListener is not best solution for proxy server.
I try set up property IgnoreWriteExceptions on true but It´s not working.
public class Ask
{
private int requestCounter = 0;
private ManualResetEvent stopEvent = new ManualResetEvent(false);
public class HttpListenerCallbackState
{
private readonly HttpListener _listener;
private readonly System.Threading.AutoResetEvent _listenForNextRequest;
public HttpListenerCallbackState(HttpListener listener)
{
if (listener == null) throw new ArgumentNullException("listener");
_listener = listener;
_listenForNextRequest = new AutoResetEvent(false);
}
public HttpListener Listener { get { return _listener; } }
public AutoResetEvent ListenForNextRequest { get { return _listenForNextRequest; } }
}
public void ListenAsynchronously(IEnumerable<string> prefixes)
{
HttpListener listener = new HttpListener();
foreach (string s in prefixes)
{
listener.Prefixes.Add(s);
}
listener.IgnoreWriteExceptions = true;
listener.Start();
HttpListenerCallbackState state = new HttpListenerCallbackState(listener);
ThreadPool.QueueUserWorkItem(Listen, state);
}
public void StopListening()
{
stopEvent.Set();
}
private void Listen(object state)
{
HttpListenerCallbackState callbackState = (HttpListenerCallbackState)state;
while (callbackState.Listener.IsListening)
{
callbackState.Listener.BeginGetContext(new AsyncCallback(ListenerCallback), callbackState);
int n = WaitHandle.WaitAny(new WaitHandle[] { callbackState.ListenForNextRequest, stopEvent });
if (n == 1)
{
// stopEvent was signalled
callbackState.Listener.Stop();
break;
}
}
}
private void ListenerCallback(IAsyncResult ar)
{
HttpListenerCallbackState callbackState = (HttpListenerCallbackState)ar.AsyncState;
HttpListenerContext context = null;
int requestNumber = Interlocked.Increment(ref requestCounter);
try
{
context = callbackState.Listener.EndGetContext(ar);
}
catch (Exception ex)
{
return;
}
finally
{
callbackState.ListenForNextRequest.Set();
}
if (context == null) return;
HttpListenerRequest request = context.Request;
// add Proxy and network credentials
HttpWebRequest webReq = (HttpWebRequest)WebRequest.Create(request.Url.AbsoluteUri);
// get response
HttpWebResponse response = (HttpWebResponse)webReq.GetResponse();
Stream responseOut = context.Response.OutputStream;
int CopyByte = CopyStream(response, responseOut);
responseOut.Close();
}
}
}
I have implemented an asynchronous http listener in c#.
I followed the tutorial provided here by Microsoft
and found another tutorial which i stupidly not bookmarked and now can't find again. Meaning that I have some code that I would not have written that way myself but the explanations provided made sense so I followed that.
Now I am facing two problems:
First, I have to restart the listener after each request with Listener.Stop() and then call the StartListening method and again and second, when I do this, I receive each request twice.
The request does net get sent twice, but I receive it twice.
It does not however get received twice when I pause the Thread I am listening on for about 2 seconds.
I am sorry if I am quite vague in my explanations, but so is my understanding of my problem, I have no idea what is causing it.
Since the callback method is where most of the stuff happens, I will just post it, please tell me if you need any more code.
Any help will be gladly appreciated, since I am really stuck on this one.
public void ListenAsynchronously()
{
if (listener.Prefixes.Count == 0) foreach (string s in prefixes) listener.Prefixes.Add(s);
try
{
listener.Start();
}
catch (Exception e)
{
Logging.logException(e);
}
System.Threading.ThreadPool.QueueUserWorkItem(new System.Threading.WaitCallback(Listen));
}
private void Listen(object state)
{
while (listener.IsListening)
{
listener.BeginGetContext(new AsyncCallback(ListenerCallback), listener);
listenForNextRequest.WaitOne();
}
}
private void ListenerCallback(IAsyncResult ar)
{
HttpListener httplistener = ar.AsyncState as System.Net.HttpListener;
System.Net.HttpListenerContext context = null;
int requestNumber = System.Threading.Interlocked.Increment(ref requestCounter);
if (httplistener == null) return;
try
{
context = httplistener.EndGetContext(ar);
}
catch(Exception ex)
{
return;
}
finally
{
listenForNextRequest.Set();
}
if (context == null) return;
System.Net.HttpListenerRequest request = context.Request;
if (request.HasEntityBody)
{
using (System.IO.StreamReader sr = new System.IO.StreamReader(request.InputStream, request.ContentEncoding))
{
string requestData = sr.ReadToEnd();
//Stuff I do with the request happens here
}
}
try
{
using (System.Net.HttpListenerResponse response = context.Response)
{
//response stuff happens here
}
byte[] buffer = System.Text.Encoding.UTF8.GetBytes(responseString);
response.ContentLength64 = buffer.LongLength;
response.OutputStream.Write(buffer, 0, buffer.Length);
response.Close();
StopListening();
//If I dont set the thread to sleep here, I receive the double requests
System.Threading.Thread.Sleep(2500);
ListenAsynchronously();
}
}
catch (Exception e)
{
}
}
I am not sure why you are calling StopListening() and ListenAsynchronously() in your ListenerCallback() method. The Listen() method is being run in a thread and will continue to get each next incoming request. If I was writing this, I would not be using a instance variable of HttpListener. Create a new one in your ListenAsynchronously method and pass it in your state object, for example,
public class HttpListenerCallbackState
{
private readonly HttpListener _listener;
private readonly AutoResetEvent _listenForNextRequest;
public HttpListenerCallbackState(HttpListener listener)
{
if (listener == null) throw new ArgumentNullException("listener");
_listener = listener;
_listenForNextRequest = new AutoResetEvent(false);
}
public HttpListener Listener { get { return _listener; } }
public AutoResetEvent ListenForNextRequest { get { return _listenForNextRequest; } }
}
public class HttpRequestHandler
{
private int requestCounter = 0;
private ManualResetEvent stopEvent = new ManualResetEvent(false);
public void ListenAsynchronously(IEnumerable<string> prefixes)
{
HttpListener listener = new HttpListener();
foreach (string s in prefixes)
{
listener.Prefixes.Add(s);
}
listener.Start();
HttpListenerCallbackState state = new HttpListenerCallbackState(listener);
ThreadPool.QueueUserWorkItem(Listen, state);
}
public void StopListening()
{
stopEvent.Set();
}
private void Listen(object state)
{
HttpListenerCallbackState callbackState = (HttpListenerCallbackState)state;
while (callbackState.Listener.IsListening)
{
callbackState.Listener.BeginGetContext(new AsyncCallback(ListenerCallback), callbackState);
int n = WaitHandle.WaitAny(new WaitHandle[] { callbackState.ListenForNextRequest, stopEvent});
if (n == 1)
{
// stopEvent was signalled
callbackState.Listener.Stop();
break;
}
}
}
private void ListenerCallback(IAsyncResult ar)
{
HttpListenerCallbackState callbackState = (HttpListenerCallbackState)ar.AsyncState;
HttpListenerContext context = null;
int requestNumber = Interlocked.Increment(ref requestCounter);
try
{
context = callbackState.Listener.EndGetContext(ar);
}
catch (Exception ex)
{
return;
}
finally
{
callbackState.ListenForNextRequest.Set();
}
if (context == null) return;
HttpListenerRequest request = context.Request;
if (request.HasEntityBody)
{
using (System.IO.StreamReader sr = new System.IO.StreamReader(request.InputStream, request.ContentEncoding))
{
string requestData = sr.ReadToEnd();
//Stuff I do with the request happens here
}
}
try
{
using (HttpListenerResponse response = context.Response)
{
//response stuff happens here
string responseString = "Ok";
byte[] buffer = Encoding.UTF8.GetBytes(responseString);
response.ContentLength64 = buffer.LongLength;
response.OutputStream.Write(buffer, 0, buffer.Length);
response.Close();
}
}
catch (Exception e)
{
}
}
}