Вопрос насчет сокетов
От: Tanketka  
Дата: 09.07.07 01:51
Оценка:
Ситуация такая:
Грубо говоря прокси сервер. Слушаем порт и если что, Сокет 1 принимает запросы от web-браузера, затем мы в программе немного изменяем запрос и передаем их Сокету 2, а тот передает запрос Web-серверу.
Соответственно потом мы получаем данные от Web-сервера на Сокет 2 и передаем их Сокету 1(браузеру).

Как это реализовать, просто столько классов (TcpListener, Socket у которого есть метод Listen, TcpClient), я запуталась.
И как это сделать в отдельных потоках?
Re: Вопрос насчет сокетов
От: stump http://stump-workshop.blogspot.com/
Дата: 09.07.07 05:50
Оценка:
Здравствуйте, Tanketka, Вы писали:

T>Ситуация такая:

T>Грубо говоря прокси сервер. Слушаем порт и если что, Сокет 1 принимает запросы от web-браузера, затем мы в программе немного изменяем запрос и передаем их Сокету 2, а тот передает запрос Web-серверу.
T>Соответственно потом мы получаем данные от Web-сервера на Сокет 2 и передаем их Сокету 1(браузеру).

T>Как это реализовать, просто столько классов (TcpListener, Socket у которого есть метод Listen, TcpClient), я запуталась.

T>И как это сделать в отдельных потоках?

Ну и зачем тут сокеты? Смотри в сторону HTTP Handlers
Понедельник начинается в субботу
Re[2]: Вопрос насчет сокетов
От: Tanketka  
Дата: 09.07.07 07:30
Оценка:
S>Ну и зачем тут сокеты? Смотри в сторону HTTP Handlers



Смотрела, но мне нужно сделать так чтобы эта программа была проксиком. То есть все свои браузеры я бы могла настроить выходить через нее. Писала на VB, получалось так:

Dim ToServer As String
Dim Webport 
Dim Data As String 

Private Sub Form_Load()
Form1.Visible = True
Do 
    If Winsock1.State <> sckConnected And Winsock1.State <> sckListening _
                Then 
    Beep
        Winsock1.Close 
        Winsock1.LocalPort = 8887
        Winsock1.Listen 
    End If
    DoEvents
Loop
End Sub

Private Sub Winsock2_SendToWebserver() 
Winsock2.Close 
Winsock2.RemoteHost = "www.mywebserver.com"   
Winsock2.RemotePort = 80 
Winsock2.Connect 
Webport = Winsock2.RemotePort 
End Sub

Private Sub Winsock2_Connect()
    If Winsock2.State <> sckConnected Then Exit Sub 
Winsock2.SendData ToServer 

End Sub

Private Sub Winsock2_DataArrival(ByVal bytesTotal As Long)
If Winsock2.State <> sckClosing Then
    Winsock2.GetData Data                      

    
    Winsock1.SendData Data   
End If
End Sub



''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''
''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''
''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''
Private Sub Winsock1_ConnectionRequest(ByVal requestID As Long)

Winsock1.Close 
Winsock1.Accept requestID 

End Sub


Private Sub Winsock1_DataArrival(ByVal bytesTotal As Long)
Dim ServerData As String ServerData
Winsock1.GetData ServerData 

ToServer = ServerData 
'Необходимое мне измение
ToServer = "GET " + "http://www.mywebserver.com/myveryneedfile.php?url=" + Mid(ToServer, 5)
Winsock2_SendToWebserver 


End Sub


Только теперь это нужно сделать на C#, и многопоточной,
то есть я так понимаю в главной функции создаем поток, в котором будем слушать порт и если что-то приходит, выполнять все это дело.
Re: Вопрос насчет сокетов
От: Vector Россия  
Дата: 09.07.07 08:16
Оценка:
Здравствуйте, Tanketka, Вы писали:

T>Ситуация такая:

