hack.connect
 
[ru.scene.community]
HackConnect.E-zine issue #1
// 00101 Cокеты на C#

Введение.

C#, как и любой другой язык программирования, позволяет работать с интерфейсом сокетов. Для этого в нем предусмотрен набор классов System.Net.Sockets. В этой статье я рассмотрю базовые алгоритмы работы с сокетами, в результате как практический пример мы получим многопоточный консольный проксичекер. Как видно из слова «многопоточный», будет также рассмотрена на базовом уровне работа с потоками.

Теория.

Перечислю базовый набор классов и методов, необходимый для вполне сносной реализации сетевой работы:

Socket:                      // Основной класс для реализации работы через сокеты.
                 Socket.Send(Byte[]);    // Отправляет последовательность байт в
                                         // открытый сокет. В этой статье я
                                         // рассмотрю только этот, наиболее
                                         // простой экземпляр этой функции.
                 Socket.Receive(Byte[]);     // Читает данные из сокета. Это также
                                             // наиболее простой экземпляр Receive.
                 Socket.Close();      // Закрывает сокет.
TcpClient:                  // Класс клиента TCP
                 TcpClient.Connect(String, Int32);  // Принимает 2 аргумента – IP
                                                    // и порт.
                 TcpClient.Close();              // Закрывает экземпляр.
TcpListener:               // Класс TCP сервера
                 TcpListener(Int32);           // Начинает слушать порт, указанный в
                                               // аргументе.
                 TcpListener.AcceptSocket();   // Возвращает экземпляр Socket для
                                               // работы с клиентом.
                 TcpListener.Close();          // Закрывает экземпляр.

Напишем 2 простеньких приложения – клиент и сервер.

Клиент:

using System;       ///////////////////////////////////////////////////
using System.Collections.Generic;/////// Консольный стандарт //////////
using System.Text;  ///////////////////////////////////////////////////
using System.Net.Sockets; ///// Вот он, родимый коллекшн классов //////


namespace tcpclient
{
    class Program
    {
        static void Main(string[] args)
        {
            string data; // Юзерская дата
            byte[] remdata ={ }; // Дата с сервака
            TcpClient Client = new TcpClient(); // создаем экземпляр
            Console.Write("IP to connect to: "); // всякая фигня типа UI
            string ip = Console.ReadLine();
            Console.Write("\r\nPort: ");
            int port = Convert.ToInt32(Console.ReadLine());
            Console.WriteLine("\r\nConnecting to server...");
            try
            {
                Client.Connect(ip, port); // попробуем подконнектиться.
            } // если не получится - эксепшн
            catch
            {
                Console.WriteLine("Cannot connect to remote host!");
                return;
            }
            Console.Write("done\r\nTo end, type 'END'");
            Socket Sock = Client.Client; // нах всякие NetStream'ы
            while (true) // базарим с сервером… если достало – пишем END
            {
                Console.Write("\r\n>");
                data = Console.ReadLine();
                if (data == "END")
                    break;
                Sock.Send(Encoding.ASCII.GetBytes(data)); // преобразование
                                                          // типов
                Sock.Receive(remdata);
                Console.Write("\r\n<" + Encoding.ASCII.GetString(remdata));
            }
            Sock.Close();
            Client.Close();
        }
    }
}

Сервер:

using System; /////////////////////////////////////////////////
using System.Collections.Generic;///// Консольный стандарт ////
using System.Text;/////////////////////////////////////////////
using System.Net.Sockets; // Вот он, родимый коллекшн классов /
using System.Threading; // Коллекшн для работы с потоками /////

namespace tcpserver
{
    class Program
    {
        static void Main(string[] args)
        {
            string cmd;
            Console.Write("Port to listen: ");
            int port = Convert.ToInt32(Console.ReadLine());
            Console.WriteLine("Creating server...");
            Server Serv = new Server(); // Создаем новый экземпляр класса
                                        // сервера
            Serv.Create(port);
           
            while (true)
            {
                cmd = Console.ReadLine(); // Ждем фразы EXIT когда
                                          // понадобится выйти из приложения.
                                          // типа интерактивность.
                if (cmd == "EXIT")
                {
                    Serv.Close(); // раз выход – значит выход. Серв-нах.
                    return;
                }
            }
        }

        public class Server // класс сервера.
        {
            private int LocalPort;
            private Thread ServThread; // экземпляр потока
            TcpListener Listener; // листенер))))
           
            public void Create(int port)
            {
                LocalPort = port;
                ServThread = new Thread(new ThreadStart(ServStart));
                ServThread.Start(); // запустили поток. Стартовая функция –
                                    // ServStart, как видно выше
            }
           
