//
// 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.
//
// 
/*+==========================================================================
  Plik:      sockget.cs

  Opis:      Przedstawia zastosowanie gniazd asynchronicznych przy użyciu klasy 
             Socket w URT Frameworks SDK. Program wysyła "GET" do określonego 
             serwera WWW, a następnie odczytuje domyślną stronę internetową.

  Klasy:     AsyncData, SockGet

  Funkcje:   Main, DoGet, DoSend, DoReceive, ConnectCallback, SendCallback,
             ReceiveCallback

----------------------------------------------------------------------------  

  Copyright (C) 2000 Microsoft Corporation.  Wszystkie prawa zastrzeżone.
==========================================================================+*/

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 SockGet
{

	private static string server = "localhost";
	private static string path = "";
	private static string Page = "";
	private static AsyncData ad = new AsyncData();
	private static int BytesReceived = -1; // Nie może mieć wartości 0

	private static ManualResetEvent ConnectDone = new ManualResetEvent(false);
	private static ManualResetEvent SendDone = new ManualResetEvent(false);
	private static ManualResetEvent ReceiveDone = new ManualResetEvent(false);
	
	public static void Main (string [] args) 
	{        
		if (args.Length < 1)
		{
			Console.WriteLine("Nie wprowadzono nazwy serwera. Domyślnie jest to localhost.");
		}
		else
			server = args[0];
				        
		DoGet();        
	}

	public static Socket GetSocket(string host, int port) 
	{
		Socket s = null;
		IPHostEntry iphe = Dns.Resolve(host);
		// Pętla dla wszystkich adresów, 
		// a nie tylko pierwszego. 
		foreach(IPAddress ipa in iphe.AddressList) 
		{
			IPEndPoint ipe = new IPEndPoint(ipa, port);
		
			// Należy użyć AddressFamily będącej częścią
			// punktu końcowego. 
			// AddressFamily.InterNetwork ogranicza tylko
			// do IPv4
			Socket ts = new Socket(ipe.AddressFamily,
				SocketType.Stream,
				ProtocolType.Tcp);
			try 
			{
				// Zwrócenie pierwszego gniazda, z którym
				// można nawiązać połączenie.
				ts.Connect(ipe);
				s = ts;
				break;
			} 
			catch (ArgumentNullException ae)
			{
				Console.WriteLine("Wyjątek ArgumentNullException: " + ae.ToString());
			}
			catch (SocketException se)
			{				
				Console.WriteLine("Wyjątek SocketException: " + se.ToString());
			}
			catch (Exception e) 
			{
				Console.WriteLine("Połączenie nie powiodło się: " + e.ToString());
			}
		}
		return s;
	}

	private static void DoGet()
	{
		try
		{
            Socket s = GetSocket(server, 80);

			s.Blocking = false; // domyślna wartość to true

			// Ustawia czas oczekiwania wysyłki i odbioru na 10 sekund.
			s.SetSocketOption(SocketOptionLevel.Socket, 
				SocketOptionName.SendTimeout,
				10000);

			s.SetSocketOption(SocketOptionLevel.Socket, 
				SocketOptionName.ReceiveTimeout,
				10000);

			DoSend(s);
			SendDone.WaitOne();
			                			
			DoReceive(s);		
            
			Console.WriteLine("\nDomyślna strona HTML na " + server + " została odebrana\n");
			Console.WriteLine(Page);			

			s.Shutdown(SocketShutdown.Both);
			s.Close();
		}

		catch (Exception e)
		{
			Console.WriteLine("Wyjątek Exception: " + e.Message);
			Console.WriteLine("Wyjątek StackTrace: " + e.StackTrace);
			return;
		}
	}

	private static void DoSend(Socket s) 
	{				
		Encoding ASCII = Encoding.ASCII;
		string Get = "GET /" + path + " HTTP/1.1\r\nHost: " + server + "\r\nConnection: Close\r\n\r\n";
		Byte[] ByteGet = ASCII.GetBytes(Get);            

		IAsyncResult result = s.BeginSend(ByteGet,                
			0,
			ByteGet.Length, 
			0,
			new AsyncCallback(SendCallback),
			s);        			
        
		return;		
	}

	private static void SendCallback(IAsyncResult result) 
	{        
		try
		{

			Socket socket = (Socket) result.AsyncState;

			int sentBytes = socket.EndSend(result);
			Console.WriteLine("\Wysłano " + sentBytes + " bajtów do " + server);
        
			SendDone.Set();
			return;
		}

		catch (SocketException e)
		{
			// Wysyłanie danych nie powiodło się.

			Console.WriteLine("Nie można wysłać danych do: " + server);
			Console.WriteLine("\nWyjątek SocketException: " + e.Message);
			Console.WriteLine("Kod błędu: " + e.NativeErrorCode);
			Console.WriteLine("StackTrace: " + e.StackTrace);	

			return;		
		}
	}

	private static void DoReceive(Socket s)
	{				
		try
		{
			ad.socket = s;

			while (BytesReceived != 0)
			{
				ReceiveDone.Reset();


				IAsyncResult result = s.BeginReceive(ad.RecBytes,
					0,
					ad.RecBytes.Length,
					SocketFlags.None,
					new AsyncCallback(ReceiveCallback),
					null); 

				ReceiveDone.WaitOne();        
			}		
                
			ReceiveDone.Set();
			return;
		}

		catch (SocketException e)
		{
			// Odbiór danych nie powiódł się.

			Console.WriteLine("Błąd w czasie odbioru danych z: " + server);
			Console.WriteLine("\nWyjątek SocketException: " + e.Message);
			Console.WriteLine("Kod błędu: " + e.NativeErrorCode);
			Console.WriteLine("StackTrace: " + e.StackTrace);	

			return;
		}
	}

	private static void ReceiveCallback(IAsyncResult result) 
	{
		try
		{    
			Encoding ASCII = Encoding.ASCII;
			BytesReceived = ad.socket.EndReceive(result);							

			Page += ASCII.GetString(ad.RecBytes, 0, BytesReceived);
						
			ReceiveDone.Set();
			return;
		}

		catch (SocketException e)
		{
			// Odbiór danych nie powiódł się.

			Console.WriteLine("Błąd w czasie odbioru danych z: " + server);
			Console.WriteLine("\Wyjątek nSocketException: " + e.Message);
			Console.WriteLine("Kod błędu: " + e.NativeErrorCode);
			Console.WriteLine("StackTrace: " + e.StackTrace);	

			return;		
		}
	}

} // SockGet