T>Грубо говоря прокси сервер. Слушаем порт и если что, Сокет 1 принимает запросы от web-браузера, затем мы в программе немного изменяем запрос и передаем их Сокету 2, а тот передает запрос Web-серверу.
T>Соответственно потом мы получаем данные от Web-сервера на Сокет 2 и передаем их Сокету 1(браузеру).

T>Как это реализовать, просто столько классов (TcpListener, Socket у которого есть метод Listen, TcpClient), я запуталась.

T>И как это сделать в отдельных потоках?

Полного кода сейчас нет — дома. На память примерно следущее:


public Form1()
{
    Socket listenSocket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
    IPAddress hostIP = new IPAddress(new byte[] { 127, 0, 0, 1 });
    IPEndPoint ep = new IPEndPoint(hostIP, 8080);
    listenSocket.Bind(ep);
    listenSocket.Listen(10);
    listenSocket.BeginAccept(new AsyncCallback(Accept), listenSocket);
}

void Accept(IAsyncResult ar)
{
    Socket listenSocket = (Socket)ar.AsyncState;
    Socket socketBrowser = listenSocket.EndAccept(ar);
    Thread thread = new Thread(ConnectThreadProc);
    thread.Start(socketBrowser);
    listenSocket.BeginAccept(new AsyncCallback(Accept), listenSocket);
}

