Top.Mail.Ru
Мастера DELPHI, Delphi programming community Титульная страница Поиск, карта сайта Написать письмо 
| Новости | Поиск | FAQ | Статьи | Книги | Новости VCL |
| Форумы ЧАТ | Орешник | KOL и MCK |
Форумы -» Основная («Начинающим»)/ Базы / WinAPI / Компоненты / Сети / Media / Игры / Corba и COM / KOL / FreePascal / .Net / Прочее / RSDN.ORG

« предыдущая ветвь | форум | следующая ветвь »

Работа с сокетами (теория) [D7]


Страницы:   1 2

GanibalLector ©   (2009-07-02 16:51:00) 

Добрый день!

Имеется следующая задача. Есть девайсы, к ним подключены модемы.
В данный момент я дозваниваюсь к ним и обмениваюсь данными(через 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. Ну...это условно. Я понимаю, что этим конечно дело не закончится.

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

Заранее спасибо !!!


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


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

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


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


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


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


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


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


GanibalLector   (2009-07-06 10:45:00) [3]


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


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


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


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


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


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


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


GanibalLector   (2009-07-05 20:44:00) [5]


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


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


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


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


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

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


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


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


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

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


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


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


GanibalLector   (2009-07-03 18:59:00) [7]


> И еще. Ранее я использовал 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мс и иду дальше. Далее спустя какое-то время клиент посылает пакет и первый байт этого пакета я теряю.

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


GanibalLector   (2009-07-03 17:49:00) [8]

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

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

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


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

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


GanibalLector   (2009-07-03 17:39:00) [10]


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


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

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


GanibalLector   (2009-07-03 17:25:00) [11]

Попробовал писать через 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;


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


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

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


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

2 Вариант

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


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


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


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

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


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


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


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

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


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


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

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

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


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


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


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

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


Вариант   (2009-07-03 09:34:00) [18]


> Сергей М. ©   (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 08:14:00) [19]


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


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


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


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


> GanibalLector ©   (02.07.09 16:51)


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

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


Страницы:   1 2

Наверх

Титульная страница Поиск, карта сайта Написать письмо