Конференция "Сети" » Работа с сокетами (теория) [D7]
 
  • GanibalLector © (02.07.09 16:51) [0]
    Добрый день!

    Имеется следующая задача. Есть девайсы, к ним подключены модемы.
    В данный момент я дозваниваюсь к ним и обмениваюсь данными(через TAPI).
    Все хорошо, все работает. Решили удешевить стоимость обмена , увеличить скорость обмена и перейти на GPRS.
    Модемы прошили ( на работу с GPRS ), проверил все работает.

    В связи с этим пару вопросов.

    1) начитался я А.Б.Григорьева и не пойму. а какие сокеты выбрать ?
    Блокирующие/не блокирующие (асинхронные на сообщениях,на событиях и т.д.)

    Впервые работаю с сокетами.

    2) есть готовый код, который работает. Как Вы понимаете, обмен у меня был по RS232.
    Протокол обмена с девайсом не менялся. Я хочу максимально упростить переход с RS232 на сокеты(ибо кода много уже).

    Т.е. в моем коде весь обмен сводится к одной ф-ции,  в которой есть ReadFile и WriteFile(для работы с RS232).
    Вот ,например  :

    procedure Request(Busy,NoWrite:Boolean; HPort: THandle;
     AddrPC, Addr, Cmd: Byte; const Data: string;
     out Status: TStatusRec; out Reply: string); overload;
    var
    ...
    begin
     if not Busy then
      Num := Byte(InterlockedIncrement(ncmd)) else
      Num:=  Byte(ncmd);
     Reply := '';
     Databuffer := FormatBuffer(AddrPC, Addr, Num, Cmd, Data);
     evOverlapped := CreateEvent(nil, True, True, nil);
     try
       if not WinCheck(SetCommMask(HPort, EV_RXCHAR or EV_ERR), Status) then Exit;

       if not NoWrite then
       begin
         ZeroMemory(@Overlapped, SizeOf(Overlapped));
         Overlapped.hEvent := evOverlapped;
         if not WinCheckOvr(WriteFile(HPort, Databuffer[1], Length(Databuffer),
           Transferred, @Overlapped), Status) then Exit;
         if not Wait(WriteTimeout, evOverlapped, Status) then Exit;
         if not WinCheck(GetOverlappedResult(HPort, Overlapped,
           Transferred, False), Status) then Exit;
       end;

       ...

     finally
       CloseHandle(evOverlapped);
     end;
    end;



    Т.е. я открываю порт и передаю в эту ф-цию хендл. А сама ф-ция что-то передает/принимает.

    Что я хочу. Создам  сервер. Подключу клиента. В эту ф-цию передам сокет подключившегося клиента и изменю ReadFile|WriteFile на
    на Recv , Send. Ну...это условно. Я понимаю, что этим конечно дело не закончится.

    Правильный ли ход моих мыслей ?

    Заранее спасибо !!!
  • GanibalLector © (02.07.09 16:54) [1]
    Небольшое уточнение.

    Удаленные клиенты (в моем случаи) ничего сами по себе данные не передают. Т.е. я обязан выслать пакет и только после этого клиент ответит.
  • Сергей М. © (02.07.09 17:09) [2]

    > В эту ф-цию передам сокет подключившегося клиента и изменю
    > ReadFile|WriteFile на
    > на Recv , Send


    Вовсе не обязательно  изменять.
    ReadFile|WriteFile успешно работают и с сокетным транспортом, т.е. в эти ф-ции допустимо передавать параметром хэндл сокета.


    > понимаю, что этим конечно дело не закончится


    Это да.
    Поработать напильником придется изрядно)


    > не пойму. а какие сокеты выбрать ?


    Да любые)
    Все равно напильником работать придется.

    Блокирующий режим проще в алгоритмической реализации сокетного транспорта, но он блокирует кодовый поток на время выполнения ф-ций сокетного ввода/вывода.

    Неблокирующий режим чуть посложнее в реализации, но он не блокирует кодовый поток.
  • GanibalLector © (02.07.09 17:23) [3]

    > Вовсе не обязательно  изменять.ReadFile|WriteFile успешно
    > работают и с сокетным транспортом, т.е. в эти ф-ции допустимо
    > передавать параметром хэндл сокета.


    Да, но тот же А.Б.Григорьев говорит следующее : специальных ф-ций для перекрытого ввода-вывода в WinSock1 не было, требовались ReadFile и WriteFile. В WinSock2 появилась полноценная поддержка перекрытого ввода-вывода для всех версий Windows...Здесь мы  будем рассматривать перекрытый ввод-вывод только в спецификации WinSock2, т.к. старый вариант из-за своих ограничений уже не имеет практического смысла

    Вот что пугает !
  • Сергей М. © (02.07.09 20:05) [4]
    Страхи свои обрати к Антону, ибо жив и уважаем)
  • Сергей М. © (02.07.09 20:08) [5]

    > GanibalLector


    Впрочем, это к слову о минимальной переделке твоего кода, а не переписи его с нуля
  • Вариант (03.07.09 07:20) [6]

    > GanibalLector ©   (02.07.09 16:51)


    Мое мнение и не более:

    Если тебе не трудно создать отдельный поток для сокетного транспорта, то для клиента  проще код на мой взгляд для блокирующих сокетов, да и возможностей для ошибок меньше в программе. В этом случае  ReadFile и WriteFile правда прийдется поменять на recv и send.
  • Сергей М. © (03.07.09 08:14) [7]

    > Вариант   (03.07.09 07:20) [6]


    > В этом случае  ReadFile и WriteFile правда прийдется поменять
    > на recv и send.


    С чего бы вдруг ?
  • Вариант (03.07.09 09:34) [8]

    > Сергей М. ©   (03.07.09 08:14) [7]


    С собственного опыта.

    В свое время, прочитал в MSDN для WriteFile

    > hFile
    > [in] Handle to the file. The file handle must have been
    > created with the GENERIC_WRITE access right. For more information,
    >  see File Security and Access Rights.
    > For asynchronous write operations, hFile can be any
    > handle opened with the FILE_FLAG_OVERLAPPED flag by the
    > CreateFile function, or a socket handle returned by the
    > socket or accept function.


    Не понравилась мне выноска (жирным выделил) в асинхронные  операции сокетов.  Проверил кодом для блокирующих сокетов (сокет получен функцией accept) для Winsock 1.1 и/или Winsock 2.2, не работает WriteFile, возвращает false.  GetLastError и WsaGetLastError вернули ошибку "Параметр задан не верно". В качестве handle для WriteFile  пробовал передавать как сам сокет, так и handle полученный DuplicateHandle. А вот send работает с тем же сокетом или дублированным handle.

    Код для WriteFile


    > Res := WriteFile(Soc, s[1], j, cardinal(j), 0);
    >                   if not Res then
    >                     begin
    >                       s1 := SysErrorMessage(WSAGetLastError);// GetlastError тоже
    >                    
    >                     end;


    Где Soc -сокет полученный accept, s[1] -строка, j - длина строки, Res - bool

    ПРоверялось в Windows XP на D6.

    Я допускаю что мог ошибиться или чего-то еще не знать. Может ты делал такие операции с блокирующим сокетом? Если есть, то может покажешь код, где для блокирующих сокетов WriteFile работает в Winsock 1.1 или Winsock 2.2?
    Было бы интересно посмотреть.
  • Сергей М. © (03.07.09 10:22) [9]

    > GetLastError и WsaGetLastError вернули ошибку "Параметр
    > задан не верно"


    А с чего ты взял, что ошибка относится к 1-му параметру ?

    Она отнюдь не к первому относится, а к последнему : ты передал туда 0 (nil), а следовало передать указатель на структуру TOverlapped, ибо overlapped-режим ввода/вывода в этом случае обязателен
  • Вариант (03.07.09 10:36) [10]

    > Сергей М. ©   (03.07.09 10:22) [9]

    Согласен, возможно и так.

    Я использовал просто блокирующий сокет не в overlapped режиме.
    Поэтому в этом случае WriteFile работать не будет, собственно я имел ввиду это.  Когда писал [6]
  • Сергей М. © (03.07.09 10:48) [11]

    > Вариант   (03.07.09 10:36) [10]


    Так что будет оно работать, куда оно денется)

    Но, конечно же, применять ли файлопоточные ф-ции ввода-вывода или пользовать [WSA]Send/Recv - этот выбор должен делаться исходя из конкретной ситуации и условий.
  • Вариант (03.07.09 10:53) [12]

    > Сергей М. ©   (03.07.09 10:48) [11]

    Я не утверждал, что оно не будет работать вообще
  • GanibalLector © (03.07.09 14:11) [13]
    2 Вариант

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


    Да...пока так и сделал. Что-то даже начало работать(делал с recv и send ).
    Но !!! Клиентов много. Каждому клиенту отдать поток не могу. Тут прийдется или пул делать или выбирать неблокирующие. Даже не знаю..
  • Вариант (03.07.09 15:09) [14]

    > GanibalLector ©   (03.07.09 14:11) [13]

    Согласен.
    Если количество одновременно работающих клиентов велико ( то есть у тебя сервер, и ты работаешь с клиентами) или может вырасти в дальнейшем, то блокирующий не overlapped сокет не самое лучшее решение. Причем не лучшее решение именно из-за числа одновременно работающих потоков, мне кажется.
    Посмотри книгу
    "Программирование в сетях Microsoft Windows" Э.Джонс, Д.Оланд. Довольно подробно рассмотрены разные варианты работы с сокетами.
  • GanibalLector © (03.07.09 17:25) [15]
    Попробовал писать через WriteFile. Работает. Ниже код.
    Не понятно с чтением. Для порта я флаги ставил...а тут я так понимаю они не нужны теперь.

    function WinCheck(Val: BOOL;  AllowOvr: Boolean= False): BOOL;
    begin
     if not Val then
     begin
       if AllowOvr and (GetLastError = ERROR_IO_PENDING) then
       begin
         Result := True;
         Exit;
       end;
     end;
     Result := Val;
    end;

    function WinCheckOvr(Val: BOOL): BOOL;
    begin
     Result := WinCheck(Val, True);
    end;

    function Wait(Timeout: DWORD; hData: THandle): Boolean;
     var W:DWORD;
    begin
     Result:=False;
     W:=WaitForSingleObject(hData,Timeout);
     case W of
       WAIT_OBJECT_0 : Result:=True;
       WAIT_TIMEOUT  : Result:=False;
     end;
    end;

    Str:='Hello,World';
    evOverlapped := CreateEvent(nil, True, True, nil);
     try
       ZeroMemory(@Overlapped, SizeOf(Overlapped));
       Overlapped.hEvent := evOverlapped;
       //
       if not WinCheckOvr(WriteFile(FSocket,Str[1],Length(Str),J,@Overlapped)) then Exit;
       if not Wait(500,evOverlapped) then Exit;
       if not WinCheck(GetOverlappedResult(FSocket, Overlapped,J, False)) then Exit;
       //
     finally
       CloseHandle(evOverlapped);
     end;

  • GanibalLector © (03.07.09 17:39) [16]

    > Не понятно с чтением. Для порта я флаги ставил...а тут я
    > так понимаю они не нужны теперь.


    И еще. Ранее я использовал ClearCommError чтобы взять кол-во пришедших байт в порт. А сейчас как ? По одному байту что-ли читать ?
    Могу конечно IOctlSocket с флагом FIONREAD но уместно ли это ?

    Это так...мысли в слух.
  • Polevi © (03.07.09 17:46) [17]
    для нагруженных систем лучше Completion Ports использовать, я могу пример дать если интересно
  • GanibalLector © (03.07.09 17:49) [18]
    2 Polevi ©   (03.07.09 17:46) [17]

    Да, очень.
    Сюда , если можно : Talla2kDOGukr.net

    И еще вопрос сразу. У меня ситуация такая, что я сперва должен что-то отправить, а потом устройство ответит. А во всех серверах наоборот...они ждут. а потом отвечают.
  • GanibalLector © (03.07.09 18:59) [19]

    > И еще. Ранее я использовал ClearCommError чтобы взять кол-
    > во пришедших байт в порт. А сейчас как ? По одному байту
    > что-ли читать ?


    Таки да. По одному все получилось.
    Вот...если кому интересно :

    evOverlapped := CreateEvent(nil, True, True, nil);
     try
       repeat
         //
         J:=1;
         SetLength(Str, J);
         ZeroMemory(@Overlapped, SizeOf(Overlapped));
         Overlapped.hEvent := evOverlapped;
         if not WinCheckOvr(ReadFile(FSocket, PChar(Str)^, J,J, @Overlapped)) then Break;
         if not Wait(2500, evOverlapped) then Break;
         if not WinCheck(GetOverlappedResult(FSocket, Overlapped,J, False)) then Break;
         //
         LogMessage('принял байт '+Str+' '+IntToHex(Ord(Str[1]),2));
       until False;

     finally
       CloseHandle(evOverlapped);
     end;



    Правда один байт теряется. Странно, но разберусь.
    Т.е. после коннекта принимаю строку(но ее может и не быть).В общем принимаю или жду 2500мс и иду дальше. Далее спустя какое-то время клиент посылает пакет и первый байт этого пакета я теряю.

    Видимо это связано с тем, что я начал чтение одного байта, но оно было прервано .
 
Конференция "Сети" » Работа с сокетами (теория) [D7]
Есть новые Нет новых   [134435   +33][b:0][p:0.003]