Главная страница
    Top.Mail.Ru    Яндекс.Метрика
Форум: "Сети";
Текущий архив: 2010.10.31;
Скачать: [xml.tar.bz2];

Вниз

Как правильно написать socks-сервер?   Найти похожие ветки 

 
zsergey   (2008-01-13 21:01) [0]

Привет господа! Есть такая задача: написать простой сокетный сервер, который должен обслуживать несколько подключений одновременно. Я только начал разбирать с этим вопросом, просьба, не ругать. Вот навоял небольшой работающий пример, правда без обработок ошибок:

procedure TForm1.startserverClick(Sender: TObject);
var S,AcceptedSock:TSocket;
   Addr:TSockAddr;
   Data:TWSAData;
   Len:Integer;
   buf : array[0..30] of byte;
begin
 WSAStartup($101,Data);
 S:=Socket(AF_Inet,Sock_Stream,0);
 Addr.sin_family:=PF_Inet;
 Addr.sin_port:=HToNS(5005);
 Addr.sin_addr.S_addr:=InAddr_Any;
 FillChar(Addr.Sin_Zero,SizeOf(Addr.Sin_Zero),0);
 Bind(S,Addr,SizeOf(TSockAddr));
 Listen(S,SoMaxConn);
 Len:=SizeOf(TSockAddr);
 AcceptedSock:=Accept(S,@Addr,@Len);
 Recv(AcceptedSock,buf, sizeof(buf),0);
// дальше обрабатываю данные
end;

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


 
DVM ©   (2008-01-13 21:06) [1]


> в какую сторону хотя бы смотреть...

в сторону либо потоков либо в сторону асинхронных сокетов на сообщениях.

Для блокирующих сокетов Accept крутить в цикле (в идеале цикл в отдельном потоке), при подключении клиента создавать новый поток - передавать туда сокет и работать с конкретным клиентом в этом потоке.


 
DVM ©   (2008-01-13 21:08) [2]

Вот как то так (в коде могут быть ошибки!):


program server;

{$APPTYPE CONSOLE}
uses
 SysUtils,
 Winsock,
 Windows;

type

 ThreadData = record
   Socket: TSocket;
 end;
 PThreadData = ^ThreadData;

var
 WSAData: TWSAData;
 ListenSocket, ClientSocket: TSocket;
 Info: PThreadData;
 SockAddr: TSockAddr;
 ThreadId: THandle;
 hClientThread: Thandle;

const
 Port = word(25);

//------------------------------------------------------------------------------

procedure SocketThread(Info: PThreadData);
var
 SockName: TSockAddr;
 NameLen, OptLen: Integer;
 Buf: PChar;
 RecvSize: integer;
 s: TSocket;
 BuffSize: integer;
begin
 s := Info^.Socket;
 try
   NameLen := SizeOf(TSockAddr);
   if GetPeerName(s, SockName, NameLen) <> 0 then exit;
   Writeln(format("Client accepted, remote address [%s].", [inet_ntoa(SockName.sin_addr)]));
   OptLen := SizeOf(BuffSize);
   if GetSockOpt(s, SOL_SOCKET, SO_RCVBUF, pointer(@BuffSize), OptLen) <> 0 then exit;
   writeln(format("Receive buffer size [%d]", [BuffSize]));
   GetMem(Buf, BuffSize);
   try
     repeat
       RecvSize := recv(s, Buf^, BuffSize, 0);
       sleep(5);
       if RecvSize > 0 then
         writeln(format("Received from client: %s", [strpas(Buf)]));
     until RecvSize <= 0;
   finally
     FreeMem(Buf, BuffSize);
   end;
   Writeln(format("Client disconnected, remote address [%s].",[inet_ntoa(SockName.sin_addr)]));
 finally
   CloseSocket(s);
   Dispose(Info);
 end;  
end;

//------------------------------------------------------------------------------

