Введение.
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
|