            public void Close() // Закрыть серв?
            {
                Listener.Stop();
                ServThread.Abort();
                return;
            }
       
            private void ServStart()
            {
                Socket ClientSock; // сокет для обмена данными.
                string data;
                byte[] cldata = new byte[1024]; // буфер данных
                Listener = new TcpListener(LocalPort);
                Listener.Start(); // начали слушать
                Console.WriteLine("Waiting connections [" + Convert.ToString(LocalPort) + "]...");
                try
                {
                    ClientSock = Listener.AcceptSocket(); // пробуем принять
                                                          // клиента
                }
                catch
                {
                    ServThread.Abort(); // нет – жаль(
                    return;
                }
                int i=0;
       
                if (ClientSock.Connected)
                {
                    while (true)
                    {
                        try
                        {
                            i = ClientSock.Receive(cldata); // попытка чтения
                                                            // данных
                        }
                        catch
                        {
                        }

                        try
                        {
                            if (i > 0)
                            {
                           
                                data = Encoding.ASCII.GetString(cldata).Trim();
                                Console.WriteLine("<" + data);
                                if (data == "CLOSE") // если CLOSE –
                                                     // вырубимся
                                {
                                    ClientSock.Send(Encoding.ASCII.GetBytes("Closing the server..."));
                                    ClientSock.Close();
                                    Listener.Stop();
                                    Console.WriteLine("Server closed. Reason: client wish! Type EXIT to quit the application.");
                                    ServThread.Abort();
                                    return;
                                }
                                else
                                { // нет – шлем данные взад.
                                    ClientSock.Send(Encoding.ASCII.GetBytes("Your data: " + data));
                                }
                            }
                        }
                        catch
                        {
                            ClientSock.Close(); // ну эт если какая хрень..
                            Listener.Stop();
                            Console.WriteLine("Server closing. Reason: client offline. Type EXIT to quit the application.");
                            ServThread.Abort();
                        }
                    }
                }
            }
        }
    }
}


Практика.

Здесь я приведу подробно прокомментированный пример проксичекера. К нему довольно просто прикрутить многопоточность.

using System;
using System.Collections.Generic;
using System.IO; // чтоб с файлами пахал))
using System.Net.Sockets;
using System.Text;

namespace dm_prxchk
{
    class Program
    {
        static void Main(string[] args)
        {
         StreamReader nproxy; // поток для чтения из файла
         string[] proxy = new string[128];
         if (args.Length < 1)
         {
             Console.WriteLine("Please specify the proxylist file!");
             return;
         }
        
         try
         {
             nproxy = new StreamReader(args[0]); // пробуем открыть файл
                                                 // с проксями
         }
         catch
         {
             Console.WriteLine("Proxylist file " + args[0] + "does not exists!"); // если че – орем «где файл?»
             return;
         }
         int n = 0;
         while ((proxy[n] = nproxy.ReadLine()) != null)
         {
              n++;
              if (n >= 128) // больше 128 не охота проверять, да и
                            // нецелесообразно…
              {
                 Console.WriteLine("More than 128 proxies in proxylist file! First 128 will be checked!");
                 break;
              }
          }
          nproxy.Close(); // прочитали
          for (int i = 0; i < n; i++)
         {
             dm_testprx(proxy[i]); // тестируем
         }
        
         return;
         }
     
         private static void dm_testprx(string proxy)
         {
             string[] prxopt = proxy.Split(':'); // режем прокс на хост и порт
             int time = Convert.ToInt32(DateTime.Now.ToString("ss"));
             int timep2 = DateTime.Now.Millisecond; // таймаут то надо
                                                    // посчитать
             try
             {
                 TcpClient chk = new TcpClient(prxopt[0], Convert.ToInt32(prxopt[1])); // пробуем подконнектиться
                 if (!chk.Connected)
                     throw new System.Exception();
                 else
                 {
                     Console.WriteLine("[a]" + proxy + " - Timeout: " + Convert.ToString(Math.Abs(Convert.ToInt32(DateTime.Now.ToString("ss")) - time)) + '.' + Convert.ToString(Math.Abs(DateTime.Now.Millisecond - timep2))); // живой, выводим вместе с таймаутом
                 }
             }
             catch
             {
                Console.WriteLine("[d]" + proxy); // дохлый
             }
        }
    }
}

Заключение.

Если кому что непонятно – пишем мне))). А я закругляюсь и ваще скоро 8 марта.

// by DeaDMonaX

/* ----------------------------------------------------[contents]----------------------------------------------------- */