begin
 Writeln("WSA Initialize...");
 if WSAStartup($101, WSAData) <> 0 then exit;
 try
   Writeln(format("Creating socket on port [%d].", [Port]));
   ListenSocket := socket(AF_INET, SOCK_STREAM, IPPROTO_IP);
   if ListenSocket = INVALID_SOCKET then exit;
   try
     FillChar(SockAddr, SizeOf(TSockAddr), 0);
     SockAddr.sin_family := AF_INET;
     SockAddr.sin_port := htons(Port);
     SockAddr.sin_addr.S_addr := INADDR_ANY;
     Writeln("Binding socket...");
     if Bind(ListenSocket, SockAddr, SizeOf(TSockAddr)) <> 0 then exit;
     if listen(ListenSocket, SOMAXCONN) <> 0 then exit;
     Writeln("Socket status: listening...");
     repeat
       Writeln("wait");
       ClientSocket := accept(ListenSocket, nil, nil);
       if ClientSocket <> INVALID_SOCKET then
         begin
           New(Info);
           Info^.Socket := ClientSocket;
           hClientThread := BeginThread(nil, 0, @SocketThread, Info, 0, ThreadId);
           if hClientThread <> 0 then CloseHandle(hClientThread);
         end;  
     until false;
   finally
     CloseSocket(ListenSocket);
   end;
 finally
   WSACleanup;
 end;

//------------------------------------------------------------------------------

end.


 
zsergey   (2008-01-13 21:25) [3]

Cпасибо за пример, буду разбираться.


 
zSergey   (2008-01-14 15:35) [4]

теперь задача немного усложняется: сам сервер я написал в виде библиотеки (*.dll), увы такое требование. Сервер будет запускаться сторонним приложением. Здесь проблем нет. Вопрос в том, как сервер должен передавать данные в вызвавшую его  программу?. Поясняю задачу: сам сервер пишется для связки с 1с v.8. Т.к. у 1с нет стандартных средств для работы с сокетами, приходиться прибегнуть с помошью внешних компонент (dll), где собственно будет находится сам функционал сервера. Далее, приняв данные, сервер должен в реальном времени передать их приложению-хозяину, приложение в это время не находиться в режиме ожидания, а продолжает выполнять весь свой функционал. Я понимаю, что это форум отношения к 1с не имеет, потому я пока рассматриваю вариант  взаимодействия программы и сервера на delphi. Буду благодарен любым идеям.


 
Сергей М. ©   (2008-01-14 16:16) [5]


> zSergey   (14.01.08 15:35) [4]


Какого хрена ты вообще взялся за WinsockAPI ?
Тебе что, компонентов готовых мало ?)


 
zSergey   (2008-01-14 16:31) [6]

> Сергей М.
ну зачем жет так резко, уважаемый? Ничего плохо в том, что я хочу разобрать с WinsockAPI нет. Да и сама библиотечка будет намного легче весить без компонент. Кстати, а как запихнуть компонент в Dll ?  :)Да и вопрос ведь в том, какой должен быть механизм обмена между 2 приложениями, пусть даже с использованием компонент?


 
zSergey   (2008-01-14 16:40) [7]


> Тебе что, компонентов готовых мало ?)

Кстати, а о каких компонентах речь идет?


 
DVM ©   (2008-01-14 22:59) [8]


> теперь задача немного усложняется: сам сервер я написал
> в виде библиотеки (*.dll), увы такое требование. Сервер
> будет запускаться сторонним приложением. Здесь проблем нет.
>  Вопрос в том, как сервер должен передавать данные в вызвавшую
> его  программу?.

А разве сервер запущенный из dll некой сторонней программой не находится в адресном пространстве процесса этой программы? Это передача не между процессами, а между частями одного и того же приложения (в данном случае между потоками).

Самый простой и надежный способ передачи через сообщения Windows.


 
Сергей М. ©   (2008-01-15 08:21) [9]


> о каких компонентах речь идет?


Мало ли таковых существует !

TTCPServer, TServerSocket у тебя прямо под рукой имеются.


> как запихнуть компонент в Dll


Точно также как в exe


 
ketmar ©   (2008-01-15 09:13) [10]

>[6] zSergey (14.01.08 16:31)
>какой должен быть механизм обмена между 2 приложениями
рабочий он должен быть. далее конкретизируется под конкретную задачу.

вот с той же адынцэ — это тебе ActiveX писать. где, кстати, пофигу, компоненты али не компоненты — всё равно будет жирное (если ты, конечно, не хардкорщик, желающий ваять ActiveX «с нуля»).


 
zSergey   (2008-01-15 13:33) [11]


> вот с той же адынцэ — это тебе ActiveX писать. где, кстати,
>  пофигу, компоненты али не компоненты — всё равно будет
> жирное (если ты, конечно, не хардкорщик, желающий ваять
> ActiveX «с нуля»).

