Ситуация такая:
Грубо говоря прокси сервер. Слушаем порт и если что, Сокет 1 принимает запросы от web-браузера, затем мы в программе немного изменяем запрос и передаем их Сокету 2, а тот передает запрос Web-серверу.
Соответственно потом мы получаем данные от Web-сервера на Сокет 2 и передаем их Сокету 1(браузеру).
Как это реализовать, просто столько классов (TcpListener, Socket у которого есть метод Listen, TcpClient), я запуталась.
И как это сделать в отдельных потоках?
Здравствуйте, Tanketka, Вы писали:
T>Ситуация такая: T>Грубо говоря прокси сервер. Слушаем порт и если что, Сокет 1 принимает запросы от web-браузера, затем мы в программе немного изменяем запрос и передаем их Сокету 2, а тот передает запрос Web-серверу. T>Соответственно потом мы получаем данные от Web-сервера на Сокет 2 и передаем их Сокету 1(браузеру).
T>Как это реализовать, просто столько классов (TcpListener, Socket у которого есть метод Listen, TcpClient), я запуталась. T>И как это сделать в отдельных потоках?
Ну и зачем тут сокеты? Смотри в сторону HTTP Handlers
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#, и многопоточной,
то есть я так понимаю в главной функции создаем поток, в котором будем слушать порт и если что-то приходит, выполнять все это дело.
Здравствуйте, 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();
}
Думаю, идея понятна. Не обещаю, что все скомпилируется и запустится (+ надо сокеты за собой закрывать и т.д.), но при таком ходе мыслей у меня все получилось. Если что, вечером найду дома полный рабочий пример, залью.
Здравствуйте, 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. прокси отвечает только за передачу данных в бинарном виде.
всю работу по анализу трафика тоже лучше выделить в отдельные классы, и тоже
передавать в сессию / прокси через интерфейсы.
при таком подходе реализация какого-либо протокола — всего лишь новый набор фильтров.
Здравствуйте, Vector, Вы писали:
V>Думаю, идея понятна. Не обещаю, что все скомпилируется и запустится (+ надо сокеты за собой закрывать и т.д.), но при таком ходе мыслей у меня все получилось. Если что, вечером найду дома полный рабочий пример, залью.
Самая главная дыра — то что ответ от веб-сервера может приходить несколькими пакетами
( не только может, а будет — если ответ достаточно большой )
ну и такая архитектура, при которой мы заставляем браузер при каждом запросе
заново к нам конектится — очень тормознутая,
попробуй через такой прокси загрузить что-то вроде yahoo.com, он пока ее загрузит
на твоем аксепте мозоль натрет.
зы:
public Form1()
{
Socket listenSocket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
...............
Здравствуйте, 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 разбираться, читать все заголовки, соответственно все гораздо сложней и описывать это "на коленках" нереально. Опять же в моей задаче такого не возникало ниразу.
Здравствуйте, 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();
}
Здравствуйте, 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 кб., в этом случае браузер получит только кусок в лучшем случае.
Здравствуйте, 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...