Авторские статьи УЧИМСЯ ИСПОЛЬЗОВАТЬ WINSOCK. ЧАСТЬ II.

Discussion in 'Статьи' started by slesh, 14 Jun 2009.

  1. slesh

    slesh Elder - Старейшина

    Joined:
    5 Mar 2007
    Messages:
    2,702
    Likes Received:
    1,224
    Reputations:
    455
    УЧИМСЯ ИСПОЛЬЗОВАТЬ WINSOCK. ЧАСТЬ II.

    (подключение, передача и прием данных. многопоточность.) ​


    ПРЕДИСЛОВИЕ
    Как и обещал в первой статье, сейчас будут рассмотрены:
    - Функции подключения к удаленному серверу и ожидание входящих подключений
    - Передачи и приема данных.
    - Организация многопоточности клиента и сервера.
    - Будут рассмотрены вопросы, связанные с пингованием серверов и таймаута для подключения.

    ПОДКЛЮЧЕНИЕ
    Предположим что у нас уже был создан сокет sock:TSOCKET и настроены все параметры caddr:sockaddr_in. (читайте первую статью)
    Для осуществления подключения к другому компьютеру, необходимо использовать функцию connect
    function connect( const s: TSocket; const name: PSockAddr; namelen: Integer): Integer; stdcall;
    Первым параметром передается созданный сокет
    Вторым – адрес структуры sockaddr_in
    Третьим – размер структуры sockaddr_in в байтах.
    При удачном подключении будет возвращено число 0.
    Для распознания кода ошибки необходимо использовать функцию WSAGetLastError которая возвращает код ошибки. Коды ошибок можно посмотреть в MSDN
    Но на практике важны следующие значения:
    WSAECONNREFUSED – сервер отверг попытку подключения
    WSAENETUNREACH – сеть недоступна
    WSAETIMEDOUT – не удалось подключить к серверу в установленный промежуток времени(банальный timeout).
    На практике это выглядит как
    Code:
    if connect(sock, @caddr, SizeOf(caddr))=0 then
     begin
       удачно подключились
     end
    else
     begin
       обработка ошибки
     end;
    
    Важно заметить, что по умолчанию в Windows XP/2003 время таймаута для коннекта стоит 20-45 секунд. В некоторых случаях это очень неудобно когда, к примеру используется работа со списками прокси серверов и при попадании на мертвый сервер идет большая пауза, по этому для того, чтобы учитывать такие ситуации (когда они критически важны) необходимо применять различные способы. Каждый из этих способов имеет свои плюсы и минусы. В основе этого лежат 3 способа
    1) предварительное пингование сервера – очень удобно чтобы сразу определить доступен сервак или нет. Но есть минусы – не все серваки отвечают на пинг, а также пинг не может свидетельствовать об открытости нужного порта.
    2) запуск отдельного потока и в нем выполнение действий, и при истечении таймаута закроется сокет и прибьется поток. – отнимает ресурсы очень сильно, но зато в его помощью можно задать таймаут на выполнения блока функций. К примеру, функция которая получает данные от клиентов или от сервера может завершить свою работу по истечению таймаута, независимо от того, на чем остановилась работа.
    Алгоритм выглядит примерно так:
    procedure f():stdcall;
    begin
    создает сокет и помещает его в глобальную переменную, далее идет работа с сетью, а затем закрытие сокета
    end;
    В основной программе создается поток через CreateThread
    И затем дескриптор потока передается функции WaitForSingleObject, в которой как раз и задается таймаут. Если функция завершилась с кодом WAIT_TIMEOUT, значит, сработал таймаут, и тогда необходимо самому закрыть сокет (CloseSocket) и завершить поток через TerminateThread
    Также аналогом данного метода может быть применение таймеров. В таком случае делается наоборот всё. Т.е. перед выполнение работы с сетью запускается таймер – settimer. И в его обработчике уже делаются все действия связанные с таймаутом. Но как видно – метод очень сильно тратит ресурсы.
    3) временное применение неблокируемых сокетов. Сущность данного способа заключается в том, что мы создаем сокет, затем переводим его в неблокируемый режим, затем пытаемся подключиться, а далее обратно возвращаем сокет в блокируемый режим.

    Рассмотрим 1 и 3 способ более подробно т.к. они более актуальны сейчас.

    ВРЕМЕННЫЙ ПЕРЕВОД В НЕБЛОКИРУЕМЫЙ РЕЖИМ
    Code:
    var
     sock:TSOCKET;
     block:bool;
     timeout:ttimeval;
     fds:TFDSet;
     rc:integer;
    .-.-.-.-.-.-.-.-.--
    begin
    .-.-.-.-.-.-.-.-.--
     block:=true;
     ioctlsocket(sock, FIONBIO, cardinal(block)); // переводим сокет в неблокируемый режим
     if connect(sock, @caddr, SizeOf(caddr))=SOCKET_ERROR then // пытаемся подключиться
      begin
       if WSAGetLastError=WSAEWOULDBLOCK then // проверяем что сокет перешел в неблокируемый режим
         begin
          FD_ZERO(fds);
          FD_SET(sock,fds);
          timeout.tv_sec:=10; // таймаут 10 секунд
          timeout.tv_usec:=0;
          rc:=select(0, nil, @fds, nil, @timeout); // ожидаем 
         end;
       end;
     block:=false;
     ioctlsocket(sock, FIONBIO, cardinal(block)); // переводим сокет обратно в блокируемый режим
    if rc=0 then
     begin
       // сработал таймаут
     end
    else
     begin
       // удачно подключились
     end;
        
    ПРЕДВАРИТЕЛЬНОЕ ПИНГОВАНИЕ СЕРВЕРА
    Пингование будем осуществлять через функции icmp.dll
    Code:
    function IcmpCreateFile : THandle; stdcall; external 'icmp.dll';
    function IcmpSendEcho (IcmpHandle : THandle; DestinationAddress : TInAddr;
        RequestData : Pointer; RequestSize : Smallint;
        RequestOptions : pointer;
        ReplyBuffer : Pointer;
        ReplySize : DWORD;
        Timeout : DWORD) : DWORD; stdcall; external 'icmp.dll';
    
    
    Type // структуры кторые нам понадобятся
     ip_option_information = record
      Ttl : byte;
      Tos : byte;
      Flags : byte;
      OptionsSize : byte;
      OptionsData : pointer;
    end;
    
    type
    ICMP_ECHO_REPLY =
    record
     Address : TInAddr;
     Status : ULONG;
     RoundTripTime : ULONG;
     DataSize : Word;
     Reserved : Word;
     Data : Pointer;
     Options : IP_OPTION_INFORMATION;
     PingBuf: array[0..31] of char;
    end;
    
    function Ping(ip:string;timeout:cardinal):boolean;
    var
     Handle:THandle;// дискрипт icmp
     InAddr:TInAddr;// адрес который пинговать будем
     DW:DWORD;//результат пингования
     Reply:ICMP_ECHO_REPLY;// структура echo запроса
     PingBuf: array[0..31] of char;// буфер с данными для пинга
    begin
     result:=false;
     Handle:=IcmpCreateFile;// открываем ICMP
     if Handle=INVALID_HANDLE_VALUE then exit; // если неудалось открыть
     InAddr.S_addr:=inet_addr(Pansichar(ip)); // кого будем пинговать
     Reply.Data:=@pingBuf;// буфер для пингования
     Reply.DataSize:=32;// размер буфера
     DW:=IcmpSendEcho(Handle, InAddr, @PingBuf, 32, nil, @reply, SizeOf(icmp_echo_reply)+32,timeout);// посылаем пинг
     if DW<>0 then result:=true else result:=false;
    end;
    
    Теперь если нам необходимо проверить жив сервак или нет мы может использовать эту функцию так:
    Code:
    if Ping(‘xxx.xxx.xxx.xxx’,3000) then
     begin
      пингуется
     end
    else
     begin
      НЕ пингуется
     end;
    
    первым параметром задается IP адрес сервера. (доменное имя нельзя)
    вторым – время ожидания ответа на пинг в миллисекундах.
    т.е. 3000 – 3 секунды.

    ОЖИДАНИЕ ВХОДЯЩЕГО ПОДКЛЮЧЕНИЯ
    Если до этого мы рассматривали в основном случае, когда нам необходимо было подключиться к какому либо серверу, то сейчас будет рассмотрена ситуация когда мы будем выступать в роле сервера. Для этого существует рад команд и последовательность их использования.
    1) bind – ассоциирование локального адреса с сокетом.
    function bind( const s: TSocket; const addr: PSockAddr; const namelen: Integer ): Integer; stdcall;
    первый параметр – сокет
    второй – ссылка на структуру sockaddr_in в которой необходимо указать порт на котором мы будем ожидать подключение и IP адрес (для того чтобы привязать к определенному интерфейсу)
    третий – размер структуры sockaddr_in
    При удачном выполнении функция вернет 0
    Как видно это функция очень похожа connect.
    Также при ошибке можно воспользоваться функцией WSAGetLastError
    Самой распространенной ошибкой является WSAEADDRINUSE.
    Данная ошибка появляется, когда указанный порт уже занят другим приложением.

    2) listen – переводит сокет в режим ожидания входящих подключений.
    function listen(s: TSocket; backlog: Integer): Integer; stdcall;
    первый параметр – сокет
    второй – максимальная длинна очереди клиентов, которые запросили подключение, но еще небыли подключены.

    3) accept - извлекает из очереди ожидающих подключений первое, затем создает новый сокет и возвращает его дескриптор и некоторое описание клиента. Размер этой очереди как раз и устанавливается в функции listen.

    function accept( const s: TSocket; var addr: TSockAddr; var addrlen: Integer ): TSocket; stdcall;
    первый параметр – сокет
    второй – адрес структуры sockaddr_in куда будет помещена информация о клиенте.
    третий – размер структуры sockaddr_in
    При удачно завершении функция возвращает дескриптор сокета. При неудачно - INVALID_SOCKET

    В сокет, который возвращает accept, можно использовать для приёма и передачи информации.
    Как я уже говорил ранее в addr будет возвращена информация о клиенте. Нам будет важна только:
    addr.sin_port – порт с которого было подключение
    addr.sin_addr – ip клиента в двоичном формате. Для преобразования в строковой формат используется функция inet_ntoa. Т.е. ip:pchar; ip:=inet_ntoa(addr.sin_addr);

    т.е. основная структура будет выглядеть так:
    Code:
     WSAStartup($202, ws);
    lsocket:=socket(AF_INET, SOCK_STREAM, 0);
    laddr.sin_family:=AF_INET;
    laddr.sin_port:=htons(1080);
    laddr.sin_addr.s_addr:=INADDR_ANY; // для всех интерфейсов
    if bind(lsocket,@laddr, sizeof(laddr))<>0 then
     begin
      writeln('[-] Bind');
      exit;
     end;
    if listen(lsocket, $100)<>0 then // максимум очередь - 256 
     begin
      writeln('[-] Listen');
      exit;
     end;
    writeln('[+] Waiting connection');
    while true do // будем обрабатывать бесконечно все подключения
     begin
       csocket:=accept(lsocket,caddr,size_caddr);
       if csocket<> INVALID_SOCKET then 
         begin
           writeln('[+] Client connected. IP=',inet_ntoa(caddr.sin_addr));
           работаем с сокетом. Читаем и принимаем данные
           closesocket(csocket); // закрываем сокет
         end;
     end;
    end;
    
    Хотя на деле лучше всего конструкцию while true do заменить на
    while переменаня do чтобы можно было прерывать бесконечный цикл

    ПОЛУЧЕНИЕ ДАННЫХ
    Вот мы и добрались до приема данных с сокета.
    Это самое простое что может быть. Для получения данных используется функция
    recv. Функция может применять и для TCP и для UDP соединение. Но всё же желательно использовать её только для TCP
    function recv(s: TSocket; var Buf; len, flags: Integer): Integer; stdcall;
    первый параметр – сокет
    второй – адрес буфера куда будут закинуты данные
    третий – размер этого буфера.
    Flags – специально опции чтения. Нам они особо не важны, по этому будем ставить его = 0
    При неудаче функция возвращает значение SOCKET_ERROR
    Если функция вернула значение = 0 – это свидетельствует о том, что клиент/сервер разорвал соединение и дальнейшее чтении уже ненужно и требуется лишь закрыть сокет.
    Все остальные значение – это размер считанных данных. Но в любом случае это кол-во не может быть больше чем размер буфера указанный в len.


    Для чтение данные передаваемых по UDP протоколу используется функция recvfrom
    function recvfrom(s: TSocket; var Buf; len, flags: Integer; var from: TSockAddr; var fromlen: Integer): Integer; stdcall;
    Она ничем не отличается от recv за исключением последних двух параметров.
    from: TSockAddr – адрес структуры в которую будет помещена информация о том, кто прислал данные (PORT и IP).
    fromlen: Integer – размер этой структуры.

    ПОСЫЛКА ДАННЫХ
    Для посылке данных используется функция send и sendto.Подробно описывать данные функции я не буду, потому что они полностью аналогичны recv и recvfrom за одним 2-мя лишь исключениями:
    - функции возвращают кол-во посланных, а не принятых данных
    - в sendto в поле addrto (аналог from из recvfrom) указывается адрес и порт того компьютера кому предназначаются эти данные.

    P.S. Если вы клиент и посылаете данные по UDP протоколу, то следует учесть следующее:
    1) Вы можете не делать connect а сразу после создания сокета слать данные через sendto указывая в нем адрес того, кому предназначаются данные
    2) Если вы используете connect то для посылке данных можно использовать функцию send. И тогда ip и порт сервера будут браться из структуры, которая была передана функции connect

    МНОГОПОТОЧНОСТЬ КЛИЕНТА
    Организация многопоточной работы для отправки данных особо ничем не отличается об однопоточной работы, просто весь код работы с сетью оформляется в виде отдельной процедуры(за исключением функции WSAStartup потому как её нужно делать только 1 раз), которая запускается в потоке через функцию CreateThread. Единственное о чем хочется упомянуть так это только о том, что при работе с глобальными переменными желательно делать простейшую синхронизацию. Выглядеть это будет примерно так:
    Code:
    Wait:Boolean=false; // глобальная переменная
    Procedure threadproc(param:pointer);stdcall;
    Begin
    .-.-.-.-.-.-.-.-.
    While (wait) do sleep(1); // ждем если идет работа с глобальными переменными
    Wait:=true;// ставим флаг что начали работать с глобальными переменными
    Работаем с глобальными переменными
    Wait:=false; // разрешаем другим потокам работать с глобальными переменными
    .-.-.-.-.-.-.-.-.-.-
      ExitThread(0); // ОБРАТИТЕ ВНИМАНИЕ 1
    End;
    Запуск потоков
    Wait:=false;
    For x:=1 to кол-во_потоков do 
     Begin
      Thread[x]:=CreateThread(nil, 0, @ threadproc, param, 0, Thread[x]);
     End;
    
    Thread – это массив в который мы будем записывать дескрипторы созданных потоков, чтобы потом при необходимости можно было завершить их через TerminateThread
    Для более сложной синхронизации лучше применять критические секции.
    Также многопоточность может применять и при одном потоке работы с сетью. К примеру в графических приложениях работу с сетью выделить в отдельный поток, чтобы основная программа не зависала по время приёма и передачи данных по сети.

    МНОГОПОТОЧНОСТЬ СЕРВЕРА
    Показанный выше код серверной части одновременно может обрабатывать только 1 запрос клиента. А это очень сильно снижает скорость работы. Для того чтобы организовать многотопочную обработку клиентов необходимо после получения сокета от функции accept, передать его созданному потоку. Программно это будет выглядеть так:

    Code:
    
    procedure ThreadProc(sock:tsocket);stdcall;
    begin
      работа с сокетом (recv/send)
      closesocket(sock);
      ExitThread(0); // ОБРАТИТЕ ВНИМАНИЕ 1
    end;
    
    
    Var
     ThID:dword;
    while true do // будем обрабатывать бесконечно все подключения
     begin
       csocket:=accept(lsocket,caddr,size_caddr);
       if csocket<> INVALID_SOCKET then 
         begin
           writeln('[+] Client connected. IP=',inet_ntoa(caddr.sin_addr));
           ThID:=CreateThread(nil, 0, @ThreadProc, pointer(csocket), 0, ThID); 
           CloseHandle(ThID); // ОБРАТИТЕ ВНИМАНИЕ 2
         end;
     end;
    
    Как видно из кода, после получения дескриптора сокета мы запускаем поток, и передаем в виде параметра, этот дескриптор.

    ОБРАТИТЕ ВНИМАНИЕ 1 – поток должен быть завершен командой ExitThread(0) т.к. это является более корректным завершением.
    ОБРАТИТЕ ВНИМАНИЕ 2 – очень важная деталь, о котором многие забывают. После создания потока мы получаем дескриптор потока. Но даже при завершении потока через ExitThread, всё равно дескриптор остается открытым. Со временем это приводит в утечки памяти, а также разного рода тормозам, когда невозможно запустить новые потоки.
    Т.к. мы не собираемся завершать поток насильно, то нам этот дескриптор не нужен и по этому мы его сразу закроем через CloseHandle.

    Ну вот всё ))
    (С) SLESH 2009
     
    7 people like this.
  2. main()

    main() Banned

    Joined:
    10 Jun 2009
    Messages:
    7
    Likes Received:
    2
    Reputations:
    1
    Статья вобщем хорошая. Но растроило только то, что на дельфе писал,а не на сях
     
  3. slesh

    slesh Elder - Старейшина

    Joined:
    5 Mar 2007
    Messages:
    2,702
    Likes Received:
    1,224
    Reputations:
    455
    А разница какая?
    замени := на =
    = замени на ==
    <> на !=
    Ну и в некоторых местах поставить &
    Да и то, это только лишь синтаксис языка который невлияет на сам алгоритм и апифункции
     
  4. --StraNger--

    --StraNger-- Member

    Joined:
    4 Jan 2009
    Messages:
    63
    Likes Received:
    57
    Reputations:
    5
    Да действительно интересно.
    Не плагиат
     
  5. #Specan

    #Specan New Member

    Joined:
    16 Feb 2010
    Messages:
    13
    Likes Received:
    1
    Reputations:
    0
    slesh, подскажи ответ.
    Я сейчас разрабатываю одну интересную сетевую програмку.
    Суть такова.
    1) происходит соединение с серваком
    2) обмениваемся данными продолжительное время
    3) закрываем соединение (желательно мягко)


    Проблема в том что возможнно бональное откючение сети (не важно где, важно что возможно), или же сервак сам закроет соединение.
    Мне надо проверять соединён ли сокет на данный момент или нет.
    Ну вот допустим я коннектом законнектился, а через 10 минут соединение оборвали.
    Вот мне и надо проверять постоянно.
    Что можешь предложить?

    Вариант законектились, отправили, получили, отконнектились - не катит...
     
  6. slesh

    slesh Elder - Старейшина

    Joined:
    5 Mar 2007
    Messages:
    2,702
    Likes Received:
    1,224
    Reputations:
    455
    Если и сервак и клиент под виндой, то:
    1) в клиенте после удачного коннекта
    2) на серваке после приконнекчивания клиента

    нужно установить для сокета автопинг.
    Вот код на Си
    #define KA_TIME 30000 // ping time - 30 sec
    #define KAI_TIME 3000 // wait time
    struct tcp_keepalive alive = {1, KA_TIME, KAI_TIME}; // SOCKET Ping

    WSAIoctl(MainSock, SIO_KEEPALIVE_VALS, &alive, sizeof(alive), 0, 0, (LPDWORD)&len, 0, 0);

    т.е. смысл такой:
    Если на сокет не приходили никакие данные в течении KA_TIME миллисекунд, то посылается пустой тестовый пакет, ответ на который ждется KAI_TIME миллисекунды, если в течении этого времени ответ не пришел, то винда разрывает принудительно коннект. (хотя помойму она еще один раз попробует повторить операцию). После этого всего если ожидание шло через recv или select или тому подобные вещи, то они после дисконнекта вернут статус ошибки. После чего ты должен будеш закрыть сокет через closesocket. shutdown тут не подходит.


    Очень удобный механиз, спасает от любого типа глюков, включая аппаратный.
    т.е. сетевой кабель выдернул, сокет будет висеть как висел, а в даном случае после первогоже пинга произойдет дисконнект на стороне клиента.
    Если делать такое с обеих сторон, то отлично всё пашет.
     
    #6 slesh, 10 Jun 2010
    Last edited: 10 Jun 2010
  7. #Specan

    #Specan New Member

    Joined:
    16 Feb 2010
    Messages:
    13
    Likes Received:
    1
    Reputations:
    0
    А на делфе это возможно?
    Одноимённую функцию я видел, а константу не нашёл.
    Подскажи пример на делфе....
     
  8. slesh

    slesh Elder - Старейшина

    Joined:
    5 Mar 2007
    Messages:
    2,702
    Likes Received:
    1,224
    Reputations:
    455
    пережде чем задавать вопрос, в гугле бы глянул бы
    Code:
      IOC_VENDOR = $18000000;
      SIO_KEEPALIVE_VALS = DWORD(IOC_IN or IOC_VENDOR or 4);
    
    
    type
      tcp_keepalive = record
        onoff: u_long;
        keepalivetime: u_long;
        keepaliveinterval: u_long;
      end;
    
     
    1 person likes this.
  9. #Specan

    #Specan New Member

    Joined:
    16 Feb 2010
    Messages:
    13
    Likes Received:
    1
    Reputations:
    0
    Слушай, а если не сложно кинь ссылку на хороший мануал по сокетной теме.
    Со всеми делами, кодами ошибок, описаниями функций...
     
  10. slesh

    slesh Elder - Старейшина

    Joined:
    5 Mar 2007
    Messages:
    2,702
    Likes Received:
    1,224
    Reputations:
    455
    msdn
     
    1 person likes this.
  11. #Specan

    #Specan New Member

    Joined:
    16 Feb 2010
    Messages:
    13
    Likes Received:
    1
    Reputations:
    0
    А где там?
    Можно поконкретнее?
     
  12. intNet

    intNet Member

    Joined:
    31 May 2009
    Messages:
    29
    Likes Received:
    14
    Reputations:
    5
    http://msdn.microsoft.com/en-us/library/ms741416(VS.85).aspx
     
  13. #Specan

    #Specan New Member

    Joined:
    16 Feb 2010
    Messages:
    13
    Likes Received:
    1
    Reputations:
    0
    Спасибо.
     
  14. Neo][acK

    Neo][acK New Member

    Joined:
    27 Mar 2009
    Messages:
    3
    Likes Received:
    0
    Reputations:
    0
    А можно небольшой примерчик посылки get или post запроса через socks4 прокси, естественно юзая только winsock?
     
  15. Sc0rpi0n

    Sc0rpi0n Banned

    Joined:
    23 Feb 2010
    Messages:
    75
    Likes Received:
    22
    Reputations:
    16
    slesh, подскажи пожалуйсто.

    у меня прога может работать в двух режимах

    1) сервер <- клиент
    2) клиент -> сервер

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

    вот кусочек

    PHP:
      if connect(SocketAddrSizeof(Addr)) <> INVALID_SOCKET then
      begin
        ThID 
    := CreateThread(nil0, @ClientThreadPointer(Socket), 0ThID);
      
    end;
    PHP:
    procedure ClientThread(ClientSocketTSocket); stdcall;
    var
      
    Buffer: array[0..1024of Char;
    begin
        
    while True do
        
    begin
        recv
    ...
        
    send...
        
    ExitThread(0);
    end;
    пробывал туда и сюда ставить sleep(100) - не помогло(((

    мне надо организовать многопоточность клиента т.е. сеть в отдельный поток
     
  16. slesh

    slesh Elder - Старейшина

    Joined:
    5 Mar 2007
    Messages:
    2,702
    Likes Received:
    1,224
    Reputations:
    455
    Если поток у тебя не запускается, то возможно может ты connect не так проверял.
    как глосит msdn - If no error occurs, this function returns zero. If an error occurs, it returns SOCKET_ERROR
    Так что делай проверку
    Code:
    if connect(***) = 0 then
    begin
     CreateThread(****);
    end;
    
    Также функция потока имеет вид:
    function Name(***:****):dword; stdcall;
    А также Не советую завершать функцию потока через ExitThread потому что тогда могут быть проблемки с освобождением ресурсов. По любому система сама потом всё почистит за тобой после заверения функции.

    Больше сказать ничего не могу потому что по коду тут ничего не скажешь т.к. его мало.
     
  17. Sc0rpi0n

    Sc0rpi0n Banned

    Joined:
    23 Feb 2010
    Messages:
    75
    Likes Received:
    22
    Reputations:
    16
    спс. за ответ. клиент конектиться норм.

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

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

    PHP:
    procedure ClientThread(ClientSocketTSocket); stdcall;
    var
      
    Buffer: array[0..4096of Char;
      
    Outputstring;
    begin
        
    while True do
        
    begin
          recv
    (ClientSocketBufferSizeOf(Buffer), 0);
          .....
        
    closesocket(ClientSocket);
        
    ExitThread(0);
    end;

    т.е. пропусчен recv.
    кстати я в поток ставил sleep(5000) и ноль реации! ещё раз привожу код
    PHP:
    procedure ClientConnect();
    const
      
    Host '127.0.0.1';
      
    Port 83;
    var
      
    DestSocketTSocket;
      
    DestAddrTSockAddr;
      
    ThIDTHandle;
    begin
      DestSocket 
    := socket(AF_INETSOCK_STREAMIPPROTO_TCP);
      
    DestAddr.sin_family := AF_INET;
      
    DestAddr.sin_addr.S_addr := inet_addr(Host);
      
    DestAddr.sin_port := htons(Port);
      if 
    connect(DestSocketDestAddrSizeof(DestAddr)) = 0 then
      begin
        ThID 
    := CreateThread(nil0, @ClientThreadPointer(DestSocket), 0ThID);
      
    end;
    end;
    ПС slesh, у тя случайно не звалялся простой пример многопотчности клиент?
     
  18. slesh

    slesh Elder - Старейшина

    Joined:
    5 Mar 2007
    Messages:
    2,702
    Likes Received:
    1,224
    Reputations:
    455
    WSAStartup делал?
    recv что возвращает? И что дает WSAGetlastError после recv. Какие советы юзаешь Winsock 1 или 2 ?
     
  19. Sc0rpi0n

    Sc0rpi0n Banned

    Joined:
    23 Feb 2010
    Messages:
    75
    Likes Received:
    22
    Reputations:
    16
    WSAStartup делал конечно. Версия 1.

    WSAGetLastError ничего не возвращет.(((
     
  20. slesh

    slesh Elder - Старейшина

    Joined:
    5 Mar 2007
    Messages:
    2,702
    Likes Received:
    1,224
    Reputations:
    455
    всмысле ничего не возвращает? такого не бывает. или она ноль у тебя возвращает?