Тоже хорошая идея, навоять собственный ActiveX контрол, чтобы было проще, на основе того же TServerSocket. Как это сделать? Полез я  визард "ActiveX control wizard", там нет родителя для TServerSocket, как я понял, так можно замутить только для визуальный компонент. возможно ли в данном случае сделать свой ActiveX на основе TSocketServer, если да, то как? (просьба не смеяться и не злиться) :)


 
Сергей М. ©   (2008-01-15 13:39) [12]

Сначала поясни, зачем тебе понадобился именно AX-контрол, да еще и невизуальный ?


 
zSergey   (2008-01-15 13:48) [13]


> Сначала поясни, зачем тебе понадобился именно AX-контрол,
>  да еще и невизуальный ?

потому что
> у 1с нет стандартных средств для работы с сокетами, приходиться
> прибегнуть с помошью внешних компонент (dll),

или еще как вариант - ActiveX :)


 
Сергей М. ©   (2008-01-15 13:51) [14]


> zSergey   (15.01.08 13:48) [13]


Зато у одноэсины есть механизм использования надстроек (1C Add-ins)
И она не имеет ни малейшего отношения к AX.


 
zSergey   (2008-01-15 13:56) [15]


> Зато у одноэсины есть механизм использования надстроек (1C
> Add-ins)
> И она не имеет ни малейшего отношения к AX.

это я понимаю! Просто мне интересно, а возжно и проще решить энту задачу через АХ. Итак, не посчитайте меня назойливым, как написать АХ на основе TSockerServer ?


 
zSergey   (2008-01-15 14:02) [16]

> Сергей М. ©
Напиши сам, делов-то на 10 минут. - Здесь вы имели в виду с нуля написать?


 
Сергей М. ©   (2008-01-15 14:03) [17]


> мне интересно, а возжно и проще решить энту задачу через АХ


Ходить в гости к соседу по двору через Китай тоже можно.


> как написать АХ на основе TSockerServer


Никак.


 
Сергей М. ©   (2008-01-15 14:04) [18]


> с нуля написать?


Почему бы и нет ?


 
zSergey   (2008-01-15 14:11) [19]


> Почему бы и нет ?

Не посчитайте назойливым, может дадите сылочку, где можно про это почитать... В книжке Архангельского подобного ничего нет.


 
Сергей М. ©   (2008-01-15 14:18) [20]


> где можно про это почитать


Дык в справке стандартной и почитай


 
DVM ©   (2008-01-15 17:33) [21]


> zSergey

Delphi автоматически позволяет создавать ActiveX только на основе потомков TWinControl кажется.


 
zsergey   (2008-01-16 19:53) [22]

Привет знатокам!
Написал сокет-сервер как сом-объект на WinApi (*.dll). Все хорошо, сокет открывается, слушается, данные из него читаются, но само приложение, использующее библиотеку, тормозит и дальше программа не может выполнить остальные свои функции, что не есть хорошо. Используются не блокирующие сокеты. Выкладываю весь исходник:

unit Utestlib;

{$WARN SYMBOL_PLATFORM OFF}

interface

uses
 ComObj, ActiveX, testlib_TLB, StdVcl, Dialogs, WinSock, SysUtils, Windows;

type
 TApp = class(TAutoObject, IApp)
 protected
   procedure OpenSocket; safecall;
end;

type
ThreadData = record
  Socket: TSocket;
end;
PThreadData = ^ThreadData;

var
WSAData: TWSAData;
ListenSocket, ClientSocket: TSocket;
Info: PThreadData;
SockAddr: TSockAddr;
ThreadId: THandle;
hClientThread: Thandle;

Arg : u_long;
s: TSocket;
const
Port = word(5555);

implementation

uses ComServ;
//////////////////////////////////////////
procedure SocketThread(Info: PThreadData);
var
SockName: TSockAddr;
NameLen, OptLen: Integer;
buf : array[0..35] of byte;
RecvSize: integer;

BuffSize,k: integer;
str : string;
error : integer;

begin
s := Info^.Socket;
try
  NameLen := SizeOf(TSockAddr);
  if GetPeerName(s, SockName, NameLen) <> 0 then exit;
  ShowMessage("Client accepted");
  OptLen := SizeOf(BuffSize);
  if GetSockOpt(s, SOL_SOCKET, SO_RCVBUF, pointer(@BuffSize), OptLen) <> 0 then exit;
  try
    repeat
      RecvSize := recv(s, Buf[0], Length(Buf), 0);
      if  RecvSize = SOCKET_ERROR then
         Begin
           error := WSAGetLastError();
           if (error<>WSAEWOULDBLOCK) then
               continue
           else RecvSize := 1
         end
      else
      sleep(5);
      if RecvSize > 1 then
         ShowMessage("Received from client ...");
    until RecvSize <= 0;
  finally
