Конференция "Сети" » таймаут сокета
 
  • tippa © (09.06.10 16:12) [0]
    Добрый день.
    var info:TWSADATA;
       sock:TSocket;
       addr:sockaddr_in;
    begin
    WSAStartup(MAKEWORD(2,0),info);
    sock:=socket(AF_INET, SOCK_STREAM, 0);
    addr.sin_family:=AF_INET;
    addr.sin_addr.S_addr:=inet_addr('151.151.151.151');
    addr.sin_port:=htons(80);
    if connect(sock,@addr,sizeof(addr))=0 then showmessage('Yes')
                                         else showmessage('No');
    end;


    Код проверяет открыт ли порт на удаленной машине. Если адрес не доступен - то функция connect подвисает секунд на 20. Нужно указать таймаут скажем на 5 секунд. Вот нашел код
    var
    timeout:TTimeVal;
    begin
    timeout.tv_usec:=0;
    timeout.tv_sec:=10000; // время задается в миллисекундах. 10000 = 10 секунд
    // установим для сокета sock время ожидания чтения данных = 10 секунд.
    setsockopt(sock, SOL_SOCKET, SO_RCVTIMEO, @timeout, sizeof(ttimeval));


    собственно та же картина, чего-то не хватает.
    Хотелось бы самое простое решение, без перевода сокета в неблокирующий режим - если это возможно конечно.
  • tippa © (09.06.10 18:48) [1]
    о как
    var info:TWSADATA;
       sock:TSocket;
       timeout:TTimeVal;
       addr:sockaddr_in;
       fds:TFDSet;
       rc:integer;
       block:bool;
    begin
    WSAStartup(MAKEWORD(2,0),info);
    sock:=socket(AF_INET, SOCK_STREAM, 0);
    addr.sin_family:=AF_INET;
    addr.sin_addr.S_addr:=inet_addr('151.151.151.151');
    addr.sin_port:=htons(80);
    block:=true;
    ioctlsocket(sock, FIONBIO, cardinal(block));
    connect(sock,@addr,sizeof(addr));
         FD_ZERO(fds)
         FD_SET(sock,fds);
         timeout.tv_sec:=1;
         timeout.tv_usec:=0;
         rc:=select(0, nil, @fds, nil, @timeout);
    block:=false;
    ioctlsocket(sock, FIONBIO, cardinal(block));
    closesocket(sock);
    if rc<>0 showmessage('Yes') else showmessage('No');
    end;

  • DVM © (09.06.10 20:51) [2]

    > tippa ©   (09.06.10 18:48) [1]

    а кто будет анализировать коды возврата функций
  • tippa © (10.06.10 06:59) [3]
    var info:TWSADATA;
      sock:TSocket;
      timeout:TTimeVal;
      addr:sockaddr_in;
      fds:TFDSet;
      rc:integer;
      block:bool;
    begin
    if WSAStartup(MAKEWORD(2,0),info)<>0 then
    begin
    showmessage('Error');
    exit;
    end;
    sock:=socket(AF_INET, SOCK_STREAM, 0);
    if sock=INVALID_SOCKET then
    begin
    showmessage('Error');
    exit;
    end;
    addr.sin_family:=AF_INET;
    addr.sin_addr.S_addr:=inet_addr('151.151.151.151');
    addr.sin_port:=htons(80);
    block:=true;
    if ioctlsocket(sock, FIONBIO, cardinal(block))<>0 then
    begin
    showmessage('Error');
    exit;
    end;
    if connect(sock,@addr,sizeof(addr))<>0 then
    begin
    showmessage('Error');
    exit;
    end;
        FD_ZERO(fds)
        FD_SET(sock,fds);
        timeout.tv_sec:=1;
        timeout.tv_usec:=0;
        rc:=select(0, nil, @fds, nil, @timeout);
    block:=false;
    if ioctlsocket(sock, FIONBIO, cardinal(block))<>0 then
    begin
    showmessage('Error');
    exit;
    end;
    closesocket(sock);
    if rc<>0 showmessage('Yes') else showmessage('No');
    end;


    так потянет?
    и обязательно ли переводить сокет обратно в блокирующий режим, ведь все равно следующей строкой мы его закрываем?
  • tippa © (10.06.10 08:27) [4]
    а заместо
    if connect(sock,@addr,sizeof(addr))<>0 then


    оказалось надо
    if connect(sock,@addr,sizeof(addr))=0 then

  • DVM © (10.06.10 10:32) [5]

    > tippa ©   (10.06.10 06:59) [3]


    > и обязательно ли переводить сокет обратно в блокирующий
    > режим

    необязательно

    Кроме этого select может возвращать 0, 1 и SOCKET_ERROR, ты же проверяешь только на неравенство 0, на вдруг SOCKET_ERROR. Затем, если select вернула 1, это еще не означает, что подключились, надо проверить не пустое ли множество fds. Еще желательно завести множество efd - оно должно быть пустое при успешном подключении: select(0, nil, @wfd, @efd, @TimeVal)

                 case select(0, nil, @wfd, @efd, @TimeVal) of
                   0: еще пока не подключились;
                   1: if FD_ISSET(Result, wfd) then
                        вот тут подключились
                      else
                        if FD_ISSET(Result, efd) then
                          begin
                            а вот тут ошибка
                            exit;
                          end;
                   SOCKET_ERROR:
                     begin
                       и тут ошибка
                       exit
                     end;

  • tippa © (10.06.10 13:30) [6]
    спасибо, вот еще
    if connect(sock,@addr,sizeof(addr))<>0 then
    begin
    showmessage('Error');
    exit;
    end;


    так как мы только что перевели сокет в неблокирующий режим, то connect всегда будет давать ошибку. Если эта ошибка WSAEWOULDBLOCK то сокет перешел в неблокирующий режим и все хорошо.
    Вообщем как-то так.
    var info:TWSADATA;
     sock:TSocket;
     timeout:TTimeVal;
     addr:sockaddr_in;
     fds,efd:TFDSet;
     rc:integer;
     block:bool;
    begin
    if WSAStartup(MAKEWORD(2,0),info)<>0 then exit;
    sock:=socket(AF_INET, SOCK_STREAM, 0);
    if sock=INVALID_SOCKET then exit;
    addr.sin_family:=AF_INET;
    addr.sin_addr.S_addr:=inet_addr('151.151.151.151');
    addr.sin_port:=htons(80);
    block:=true;
    if ioctlsocket(sock, FIONBIO, cardinal(block))<>0 then exit;
    connect(sock,@addr,sizeof(addr));
    if WSAGetLastError<>WSAEWOULDBLOCK then exit;
       FD_ZERO(fds)
       FD_SET(sock,fds);
       timeout.tv_sec:=1;
       timeout.tv_usec:=0;
    case select(0, nil, @fds, @efd, @TimeVal) of
    0: exit;
    1: if FD_ISSET(sock, fds) then showmessage('Yes');
       else
       if FD_ISSET(sock, efd) then exit;
    SOCKET_ERROR: exit;
    closesocket(sock);
    end;


    еще замечния?
  • DVM © (10.06.10 22:56) [7]

    > так как мы только что перевели сокет в неблокирующий режим,
    >  то connect всегда будет давать ошибку.

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


    > еще замечния?

    closesocket(sock); не будет вызываться в ряде случаев, а надо бы всегда, если вызов socket() был успешен. Try...Finally например добавь
 
Конференция "Сети" » таймаут сокета
Есть новые Нет новых   [134436   +26][b:0][p:0.003]