Модераторы: Snowy, Poseidon, MetalFan

Поиск:

Ответ в темуСоздание новой темы Создание опроса
> Многопоточный сервак 
:(
    Опции темы
Antony41
Дата 14.2.2013, 19:27 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Опытный
**


Профиль
Группа: Участник
Сообщений: 332
Регистрация: 27.12.2008

Репутация: нет
Всего: 1



Цитата(Чучмек @  14.2.2013,  12:42 Найти цитируемый пост)
Вызовется несколько раз proc. Остаток (меньший, чем размер TData) ,будет хранится в выделенной памяти. Адрес  выделенной памяти хранится в связанном с сокетом указателе Data. На каждый сокет свой указатель, своя память. Это гарантирует, что при получении, данные разных соединений не будут смешиваться.


тоесть если я правильно понимаю, например допустим мне осталось принять 10 байт до полного пакета данных, а пришло 
1000 байт (10 байт склееный со след. пакетом), и я сделаю вот так

обработчик OnRead...
Код

I:=Socket.ReceiveBuf(ReadBuf, 10);

затем отправлю пакет на обработку, то при следующем вызове обработчика в Socket.ReceiveLength будут оставшиеся байты (990 байт) ?

Добавлено через 2 минуты и 26 секунд
тоесть если пришло 1000 байт 
а я буду читать по 1 байту
Socket.ReceiveBuf(ReadBuf, 1);

то обработчик будет вызываться 1000 раз?
PM MAIL   Вверх
Чучмек
Дата 14.2.2013, 21:05 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


НЭТ БИЛЭТ
**


Профиль
Группа: Участник
Сообщений: 841
Регистрация: 11.5.2008
Где: СССР

Репутация: 4
Всего: 41



Скажем так. 
Твоя структура 200 байт
Пришло 199 байт.
Событие OnRead.
Выделили память 4+200
В выделенную память записали 199 принятых байт и Integer=199
Пришло еще 399 байт
Событие OnRead.
В выделенную память дописали 1 байт
Вызвали proc, передали принятую структуру.
В выделенную память записали 200 байт
Вызвали proc, передали принятую структуру.
В выделенную память записали 198 байт и Integer=198
ждем еще.

Добавлено через 5 минут и 53 секунды
Че гадать, когда легко можно проверить.
Код

unit Unit1;

interface

uses
  Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
  Dialogs,ScktComp, StdCtrls,winsock;

type
  TForm1 = class(TForm)
    Button1: TButton;
    Memo1: TMemo;
    procedure Button1Click(Sender: TObject);
  private
    { Private declarations }
  public
    { Public declarations }
    procedure fr(Sender: TObject; Socket: TCustomWinSocket);
    procedure fc(Sender: TObject; Socket: TCustomWinSocket);
    procedure fd(Sender: TObject; Socket: TCustomWinSocket);
  end;

var
  Form1: TForm1;

implementation

{$R *.dfm}

const
 Host:PAnsiChar='ya.ru';
var
 vs:TClientSocket;

procedure TForm1.Button1Click(Sender: TObject);
var p:pansichar;
begin
vs:=TClientSocket.Create(form1);
p:=gethostbyname(Host)^.h_addr_list^;
vs.Host:= inttostr(PByte(p)^)+'.'+inttostr(PByte(p+1)^)+'.'+inttostr(PByte(p+2)^)+'.'+inttostr(PByte(p+3)^);
vs.Port:=80;
vs.Open;
vs.OnRead:=fr;
vs.OnConnect:=fc;
vs.OnDisconnect:=fd;
end;

procedure TForm1.fc(Sender: TObject; Socket: TCustomWinSocket);
var
 s:ansistring;
begin
s:='GET / HTTP/1.1'#13#10+
   'Connection: close'#13#10+
   'Host: '+Host+#13#10+
   #13#10;
Socket.SendBuf(s[1],length(s));
end;


type
 TDATA=array[0..0]of ansichar; //..0,..1,..10,..100
var
 s:string;

procedure proc(const d:TDATA);
begin
 s:=s+d;
 Form1.Memo1.Text:=s;
end;

procedure TForm1.fd(Sender: TObject; Socket: TCustomWinSocket);
begin
if Socket.Data<>nil then FreeMem(Socket.Data);
end;

procedure TForm1.fr(Sender: TObject; Socket: TCustomWinSocket);
type
 pr=^tr;
 tr=record
  count:integer;
  data:TData;
 end;
var
 p:pr;
 rb,br:integer;
begin
if  socket.Data = nil then     //Если память для структуры tr не выделенна - выделяем
  begin
  GetMem(p,sizeof(tr));        //По завершенни работы необходимо вызвать FreeMem для socket.Data
  socket.Data:=p;
  p^.count:=0;
  end else p:=socket.Data;
repeat
br:=sizeOf(TData)-p^.count;    //Сколько байт прочитать
rb:=Socket.ReceiveBuf((PAnsiChar(@(p^.data))+p^.count)^,br); //только Ansi !!!
p^.count:=p^.count+rb;         //Уже записанно байт в p^.data
if p^.count=sizeOf(TData) then //Если p^.data полностью заполненна
 begin
 proc(p^.data);  //передаем p^.data в обработчик
 p^.count:=0;
 end;
until rb<br;    //Пока есть принятые данные читаем
end;

end.



--------------------
умную мысль держи при себе, а дурной - поделись с другими 
PM MAIL   Вверх
Antony41
Дата 14.2.2013, 21:17 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Опытный
**


Профиль
Группа: Участник
Сообщений: 332
Регистрация: 27.12.2008

Репутация: нет
Всего: 1



Спасибо Вам огромное! smile  Вы все очень помогли и дали мне понять в чем заключалась моя ошибка, а главное разъяснили и разжевали всё надо! Пока тему не буду закрывать, возможно возникнет в ближайшие дни нерешимая без вашей помощи ситуация.
PM MAIL   Вверх
Чучмек
Дата 14.2.2013, 21:19 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


НЭТ БИЛЭТ
**


Профиль
Группа: Участник
Сообщений: 841
Регистрация: 11.5.2008
Где: СССР

Репутация: 4
Всего: 41



Кроме того. Если ты не используешь устойчивое соединение, можешь прочитать сразу все данные в OnDisconnect.
Код

unit Unit1;

interface

uses
  Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
  Dialogs,ScktComp, StdCtrls,winsock;

type
  TForm1 = class(TForm)
    Button1: TButton;
    Memo1: TMemo;
    procedure Button1Click(Sender: TObject);
  private
    { Private declarations }
  public
    { Public declarations }
    procedure fc(Sender: TObject; Socket: TCustomWinSocket);
    procedure fd(Sender: TObject; Socket: TCustomWinSocket);
  end;

var
  Form1: TForm1;

implementation

{$R *.dfm}

const
 Host:PAnsiChar='ya.ru';
var
 vs:TClientSocket;

procedure TForm1.Button1Click(Sender: TObject);
var p:pansichar;
begin
vs:=TClientSocket.Create(form1);
p:=gethostbyname(Host)^.h_addr_list^;
vs.Host:= inttostr(PByte(p)^)+'.'+inttostr(PByte(p+1)^)+'.'+inttostr(PByte(p+2)^)+'.'+inttostr(PByte(p+3)^);
vs.Port:=80;
vs.Open;
vs.OnConnect:=fc;
vs.OnDisconnect:=fd;
end;

procedure TForm1.fc(Sender: TObject; Socket: TCustomWinSocket);
var
 s:ansistring;
begin
s:='GET / HTTP/1.1'#13#10+
   'Connection: close'#13#10+
   'Host: '+Host+#13#10+
   #13#10;
Socket.SendBuf(s[1],length(s));
end;


type
 TDATA=array[0..2]of ansichar;
var
 s:string;

procedure proc(const d:TDATA);
begin
 s:=s+d;
 Form1.Memo1.Text:=s;
end;

procedure TForm1.fd(Sender: TObject; Socket: TCustomWinSocket);
var d:TDATA;
begin
while sizeof(TDATA)=Socket.ReceiveBuf(d,sizeof(TDATA)) do proc(d);
end;


end.



--------------------
умную мысль держи при себе, а дурной - поделись с другими 
PM MAIL   Вверх
Antony41
Дата 17.2.2013, 20:47 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Опытный
**


Профиль
Группа: Участник
Сообщений: 332
Регистрация: 27.12.2008

Репутация: нет
Всего: 1



Ребят вот еще какой вопрос. Сервак запущен в блочном режиме, и создаются отдельные потоки, в самом потоке прописана вот эта строка
Код

while (not Terminated) and (ClientSocket.Connected) do


И эта строка грузит мой цп на 25% при 1 подключенном клиенте, почему так как быть?
PM MAIL   Вверх
Чучмек
Дата 17.2.2013, 21:20 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


НЭТ БИЛЭТ
**


Профиль
Группа: Участник
Сообщений: 841
Регистрация: 11.5.2008
Где: СССР

Репутация: 4
Всего: 41



потому что процессор четырёхядерный


--------------------
умную мысль держи при себе, а дурной - поделись с другими 
PM MAIL   Вверх
Antony41
Дата 17.2.2013, 21:37 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Опытный
**


Профиль
Группа: Участник
Сообщений: 332
Регистрация: 27.12.2008

Репутация: нет
Всего: 1



ну 4 и что, его хватит только на 4 потока... и потом цп будет загружен на 100%. как быть то чтоб не такой прожерливый был? Может быстро  обрабатывать данные и выходить из потока, отключать клиента, а клиент будет слать серваку каждые например 30-40 сек команду что он на связи... серв же посчитает его отключенным только после 5 попыток поставит ему статус офлайн. А вот если выполняешь какой нибудь запрос от клиента то отсылать сразу его и если нужно соединиться например для передачи какого нибудь файла, стрима, затем снова отрубить.
PM MAIL   Вверх
Чучмек
Дата 17.2.2013, 21:51 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


НЭТ БИЛЭТ
**


Профиль
Группа: Участник
Сообщений: 841
Регистрация: 11.5.2008
Где: СССР

Репутация: 4
Всего: 41



Ну самое простое, но неправильное, вставить в цикл sleep(100)


--------------------
умную мысль держи при себе, а дурной - поделись с другими 
PM MAIL   Вверх
Antony41
Дата 17.2.2013, 21:59 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Опытный
**


Профиль
Группа: Участник
Сообщений: 332
Регистрация: 27.12.2008

Репутация: нет
Всего: 1



ну а правильное ?
PM MAIL   Вверх
Чучмек
Дата 17.2.2013, 22:43 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


НЭТ БИЛЭТ
**


Профиль
Группа: Участник
Сообщений: 841
Регистрация: 11.5.2008
Где: СССР

Репутация: 4
Всего: 41



Ну вот посмотри.
Код

unit Unit1;

interface

uses
  Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
  Dialogs,ScktComp, StdCtrls;

type
  TForm1 = class(TForm)
    Button1: TButton;
    procedure FormCreate(Sender: TObject);
  private
    { Private declarations }
  public
    { Public declarations }
    procedure fl(Sender: TObject; Socket: TCustomWinSocket);
  end;

var
  Form1: TForm1;

implementation

{$R *.dfm}
type
 tmt=class (tthread)
  fs:TCustomWinSocket;
  procedure execute;override;
  constructor create(s:TCustomWinSocket);
 end;

var ss:TServerSocket;


procedure TForm1.fl(Sender: TObject; Socket: TCustomWinSocket);
begin
tmt.create(Socket);
end;

{ tmt }

constructor tmt.create(s: TCustomWinSocket);
begin
inherited create(true);
fs:=s;
Resume;
end;

procedure tmt.execute;
var
 s,sh:ansistring;
 buf:array[0..255]of ansichar;
 l:integer;
begin
repeat
 l:=fs.ReceiveBuf(buf,sizeof(buf));
 s:=s+copy(buf,0,l);
until (Pos(#13#10#13#10,s)>0) or (Pos(#13#13,s)>0);
sh:='HTTP/1.1 200 Ok'#13#10+
    'Connection: close'#13#10+
    #13#10;
fs.SendBuf(sh[1],length(sh));
fs.SendBuf(s[1],length(s));  
fs.Disconnect(fs.SocketHandle);
free;
end;

procedure TForm1.FormCreate(Sender: TObject);
begin
   ss:=TServerSocket.Create(form1);
   ss.ServerType :=stThreadBlocking;
   ss.Port:=3600;
   ss.OnAccept:=fl;
   ss.Open;
end;

end.

Скомпилируй запусти, и введи в браузере http://localhost:3600/


--------------------
умную мысль держи при себе, а дурной - поделись с другими 
PM MAIL   Вверх
Чучмек
Дата 18.2.2013, 09:17 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


НЭТ БИЛЭТ
**


Профиль
Группа: Участник
Сообщений: 841
Регистрация: 11.5.2008
Где: СССР

Репутация: 4
Всего: 41



Antony41, я заврался. Так тоже не годится.
Оно конечно работает, но через костыль.

Это сообщение отредактировал(а) Чучмек - 18.2.2013, 11:39


--------------------
умную мысль держи при себе, а дурной - поделись с другими 
PM MAIL   Вверх
Чучмек
Дата 18.2.2013, 11:29 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


НЭТ БИЛЭТ
**


Профиль
Группа: Участник
Сообщений: 841
Регистрация: 11.5.2008
Где: СССР

Репутация: 4
Всего: 41



Antony41, советую разобраться с сокетами (API)
Вот как выглядит сервер на WinSock API. (без проверки ошибок)
Код

unit Unit1;

interface

uses
  Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
  Dialogs, StdCtrls,winsock;

type
  TForm1 = class(TForm) 
    procedure FormActivate(Sender: TObject);
  private
    { Private declarations }
  public
    { Public declarations }
  end;

var
  Form1: TForm1;

implementation

uses Mtsobj;

{$R *.dfm}

type
 tmt=class (tthread)
  fs:integer;
  procedure execute;override;
  constructor create(s:integer);
 end;

var
 thread_count:integer=0;  //Счетчик потоков

{ tmt }

constructor tmt.create(s: integer);
begin
inherited create(true);
fs:=s;
Resume;
end;

procedure tmt.execute;
var
 s,sh:ansistring;
 buf:array[0..255]of ansichar;
 l:integer;
begin
repeat
 l:=recv(fs,buf,sizeof(buf),0);    //Читаем данные, пока не дойдем до пустой строки  (конец http GET запроса)
 s:=s+copy(buf,0,l);
until (l=0) or (Pos(#13#10#13#10,s)>0) or (Pos(#13#13,s)>0);
if l>0 then   //Пока не разорвется соединение l>0
 begin
 sh:='HTTP/1.1 200 Ok'#13#10+
     'Content-type: text/plain;charset=windows-1251'#13#10+
     'Connection: close'#13#10+
     #13#10;
 s:='Прувет я Сэрвэр'#13#10'Заголовки запроса:'#13#10+s;
 send(fs,sh[1],length(sh),0);      //Отправляем заголовки
 send(fs,s[1],length(s),0);        //Отправляем страницу
 end;
closesocket(fs);                  //закрываем сокет
InterlockedDecrement(thread_count);  //счетчик потоков -1
free;
end;

procedure TForm1.FormActivate(Sender: TObject);
var
 wsd:TWSAData;
 s,cs:integer;
 sa:TSockAddr;
 sl:TFDSet;
 timeout: TIMEVAL;
begin
WSAStartup(1,wsd);                         //Инициализируем библиотеку сокетов
s:=socket(AF_INET,SOCK_STREAM,IPPROTO_IP); //Создаем сокет для работы с TCP/IP
with sa do                                 //Заполняем структуру адресса сокета
 begin
 sin_family:=PF_INET;
 sin_port:=htons(3600);
 sin_addr.S_addr:=INADDR_ANY;
 FillChar(sin_zero,sizeof(sin_zero),0);
 end;
bind(s,sa,sizeof(sa));                     //Привязываем сокет к адресу
listen(s,0);                               //Ставим сокет на ожидание подключения
while true do
 begin
 sl.fd_count:=1;                           //Добавляем сокет в список проверки готовности
 sl.fd_array[0]:=s;
 timeout.tv_sec:=0;
 timeout.tv_usec:=100000;                  //Время ожидания 100 мс
 if 0=select(0,@sl,nil,nil,@timeout) then  //Ожидаем подключение
  begin   //Если таймаут - проверяем  завершение работы сервера
  Application.ProcessMessages;
  if Application.Terminated then
   begin
   Application.Minimize;
   Application.ProcessMessages;
   while thread_count>0 do sleep(100); //ожидаем завершение всех потоков
   closesocket(s);
   WSACleanup;
   exit;
   end;
  end else
  begin    //Если подключение -
  cs:=accept(s,nil,nil);   //Получаем сокет обслуживающий соединение
  InterlockedIncrement(thread_count);
  TMT.Create(cs); //создаем поток обработки соединения
  end;
end;


end;


end.

p.s. T(Clent)ServerSocket всего лишь ООП обертка для WinSock API.

Вот тоже на TServerSocket
Код

type
  TForm1 = class(TForm)
    procedure FormCreate(Sender: TObject);
  private
    { Private declarations }
  public
    { Public declarations }
    procedure fg(Sender: TObject; ClientSocket: TServerClientWinSocket;var SocketThread: TServerClientThread);
  end;

var
  Form1: TForm1;

implementation

{$R *.dfm}
type
 tmt=class(TServerClientThread)
  procedure ClientExecute;override;
 end;

var ss:TServerSocket;

procedure TForm1.fg(Sender: TObject; ClientSocket: TServerClientWinSocket;
  var SocketThread: TServerClientThread);
begin
 SocketThread:=tmt.Create(false,ClientSocket);
end;

procedure TForm1.FormCreate(Sender: TObject);
begin
ss:=TServerSocket.Create(form1);
ss.Port:=3600;
ss.ServerType:=stThreadBlocking;
ss.OnGetThread:=fg;
ss.Open;
end;


{ tmt }

procedure tmt.ClientExecute;
var
 s,sh:ansistring;
 buf:array[0..255]of ansichar;
 l:integer;
begin
repeat
 l:=ClientSocket.ReceiveBuf(buf,sizeof(buf));
 s:=s+copy(buf,0,l);
until (l=0) or (Pos(#13#10#13#10,s)>0) or (Pos(#13#13,s)>0);
if l>0 then
 begin
 sh:='HTTP/1.1 200 Ok'#13#10+
     'Content-type: text/plain;charset=windows-1251'#13#10+
     'Connection: close'#13#10+
     #13#10;
 s:='Прувет я Сэрвэр'#13#10'Заголовки запроса:'#13#10+s;
 ClientSocket.SendBuf(sh[1],length(sh));
 ClientSocket.SendBuf(s[1],length(s));
 end;
ClientSocket.Close;
end;

p.p.s При закрытии, оба варианта будут ждать, пока не разорвется соединение. Только тогда   recv/ClientSocket.ReceiveBuf вернет управление.



Это сообщение отредактировал(а) Чучмек - 18.2.2013, 16:24


--------------------
умную мысль держи при себе, а дурной - поделись с другими 
PM MAIL   Вверх
Чучмек
Дата 18.2.2013, 14:38 (ссылка) |    (голосов:1) Загрузка ... Загрузка ... Быстрая цитата Цитата


НЭТ БИЛЭТ
**


Профиль
Группа: Участник
Сообщений: 841
Регистрация: 11.5.2008
Где: СССР

Репутация: 4
Всего: 41



Цитата(Antony41 @  17.2.2013,  20:47 Найти цитируемый пост)
1:
    
while (not Terminated) and (ClientSocket.Connected) do


И эта строка грузит мой цп на 25% при 1 подключенном клиенте, почему так как быть? 

Ответ один - внутри цикла нет, или не правильно используется функция ожидания.
Например. TWinSocketStream.WaitForData возвращает управление если в сокете остались непрочитанные данные.
TWinSocketStream - редкое г. Лучше не используй его. На мой взгляд - бесполезный компонент. В TWinSocketStream используются только read/write/WaitForData
Если при остановке сервера необходимо сразу остановить все потоки (ClientSocket.Connected возвращает false)-
вместо TWinSocketStream.WaitForData используй можно использовать
Код

uses ...,WinSock;
...
function WaitForData(socket:TCustomWinSocket;Timeout: Longint):boolean;
var
  FDSet: TFDSet;   
  TimeVal: TTimeVal;
begin
  TimeVal.tv_sec := Timeout div 1000;
  TimeVal.tv_usec := (Timeout mod 1000) * 1000;
  FDSet.fd_count:=1;
  FDSet.fd_array[0]:=socket.SocketHandle;
  Result := select(0, @FDSet, nil, nil, @TimeVal) > 0;
end;
 
WaitForData(ClientSocket,timeout) -> true, если есть данные для чтения и false, если timeout

Это сообщение отредактировал(а) Чучмек - 18.2.2013, 16:26


--------------------
умную мысль держи при себе, а дурной - поделись с другими 
PM MAIL   Вверх
Antony41
Дата 18.2.2013, 16:28 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Опытный
**


Профиль
Группа: Участник
Сообщений: 332
Регистрация: 27.12.2008

Репутация: нет
Всего: 1



Цитата(Чучмек @  18.2.2013,  14:38 Найти цитируемый пост)
TWinSocketStream.WaitForData

Оно вобще иногда не всегда корректно работает

Спасибо Тебе за ясность! щас буду испытывать

Добавлено через 4 минуты и 6 секунд
PS по сути то я и не использую WaitForData, а использую ожидание через GetTickCount - Tick >= TIME_OUT
PM MAIL   Вверх
Чучмек
Дата 18.2.2013, 16:36 (ссылка) |    (голосов:1) Загрузка ... Загрузка ... Быстрая цитата Цитата


НЭТ БИЛЭТ
**


Профиль
Группа: Участник
Сообщений: 841
Регистрация: 11.5.2008
Где: СССР

Репутация: 4
Всего: 41



Цитата(Antony41 @  18.2.2013,  16:28 Найти цитируемый пост)
Оно вобще иногда не всегда корректно работает

Да нет, должно работать.
Возвращает true, если есть данные для чтения и false, если таймаут или ошибка.
Однако если прочитано меньше чем пришло - всегда true и без ожидания.
Один байт не прочитал - и будет твой поток грузится на 100%.



--------------------
умную мысль держи при себе, а дурной - поделись с другими 
PM MAIL   Вверх
Ответ в темуСоздание новой темы Создание опроса
Правила форума "Delphi: Сети"
Snowy
Poseidon
MetalFan

Запрещено:

1. Публиковать ссылки на вскрытые компоненты

2. Обсуждать взлом компонентов и делится вскрытыми компонентами

  • Литературу по Дельфи обсуждаем здесь
  • Действия модераторов можно обсудить здесь
  • С просьбами о написании курсовой, реферата и т.п. обращаться сюда
  • Вопросы по реализации алгоритмов рассматриваются здесь
  • 90% ответов на свои вопросы можно найти в DRKB (Delphi Russian Knowledge Base) - крупнейшем в рунете сборнике материалов по Дельфи

Если Вам помогли и атмосфера форума Вам понравилась, то заходите к нам чаще! С уважением, Snowy, Poseidon, MetalFan.

 
0 Пользователей читают эту тему (0 Гостей и 0 Скрытых Пользователей)
0 Пользователей:
« Предыдущая тема | Delphi: Сети | Следующая тема »


 




[ Время генерации скрипта: 0.0585 ]   [ Использовано запросов: 22 ]   [ GZIP включён ]


Реклама на сайте     Информационное спонсорство

 
По вопросам размещения рекламы пишите на vladimir(sobaka)vingrad.ru
Отказ от ответственности     Powered by Invision Power Board(R) 1.3 © 2003  IPS, Inc.