end;
  ShowMessage("Client disconnected ...");
finally
  CloseSocket(s);
  Dispose(Info);
end;
end;
//////////////////////////////////////////

procedure TApp.OpenSocket;
begin
ShowMessage("WSA Initialize ...");
if WSAStartup($101, WSAData) <> 0 then exit;
try
  ListenSocket := socket(AF_INET, SOCK_STREAM, IPPROTO_IP);
  // non bloking
   Arg:=1;
   IOCtlSocket(ListenSocket,FIONBIO,Arg);
  //
  if ListenSocket = INVALID_SOCKET then exit;
  try
    FillChar(SockAddr, SizeOf(TSockAddr), 0);
    SockAddr.sin_family := AF_INET;
    SockAddr.sin_port := htons(Port);
    SockAddr.sin_addr.S_addr := INADDR_ANY;
    if Bind(ListenSocket, SockAddr, SizeOf(TSockAddr)) <> 0 then exit;
      if listen(ListenSocket, SOMAXCONN) <> 0 then exit;
        repeat
           
          ClientSocket := accept(ListenSocket, nil, nil);
          if ClientSocket <> INVALID_SOCKET then
            begin
              New(Info);
              Info^.Socket := ClientSocket;
              hClientThread := BeginThread(nil, 0, @SocketThread, Info, 0, ThreadId);
              if hClientThread <> 0 then CloseHandle(hClientThread);
            end;
        until false;
  finally
  Arg:=0;
  IOCtlSocket(ListenSocket,FIONBIO,Arg);
  CloseSocket(ListenSocket);
  end;
finally
  WSACleanup;
end;
end;

initialization
 TAutoObjectFactory.Create(ComServer, TApp, Class_App,
   ciMultiInstance, tmApartment);
end.


что я не так делал?


 
Slym ©   (2008-01-17 04:28) [23]

zsergey   (16.01.08 19:53) [22]
не блокирующие сокеты

1. и нафега BeginThreadв твоем не блокирующем сокете?
2. чтобы он стал полноценно неблокирующим нужно вызвать Select функцию и в нее передать дескриптор окна в которое будут приходить сообщения об событиях сокета
3. неблокирующий у тебя только слушающий сокет (listen), остальные (accept) блокирующие т.к. становится им неблокирующими ты им не сказал


 
Slym ©   (2008-01-17 04:32) [24]

ты понацеплял ComObj, ActiveX, testlib_TLB, StdVcl, Dialogs и тебе жалко 10кб на нормальный сокет? ты еще сом-объект руками сваяй...
Slym ©   (17.01.08 4:28) [23]
4. в com объекте с потоками огребеш не зная предметной области


 
Сергей М. ©   (2008-01-17 10:11) [25]


> ShowMessage


Что еще за VCL-выкрутасы в дополнительном потоке ?


 
Сергей М. ©   (2008-01-17 10:21) [26]


> 3. неблокирующий у тебя только слушающий сокет (listen),
>  остальные (accept) блокирующие т.к. становится им неблокирующими
> ты им не сказал
>


Угу.
Но при этом в слушающем потоке, работающем с неблокирующим ListenSocket, почему-то нет ни намека на WSAEWOULDBLOCK, зато в клиентских потоках, работающих с блокирующими ClientSocket, WSAEWOULDBLOCK с какого-то перепугу фигурирует.


 
DVM ©   (2008-01-17 10:34) [27]


> zsergey   (16.01.08 19:53) [22]

Лучше бы ты на асинхронных неблокирующих сокетах сделал. На сообщениях. Ей богу проще для твоего случая. И доп потоки не нужны.


 
Сергей М. ©   (2008-01-17 10:42) [28]


> if GetSockOpt(s, SOL_SOCKET, SO_RCVBUF, pointer(@BuffSize),
>  OptLen) <> 0 then exit;


Совершенно непонятно, в каких случаях GetSockOpt может вернуть ошибку и для чего вообще запрашивается размер буфера приема, если этот полученный размер далее по ходу дела нигде не используется


 
Сергей М. ©   (2008-01-17 10:56) [29]


> Slym ©   (17.01.08 04:28) [23]


Впрочем ни ты ни я не правы  - ClientSocket у автора работает именно в неблок.режиме.

Цитата из справки по accept():

