//
// Copyright (c) 2002
// Ronald Kevin Burton
//
// Z poniższym kodem nie jest związana żadna gwarancja poprawności działania.
// Program został dołączony do książki ".NET CLR. Księga eksperta" w celu
// ilustracji koncepcji i zasad przedstawionych w tej książce. Program może być 
// używany na własne ryzyko.
//
// Przyznaje się prawo do użycia lub kopiowania tego oprogramowania do dowolnego celu
// bez konieczności ponoszenia żadnych opłat pod warunkiem, że powyższe uwagi zostaną 
// zachowane we wszystkich kopiach. Przyznaje się także prawo do modyfikacji kodu
// i dystrybucji zmodyfikowanego kodu pod warunkiem zachowania powyższych uwag
// oraz dołączenia informacji mówiącej o modyfikacji kodu.
//
// 
using System;
using System.Net;
using System.Net.Sockets;
using System.Text;
using System.Threading;

public class AsyncData
{
	public Socket socket;
	public byte[] RecBytes = new byte[256];
}

public class AsyncSrv 
{	
	private static ManualResetEvent AcceptEvent = new ManualResetEvent(false);
	private static ManualResetEvent ReceiveEvent = new ManualResetEvent(false);	
	private static ManualResetEvent SendEvent = new ManualResetEvent(false);	

	private static AsyncData ad = new AsyncData();
	
	public static Socket GetListenerSocket(string host, int port) 
	{
		Socket s = null;
		IPHostEntry iphe = Dns.Resolve(host);
		foreach(IPAddress ipa in iphe.AddressList) 
		{
			IPEndPoint lep = new IPEndPoint(ipa, port);
			Socket ts = new Socket(lep.AddressFamily,
				                   SocketType.Stream, 
				                   ProtocolType.Tcp);
			try 
			{
				ts.Bind(lep);
				ts.Listen(2);
				s = ts;
				break;
			} 
			catch (ArgumentNullException ae)
			{
				Console.WriteLine("ArgumentNullException: " + ae.ToString());
			}
			catch (SocketException se)
			{				
				Console.WriteLine("SocketException: " + se.ToString());
			}
			catch (Exception e) 
			{
				Console.WriteLine("Połączenie nie powiodło się: " + e.ToString());
			}
		}
		return s;
	}

	public static void Main() 
	{
		Socket srv = GetListenerSocket(Dns.GetHostName(), 5000);
		if(srv == null)
			return;

		Console.WriteLine("Rozpoczęcie nasłuchu na porcie 5000\n");						
		
		while (true) 
		{
			AcceptEvent.Reset();
			ReceiveEvent.Reset();
			SendEvent.Reset();
			Console.WriteLine("wywołanie BeginAccept");		
			srv.BeginAccept(new AsyncCallback(AcceptCallback), srv);		
			AcceptEvent.WaitOne();		
		}
	}

	private static void AcceptCallback(IAsyncResult result)
	{				
		Socket temp = (Socket) result.AsyncState;
		Socket a = temp.EndAccept(result);		

		Console.WriteLine("EndAccept() Socket.EndPoint: " + 
			a.RemoteEndPoint.ToString());				
		
		ad.socket = a;
		
		IAsyncResult recres = a.BeginReceive(ad.RecBytes, 
			0, 
			256, 
			0, 
			new AsyncCallback(ReceiveCallback), 
			ad);
		
		ReceiveEvent.WaitOne();		

		Byte[] byteSend = Encoding.ASCII.GetBytes("To jest test awaryjnego systemu wiadomości");

		IAsyncResult sendres = a.BeginSend(byteSend,
			0,
			byteSend.Length,
			0,
			new AsyncCallback(SendCallback),
			a);
		SendEvent.WaitOne();
				
		a.Close();
		AcceptEvent.Set();				

		return;
	}

	private static void ReceiveCallback(IAsyncResult result)
	{													
		int bytes = ad.socket.EndReceive(result);				
		
		Console.WriteLine("[AsyncSrv] ReceiveCallback: bajtów = " + bytes);		

		if (bytes > 0)
		{
			string data = Encoding.ASCII.GetString(ad.RecBytes, 0, bytes);				
			Console.WriteLine("Odebrano: [" + data + "]\n");		
		}
		
		ReceiveEvent.Set();
	}

	private static void SendCallback(IAsyncResult result) 
	{
		Socket socket = (Socket) result.AsyncState;

		int bytes = socket.EndSend(result);
		Console.WriteLine("Wysłano: " + bytes + " bajtów");

		SendEvent.Set();
	}
}