void ConnectThreadProc(object o)
{
    Socket socketBrowser = (Socket)o;
    using (Stream streamBrowser = new NetworkStream(socketBrowser))
    {
        StreamReader readerBrowser = new StreamReader(streamBrowser, Encoding.GetEncoding(1251));
        string host = string.Empty;
        string str;
        string data = string.Empty;
        do
        {
            str = readerBrowser.ReadLine();
            if (str.StartsWith("host: ", StringComparison.CurrentCultureIgnoreCase))
            {
                host = str.Remove(0, 6);
            }
            data += str;
            data += Environment.NewLine;
        } while (!string.IsNullOrEmpty(str));

        Socket socketProxy = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
        socketProxy.Connect(host, 80);
        NetworkStream streamProxy = new NetworkStream(socketProxy);
        StreamWriter writerProxy = new StreamWriter(streamProxy, Encoding.GetEncoding(1251));
        writerProxy.Write(data);
        writerProxy.Flush();

        StreamReader readerProxy = new StreamReader(streamProxy, Encoding.GetEncoding(1251));
        StreamWriter writerBrowser = new StreamWriter(streamBrowser, Encoding.GetEncoding(1251));
        do
        {
            str = readerProxy.ReadLine();
            writerBrowser.WriteLine(str);
        } while (!string.IsNullOrEmpty(str));
        str = readerProxy.ReadToEnd();
        writerBrowser.Write(str);
        writerBrowser.Flush();
}


Думаю, идея понятна. Не обещаю, что все скомпилируется и запустится (+ надо сокеты за собой закрывать и т.д.), но при таком ходе мыслей у меня все получилось. Если что, вечером найду дома полный рабочий пример, залью.
Re[2]: Вопрос насчет сокетов
От: Tanketka  
Дата: 09.07.07 08:30
Оценка:
Спасибо, буду разбираться
Re[3]: Вопрос насчет сокетов
От: Svjat Украина  
Дата: 09.07.07 08:41
Оценка:
Здравствуйте, Tanketka, Вы писали:

T>Только теперь это нужно сделать на C#, и многопоточной,


class Proxy
{
   private Socket ListenerSocket;
   List<ProxySession> Session = new List<ProxySession>();

   public void Start( IPAddress ip, int port )
   {
       Socket ListenerSocket = new Socket();
       ListenerSocket.Bind( new IPEndPoint( ip, port ) );
       ListenerSocket.Listen();
       ListenerSocket.BeginAccept( OnAccept );
   }
    
   private void OnAccept( IAsyncResult ar )
   {
       // создаем новую сессию на входящее соединение от браузера
       ProxySession session = new ProxySession( ListenerSocket.EndAccept( ar ) );

       // подписываемся на события
       //session.OnClosed += OnSessionClosed; 
       //......

       Session.Add( session );
       session.Start();
   }
}

class ProxySession
{
   Socket ClientSocket;
   Dictionnary<string,Socket> ServerSocket = new Dictionnary<string,Socket>();

   public ProxySession( Socket socket ) { ClientSocket = socket; }
   public void Start() { ClientSocket.BeginReceive( ...., OnReceive ); }

   private void OnReceive( IAsynchResult ar )
   {
      try
      {
          ClientSocket.EndReceive( ar );
      }
      catch( SocketException )
      {
          // инициатором закрытия сессии может быть только клентсое соединение
          //  точнее, его разрыв
          CloseSession();      
          return;
      }
      ......
      // тут смотрим запрос от браузера, вытаскиваем хост, на кот. идет запрос
      // и если у нас нет такого в списке подключенных, то 
      //  создаем соотв. новое подключение к веб-серверу
      //  причем это нужно делать при каждом запросе от клиента
      //  т.е. в рамках одной сессии может быть один конект от клиента,
      //  и много подключений к веб-серверам
      Socket server = null;
      if( !ServerSocket.TryGetValue( hostName, out server )  )
      {
          server = new Socket( ... );
          ServerSocket.Add( hostName, server );
      }
      server.BeginSend( ..... );
      ClientSocket.BeginReceive( ...., OnReceive );
   }
}



код условный, разумеется, старался самое главное написать.
нужно еще добавить как минимум
синхронизацию общих ресурсов и обработку исключений

что бы сделать архитектуру гибче:
1. процесс передачи данных выделить в отдельный класс — трансмиттер,
кот. владеет 2-мя сокетами, и просто из одного читает и в другой пишет.
2. Сессия просто владеет списком этих "трансмиттеров"
3. операции по созданию сессий / трансмиттеров вынести в отдельные классы,
и юзать через интерфейсы, типа

new Proxy( (ISessionBuilder)( new ProxySessionBuilder( .. ));
new ProxySession( (IConnectionBuilder)( new ConnectionBuilder() ) );

это позволит сделать внутренюю структуру сессии более гибкой.
поясню: если между нашим прокси и веб-сервером сидит другой прокси,
нет смысла делать много клиентских подключений в одной сессии, достаточно одного.
для этого при создании прокси просто создаеются другой набор билдеров.
уже не говоря о том, что тогда этот прокси можно юзать не только для хттп.
4. прокси отвечает только за передачу данных в бинарном виде.
всю работу по анализу трафика тоже лучше выделить в отдельные классы, и тоже
передавать в сессию / прокси через интерфейсы.

при таком подходе реализация какого-либо протокола — всего лишь новый набор фильтров.


-----
мда, собирался покороче один ............
Re[2]: Вопрос насчет сокетов
От: Svjat Украина  
Дата: 09.07.07 09:04
Оценка:
Здравствуйте, Vector, Вы писали:

V>Думаю, идея понятна. Не обещаю, что все скомпилируется и запустится (+ надо сокеты за собой закрывать и т.д.), но при таком ходе мыслей у меня все получилось. Если что, вечером найду дома полный рабочий пример, залью.


Самая главная дыра — то что ответ от веб-сервера может приходить несколькими пакетами
( не только может, а будет — если ответ достаточно большой )

ну и такая архитектура, при которой мы заставляем браузер при каждом запросе
заново к нам конектится — очень тормознутая,
попробуй через такой прокси загрузить что-то вроде yahoo.com, он пока ее загрузит
на твоем аксепте мозоль натрет.

зы:

public Form1()
{
Socket listenSocket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
...............
Re[3]: Вопрос насчет сокетов
От: Vector Россия  
Дата: 09.07.07 09:38
Оценка:
Здравствуйте, Svjat, Вы писали:

S>Самая главная дыра — то что ответ от веб-сервера может приходить несколькими пакетами

S>( не только может, а будет — если ответ достаточно большой )

S>ну и такая архитектура, при которой мы заставляем браузер при каждом запросе

S>заново к нам конектится — очень тормознутая,
S>попробуй через такой прокси загрузить что-то вроде yahoo.com, он пока ее загрузит
S>на твоем аксепте мозоль натрет.

S>зы:


S>public Form1()

S>{
S> Socket listenSocket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
S> ...............

Я лишь привел примерный ход мыслей! Где сказано, что это окончательное решение? А насчет разрывов соединений — так браузер сам шлет параметр "Proxy-Connection: Close", по крайней мере, так было в моем случае. И никто нигде мозоль не натер. Грузилось все довольно резво, я не особо заметил разницу(с тем, что было без прокси).
Ответ от веб-сервера несколькими пакетами — это уже надо с HTTP разбираться, читать все заголовки, соответственно все гораздо сложней и описывать это "на коленках" нереально. Опять же в моей задаче такого не возникало ниразу.
Re: Вопрос насчет сокетов
От: max-maxtor Россия www.rsdn.ru
Дата: 09.07.07 09:43
Оценка:
Здравствуйте, Tanketka, Вы писали:

T>Ситуация такая:

T>Грубо говоря прокси сервер. Слушаем порт и если что, Сокет 1 принимает запросы от web-браузера, затем мы в программе немного изменяем запрос и передаем их Сокету 2, а тот передает запрос Web-серверу.
T>Соответственно потом мы получаем данные от Web-сервера на Сокет 2 и передаем их Сокету 1(браузеру).

T>Как это реализовать, просто столько классов (TcpListener, Socket у которого есть метод Listen, TcpClient), я запуталась.

T>И как это сделать в отдельных потоках?

#using <System.dll>

using namespace System;
using namespace System::IO;
using namespace System::Net;
using namespace System::Net::Sockets;
using namespace System::Text;
using namespace System::Threading;
void main()
{
try
{

// Set the TcpListener on port 13000.
Int32 port = 13000;
IPAddress^ localAddr = IPAddress::Parse( "127.0.0.1" );

// TcpListener* server = new TcpListener(port);
TcpListener^ server = gcnew TcpListener( localAddr,port );

// Start listening for client requests.
server->Start();


// Enter the listening loop.
while ( true )
{
Console::Write( "Waiting for a connection... " );

// Perform a blocking call to accept requests.
// You could also user server.AcceptSocket() here.
TcpClient^ client = server->AcceptTcpClient();//Держит поток пока коокой-нибуть клиент не постучится. Когда постучится создаешь поток и передаешь ему client


}
}
catch ( SocketException^ e )
{
Console::WriteLine( "SocketException: {0}", e );
}

Console::WriteLine( "\nHit enter to continue..." );
Console::Read();
}
Re[4]: Вопрос насчет сокетов
От: Svjat Украина  
Дата: 09.07.07 10:01
Оценка:
Здравствуйте, Vector, Вы писали:

V>Здравствуйте, Svjat, Вы писали:


S>>Самая главная дыра — то что ответ от веб-сервера может приходить несколькими пакетами

S>>( не только может, а будет — если ответ достаточно большой )

S>>ну и такая архитектура, при которой мы заставляем браузер при каждом запросе

S>>заново к нам конектится — очень тормознутая,
S>>попробуй через такой прокси загрузить что-то вроде yahoo.com, он пока ее загрузит
S>>на твоем аксепте мозоль натрет.

S>>зы:


S>>public Form1()

S>>{
S>> Socket listenSocket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
S>> ...............

V>Я лишь привел примерный ход мыслей! Где сказано, что это окончательное решение?


так я и говорю о решении, а не о коде как таковом, это понятно.

>А насчет разрывов соединений — так браузер сам шлет параметр "Proxy-Connection: Close",


или "Proxy-Connection: Keep-alive"

>по крайней мере, так было в моем случае. И никто нигде мозоль не натер. Грузилось все довольно резво, я не особо заметил разницу(с тем, что было без прокси).



попробуй загрузить сложную страничку — с банерами / фреймами и т.д.
например главная стр. яху — для моего прокси создаст ок. 12 сессий,
в каждой пару конектов к разным хостам,
и по каждому конекту прогонит далеко не один запрос

я об этом и говорил, что на аксепте мозоль будет,
если все это заставить через автономные конекты гонять.

эти торможа даже визуально будет видно — проверено.

V>Ответ от веб-сервера несколькими пакетами — это уже надо с HTTP разбираться, читать все заголовки, соответственно все гораздо сложней и описывать это "на коленках" нереально. Опять же в моей задаче такого не возникало ниразу.


один ответ на запрос может быть больше максимального размера сетевого пакета,
например jpeg на 200 кб., в этом случае браузер получит только кусок в лучшем случае.
Re[2]: Вопрос насчет сокетов
От: AndrewVK Россия http://blogs.rsdn.org/avk
Дата: 09.07.07 12:59
Оценка:
Здравствуйте, max-maxtor, Вы писали:

Используй теги для выделения кода.
... << RSDN@Home 1.2.0 alpha rev. 688>>
AVK Blog
Re[4]: Вопрос насчет сокетов
От: Tanketka  
Дата: 11.07.07 08:50
Оценка:
Здравствуйте, Svjat, Вы писали:

S>Здравствуйте, Tanketka, Вы писали:


T>>Только теперь это нужно сделать на C#, и многопоточной,


S>
S>class Proxy
S>{
S>   private Socket ListenerSocket;
S>   List<ProxySession> Session = new List<ProxySession>();

S>   public void Start( IPAddress ip, int port )
S>   {
S>       Socket ListenerSocket = new Socket();
S>       ListenerSocket.Bind( new IPEndPoint( ip, port ) );
S>       ListenerSocket.Listen();
S>       ListenerSocket.BeginAccept( OnAccept );
S>   }
    
S>   private void OnAccept( IAsyncResult ar )
S>   {
S>       // создаем новую сессию на входящее соединение от браузера
S>       ProxySession session = new ProxySession( ListenerSocket.EndAccept( ar ) );

S>       // подписываемся на события
S>       //session.OnClosed += OnSessionClosed; 
S>       //......

S>       Session.Add( session );
S>       session.Start();
S>   }
S>}

S>class ProxySession
S>{
S>   Socket ClientSocket;
S>   Dictionnary<string,Socket> ServerSocket = new Dictionnary<string,Socket>();

S>   public ProxySession( Socket socket ) { ClientSocket = socket; }
S>   public void Start() { ClientSocket.BeginReceive( ...., OnReceive ); }

S>   private void OnReceive( IAsynchResult ar )
S>   {
S>      try
S>      {
S>          ClientSocket.EndReceive( ar );
S>      }
S>      catch( SocketException )
S>      {
S>          // инициатором закрытия сессии может быть только клентсое соединение
S>          //  точнее, его разрыв
S>          CloseSession();      
S>          return;
S>      }
S>      ......
S>      // тут смотрим запрос от браузера, вытаскиваем хост, на кот. идет запрос
S>      // и если у нас нет такого в списке подключенных, то 
S>      //  создаем соотв. новое подключение к веб-серверу
S>      //  причем это нужно делать при каждом запросе от клиента
S>      //  т.е. в рамках одной сессии может быть один конект от клиента,
S>      //  и много подключений к веб-серверам
S>      Socket server = null;
S>      if( !ServerSocket.TryGetValue( hostName, out server )  )
S>      {
S>          server = new Socket( ... );
S>          ServerSocket.Add( hostName, server );
S>      }
S>      server.BeginSend( ..... );
S>      ClientSocket.BeginReceive( ...., OnReceive );
S>   }
S>}
S>



Все вроде сделала, только в OnAccept вываливается исключение:
Object reference not set to instance...
 
Подождите ...
Wait...
Пока на собственное сообщение не было ответов, его можно удалить.