newly created socket has the same properties as s including asynchronous events registered with WSAAsyncSelect or with WSAEventSelect


 
Сергей М. ©   (2008-01-17 10:59) [30]

Но этот факт не меняет сути - логика repeat-цикла в кл.потоке с учетом неблок.режима не верна в принципе


 
Slym ©   (2008-01-17 11:08) [31]

Сергей М. ©   (17.01.08 10:56) [29]
Select

я то как раз прав...
ниодного select я не увидел...
если бы и увидел то было выглядело бы так:
WSAAsyncSelect(ListenSocket,hWnd,FD_ACCEPT,WM_USER);
этого для слушателя достаточно, а для ацепнутого сокета никаких Эвентов не прописано...


 
zSergey   (2008-01-17 11:38) [32]


> 2. чтобы он стал полноценно неблокирующим нужно вызвать
> Select функцию


> ниодного select я не увидел...
> если бы и увидел то было выглядело бы так:
> WSAAsyncSelect(ListenSocket,hWnd,FD_ACCEPT,WM_USER);

Хорошо, вот переделанный сервер с селектом (не СОМ)

program Project1;
uses
 SysUtils,
 WinSock;

{$APPTYPE CONSOLE}
var Sockets:array of TSocket;
   Addr:TSockAddr;
   Data:TWSAData;
   Len,I,J:Integer;
   FDSet:TFDSet;
   arrBuf : array [0..35] of byte;
   Arg:u_long;
   tv : Ttimeval;

begin
 WSAStartup($101,Data);
 SetLength(Sockets,1);
 Sockets[0]:=Socket(AF_Inet,Sock_Stream,0);
 Addr.sin_family:=AF_Inet;
 Addr.sin_port:=HToNS(21001);
 Addr.sin_addr.S_addr:=InAddr_Any;
 FillChar(Addr.Sin_Zero,SizeOf(Addr.Sin_Zero),0);
 Bind(Sockets[0],Addr,SizeOf(TSockAddr));
 Listen(Sockets[0],SoMaxConn);
 while True do
  begin
   FD_Zero(FDSet);
   for I:=0 to High(Sockets) do
   Arg:=1;
   IOCtlSocket(Sockets[I],FIONBIO,Arg);
   FD_Set(Sockets[I],FDSet);

   tv.tv_sec := 5;
   tv.tv_usec := 0;

   Select(0,@FDSet,nil,nil,@tv);

   if FDSet.fd_count=0 then Continue;
   sleep(100);
   writeln("wait");
   
   I:=1;
   while I<=High(Sockets) do
    begin
     if FD_IsSet(Sockets[I],FDSet) then
      if Recv(Sockets[I],arrBuf, sizeOf(arrBuf),0) <=0 then
       begin
       
        writeln("disconnect, socket close");
        CloseSocket(Sockets[I]);
        for J:=I to High(Sockets)-1 do
         Sockets[J]:=Sockets[J+1];
        Dec(I);
        SetLength(Sockets,Length(Sockets)-1)
       end
      else
       begin
        writeln("Receive data");

       end;
     Inc(I)
    end;
   

   if FD_IsSet(Sockets[0],FDSet) then
    begin
   
     writeln("new client connected");
     SetLength(Sockets,Length(Sockets)+1);
     Len:=SizeOf(TSockAddr);
     Sockets[High(Sockets)]:=Accept(Sockets[0],@Addr,@Len)
    end
  end;
end.


Но почему-то при тестировании select не возвращает сокеты для чтения, чего не должно быть. Где ошибка?


 
Сергей М. ©   (2008-01-17 11:41) [33]


> Slym ©   (17.01.08 11:08) [31]


> ниодного select я не увидел


Причем здесь select ?
Ну нет его и нет - и фиг с ним, я о другом сейчас.

Я о твоей ремарке

> остальные (accept) блокирующие т.к. становится им неблокирующими
> ты им не сказал


и о своей


> в клиентских потоках, работающих с блокирующими ClientSocket


А это неверно, поскольку слушающий сокет перед вызовом accept() переведен в неблок.режим (т.е. для него явно установлено опция-"свойство" nonblocking), что, с учетом цитаты в [29], при успешном вызове accept()  порождает новое неблокирующее гнездо.


 
Сергей М. ©   (2008-01-17 11:45) [34]


> select не возвращает сокеты для чтения


И не возвратит.

Куда у тебя accept()-то пропал ? Без него select как мертвому припарка)


 
zSergey   (2008-01-17 14:43) [35]


> И не возвратит.

