Главная страница
Top.Mail.Ru    Яндекс.Метрика
Текущий архив: 2011.12.04;
Скачать: CL | DM;

Вниз

Работа с сокетами (теория)   Найти похожие ветки 

 
GanibalLector ©   (2009-07-02 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 ©   (2009-07-02 16:54) [1]

Небольшое уточнение.

Удаленные клиенты (в моем случаи) ничего сами по себе данные не передают. Т.е. я обязан выслать пакет и только после этого клиент ответит.


 
Сергей М. ©   (2009-07-02 17:09) [2]


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


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


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


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


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


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

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

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


 
GanibalLector ©   (2009-07-02 17:23) [3]


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


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

Вот что пугает !


 
Сергей М. ©   (2009-07-02 20:05) [4]

Страхи свои обрати к Антону, ибо жив и уважаем)


 
Сергей М. ©   (2009-07-02 20:08) [5]


> GanibalLector


Впрочем, это к слову о минимальной переделке твоего кода, а не переписи его с нуля


 
Вариант   (2009-07-03 07:20) [6]


> GanibalLector ©   (02.07.09 16:51)


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

Если тебе не трудно создать отдельный поток для сокетного транспорта, то для клиента  проще код на мой взгляд для блокирующих сокетов, да и возможностей для ошибок меньше в программе. В этом случае  ReadFile и WriteFile правда прийдется поменять на recv и send.


 
Сергей М. ©   (2009-07-03 08:14) [7]


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


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


С чего бы вдруг ?


 
Вариант   (2009-07-03 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?
Было бы интересно посмотреть.


 
Сергей М. ©   (2009-07-03 10:22) [9]


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


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

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


 
Вариант   (2009-07-03 10:36) [10]


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

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

Я использовал просто блокирующий сокет не в overlapped режиме.
Поэтому в этом случае WriteFile работать не будет, собственно я имел ввиду это.  Когда писал [6]


 
Сергей М. ©   (2009-07-03 10:48) [11]


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


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

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


 
Вариант   (2009-07-03 10:53) [12]


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

Я не утверждал, что оно не будет работать вообще


 
GanibalLector ©   (2009-07-03 14:11) [13]

2 Вариант

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


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


 
Вариант   (2009-07-03 15:09) [14]


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

Согласен.
Если количество одновременно работающих клиентов велико ( то есть у тебя сервер, и ты работаешь с клиентами) или может вырасти в дальнейшем, то блокирующий не overlapped сокет не самое лучшее решение. Причем не лучшее решение именно из-за числа одновременно работающих потоков, мне кажется.
Посмотри книгу
"Программирование в сетях Microsoft Windows" Э.Джонс, Д.Оланд. Довольно подробно рассмотрены разные варианты работы с сокетами.


 
GanibalLector ©   (2009-07-03 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 ©   (2009-07-03 17:39) [16]


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


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

Это так...мысли в слух.


 
Polevi ©   (2009-07-03 17:46) [17]

для нагруженных систем лучше Completion Ports использовать, я могу пример дать если интересно


 
GanibalLector ©   (2009-07-03 17:49) [18]

2 Polevi ©   (03.07.09 17:46) [17]

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

И еще вопрос сразу. У меня ситуация такая, что я сперва должен что-то отправить, а потом устройство ответит. А во всех серверах наоборот...они ждут. а потом отвечают.


 
GanibalLector ©   (2009-07-03 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мс и иду дальше. Далее спустя какое-то время клиент посылает пакет и первый байт этого пакета я теряю.

Видимо это связано с тем, что я начал чтение одного байта, но оно было прервано .


 
Сергей М. ©   (2009-07-04 15:12) [20]


> во всех серверах наоборот...они ждут. а потом отвечают


Чего ждут-то ? Запроса ? Так и у тебя та же ситуация - ты отправляешь запрос, и устройство на этот запрос отвечает ..

И с чего такая уверенность, что "во всех" ?


> после коннекта принимаю строку(но ее может и не быть)


Как это так ?
Ты же утверждаешь, что дивайс сам по себе ничего не присылает и молчит до тех пор пока у него что-то не спросят ?


 
GanibalLector ©   (2009-07-05 20:44) [21]


> Как это так ?Ты же утверждаешь, что дивайс сам по себе ничего
> не присылает и молчит до тех пор пока у него что-то не спросят
> ?


Да, все верно. Девайс - нет. Модем - да. При коннекте модем выдает что-то типа "Bridge connect". Вот и получается, что мне приходится ждать около секунды(ждать приветствие модема),а только потом посылать девайсу данные.


> Чего ждут-то ? Запроса ? Так и у тебя та же ситуация - ты
> отправляешь запрос, и устройство на этот запрос отвечает
> ..И с чего такая уверенность, что "во всех" ?


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


В общем, отказался от ReadFile\WriteFile. Использую (блокирующий режим) Send\Recv совместно с select и ioctlsocket.

Т.е. в отдельном потоке ожидаю события через select . Далее вызываю  ioctlsocket, чтобы узнать сколько байт пришло. Ну и читаю или пишу.
Вроде работает. Напряг с кол-вом клиентов (не могу я столько потоков запускать).


 
Сергей М. ©   (2009-07-06 09:02) [22]


> GanibalLector ©   (05.07.09 20:44) [21]


А ты уверен, что модем отныне всегда будет работать в режиме моста ?
Ситуация с переводом модема в режим роутера исключена ?


> Напряг с кол-вом клиентов (не могу я столько потоков запускать)


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


 
GanibalLector ©   (2009-07-06 10:45) [23]


> Ситуация с переводом модема в режим роутера исключена ?


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


 
Сергей М. ©   (2009-07-06 12:23) [24]


> разве можно модем перевести в режим роутера ?


Если он поддерживает такую функциональность, то почему нет ?
Например, многие dsl-модемы даже бюджетного класса могут работать и в режиме моста и в режиме маршрутизатора.
За GPRS-модемы не скажу, попросту никогда не интересовался за ненадобностью, полюбопытствуй сам.


> что это, кстати, даст ?


В режиме роутера модем выступает в качестве маршрутизатора, выполняя при этом основные функции, которые в случае с режимом моста выполняет компьютер, являющийся пограничным шлюзом между ЛВС и внешними сетями, доступ к которым осуществляется через модемный мост.


 
Похмелкин   (2009-07-06 20:51) [25]


> Напряг с кол-вом клиентов (не могу я столько потоков запускать).

Посмотрите это
http://www.torry.net/vcl/internet/sockets/HPScktSrvr.zip



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

Текущий архив: 2011.12.04;
Скачать: CL | DM;

Наверх




Память: 0.55 MB
Время: 0.009 c
2-1313569604
Pepe
2011-08-17 12:26
2011.12.04
Обратный алгоритм.


15-1312895553
Медвежонок Пятачок
2011-08-09 17:12
2011.12.04
Внимание здешним телепатам. Есть работа


2-1313607461
Фотошопп2017
2011-08-17 22:57
2011.12.04
Фотошоп+Делфи???


15-1313145936
ProgRAMmer Dimonych
2011-08-12 14:45
2011.12.04
Дизассемблер: переводить или искать аналог?


3-1268214255
Spot
2010-03-10 12:44
2011.12.04
Резервирование БД mysql