нашел в чем ошибка:
FD_Set(Sockets[I],FDSet); в цикле не в операторских скобках;
для чего Arg:=1;
  IOCtlSocket(Sockets[I],FIONBIO,Arg);
перенес под Accept(); теперь заработало, НО само приложение в "замороженном" состоянии, будь там блокирующие сокеты или не блокирующие, будут там нити или нет. Я почти все перепробовал. Повторюсь, сервер должен не только обрабатывать подключения, но и одновременно сохранять другие свои ф-ции, короче, чтобы все контролы на форме были доступными. Вроде понятно объяснил.
> Лучше бы ты на асинхронных неблокирующих сокетах сделал.
>  На сообщениях. Ей богу проще для твоего случая. И доп потоки
> не нужны.

Господа, дайте пож-ста простой примерчик, на который мне можно было опереться в данном случае.


 
Сергей М. ©   (2008-01-17 15:13) [36]

ты все-таки определись, тебя на WSAPI заклинило окончательно и бесповоротно или только блажь ?


 
zSergey   (2008-01-17 15:32) [37]


> ты все-таки определись, тебя на WSAPI заклинило окончательно
> и бесповоротно или только блажь ?

ага мне вот делать нехер, низнаю чем заняться, WSAPI или еще чем. Уважаемый Сергей М, напоминаю, для чего мне все это нужно:

Поясняю задачу: сам сервер пишется для связки с 1с v.8. Т.к. у 1с нет стандартных средств для работы с сокетами, приходиться прибегнуть с помошью внешних компонент (dll), где собственно будет находится сам функционал сервера. Далее, приняв данные, сервер должен в реальном времени передать их приложению-хозяину, приложение в это время не находиться в режиме ожидания, а продолжает выполнять весь свой функционал. Я понимаю, что это форум отношения к 1с не имеет, потому я пока рассматриваю вариант  взаимодействия программы и сервера на delphi. Буду благодарен любым идеям.

ни внешних компонент, ни  бесплатных ActiveX контролов у меня нет, чтоб решить эту проблему, потому я вынужден ковырять wsapi ибо другого выхода я не вижу :)


 
Сергей М. ©   (2008-01-17 15:34) [38]


> мне вот делать нехер, низнаю чем заняться


Я вот тоже так думаю - тебе нечем более заняться)

ЧЕМ не устроили TServerSocket, TTCPServer, ICS-компоненты ?
Вразумительно ты так и не объяснил)


 
zSergey   (2008-01-17 15:41) [39]


> ЧЕМ не устроили TServerSocket, TTCPServer, ICS-компоненты
> ?
> Вразумительно ты так и не объяснил)

я не знаю как эти компоненты засунуть в dll :)


 
Сергей М. ©   (2008-01-17 15:47) [40]

А в ехе знаешь ?)
рассказывай) ..



Страницы: 1 2 вся ветка

Форум: "Сети";
Текущий архив: 2010.10.31;
Скачать: [xml.tar.bz2];

Наверх





Память: 0.58 MB
Время: 0.004 c
8-1207514958
nuflin
2008-04-07 00:49
2010.10.31
мировое освещение


2-1280993419
12
2010-08-05 11:30
2010.10.31
Что-то глючит, AV непонятно-плавающий.


2-1281011430
Scot Storch
2010-08-05 16:30
2010.10.31
парсинг имени файла


15-1280095096
0x00FF00
2010-07-26 01:58
2010.10.31
DMClient под Linux?


2-1281093695
Stenfit
2010-08-06 15:21
2010.10.31
TXMLDocument





Afrikaans Albanian Arabic Armenian Azerbaijani Basque Belarusian Bulgarian Catalan Chinese (Simplified) Chinese (Traditional) Croatian Czech Danish Dutch English Estonian Filipino Finnish French
Galician Georgian German Greek Haitian Creole Hebrew Hindi Hungarian Icelandic Indonesian Irish Italian Japanese Korean Latvian Lithuanian Macedonian Malay Maltese Norwegian
Persian Polish Portuguese Romanian Russian Serbian Slovak Slovenian Spanish Swahili Swedish Thai Turkish Ukrainian Urdu Vietnamese Welsh Yiddish Bengali Bosnian
Cebuano Esperanto Gujarati Hausa Hmong Igbo Javanese Kannada Khmer Lao Latin Maori Marathi Mongolian Nepali Punjabi Somali Tamil Telugu Yoruba
Zulu
Английский Французский Немецкий Итальянский Португальский Русский Испанский