-
Добрый день! Имеется следующая задача. Есть девайсы, к ним подключены модемы. В данный момент я дозваниваюсь к ним и обмениваюсь данными(через 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. Ну...это условно. Я понимаю, что этим конечно дело не закончится. Правильный ли ход моих мыслей ? Заранее спасибо !!!
-
Небольшое уточнение. Удаленные клиенты (в моем случаи) ничего сами по себе данные не передают. Т.е. я обязан выслать пакет и только после этого клиент ответит.
-
> В эту ф-цию передам сокет подключившегося клиента и изменю > ReadFile|WriteFile на > на Recv , Send
Вовсе не обязательно изменять. ReadFile|WriteFile успешно работают и с сокетным транспортом, т.е. в эти ф-ции допустимо передавать параметром хэндл сокета.
> понимаю, что этим конечно дело не закончится
Это да. Поработать напильником придется изрядно)
> не пойму. а какие сокеты выбрать ?
Да любые) Все равно напильником работать придется.
Блокирующий режим проще в алгоритмической реализации сокетного транспорта, но он блокирует кодовый поток на время выполнения ф-ций сокетного ввода/вывода.
Неблокирующий режим чуть посложнее в реализации, но он не блокирует кодовый поток.
-
> Вовсе не обязательно изменять.ReadFile|WriteFile успешно > работают и с сокетным транспортом, т.е. в эти ф-ции допустимо > передавать параметром хэндл сокета.
Да, но тот же А.Б.Григорьев говорит следующее : специальных ф-ций для перекрытого ввода-вывода в WinSock1 не было, требовались ReadFile и WriteFile. В WinSock2 появилась полноценная поддержка перекрытого ввода-вывода для всех версий Windows...Здесь мы будем рассматривать перекрытый ввод-вывод только в спецификации WinSock2, т.к. старый вариант из-за своих ограничений уже не имеет практического смысла Вот что пугает !
-
Страхи свои обрати к Антону, ибо жив и уважаем)
-
> GanibalLector
Впрочем, это к слову о минимальной переделке твоего кода, а не переписи его с нуля
-
> GanibalLector © (02.07.09 16:51)
Мое мнение и не более:
Если тебе не трудно создать отдельный поток для сокетного транспорта, то для клиента проще код на мой взгляд для блокирующих сокетов, да и возможностей для ошибок меньше в программе. В этом случае ReadFile и WriteFile правда прийдется поменять на recv и send.
-
> Вариант (03.07.09 07:20) [6]
> В этом случае ReadFile и WriteFile правда прийдется поменять > на recv и send.
С чего бы вдруг ?
-
> Сергей М. © (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? Было бы интересно посмотреть.
-
> GetLastError и WsaGetLastError вернули ошибку "Параметр > задан не верно"
А с чего ты взял, что ошибка относится к 1-му параметру ?
Она отнюдь не к первому относится, а к последнему : ты передал туда 0 (nil), а следовало передать указатель на структуру TOverlapped, ибо overlapped-режим ввода/вывода в этом случае обязателен
-
> Сергей М. © (03.07.09 10:22) [9]
Согласен, возможно и так.
Я использовал просто блокирующий сокет не в overlapped режиме. Поэтому в этом случае WriteFile работать не будет, собственно я имел ввиду это. Когда писал [6]
-
> Вариант (03.07.09 10:36) [10]
Так что будет оно работать, куда оно денется)
Но, конечно же, применять ли файлопоточные ф-ции ввода-вывода или пользовать [WSA]Send/Recv - этот выбор должен делаться исходя из конкретной ситуации и условий.
-
> Сергей М. © (03.07.09 10:48) [11]
Я не утверждал, что оно не будет работать вообще
-
2 Вариант
> Если тебе не трудно создать отдельный поток для сокетного > транспорта, то для клиента проще код на мой взгляд для > блокирующих сокетов, да и возможностей для ошибок меньше > в программе.
Да...пока так и сделал. Что-то даже начало работать(делал с recv и send ). Но !!! Клиентов много. Каждому клиенту отдать поток не могу. Тут прийдется или пул делать или выбирать неблокирующие. Даже не знаю..
-
> GanibalLector © (03.07.09 14:11) [13]
Согласен. Если количество одновременно работающих клиентов велико ( то есть у тебя сервер, и ты работаешь с клиентами) или может вырасти в дальнейшем, то блокирующий не overlapped сокет не самое лучшее решение. Причем не лучшее решение именно из-за числа одновременно работающих потоков, мне кажется. Посмотри книгу "Программирование в сетях Microsoft Windows" Э.Джонс, Д.Оланд. Довольно подробно рассмотрены разные варианты работы с сокетами.
-
Попробовал писать через 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;
-
> Не понятно с чтением. Для порта я флаги ставил...а тут я > так понимаю они не нужны теперь.
И еще. Ранее я использовал ClearCommError чтобы взять кол-во пришедших байт в порт. А сейчас как ? По одному байту что-ли читать ? Могу конечно IOctlSocket с флагом FIONREAD но уместно ли это ? Это так...мысли в слух.
-
для нагруженных систем лучше Completion Ports использовать, я могу пример дать если интересно
-
2 Polevi © (03.07.09 17:46) [17] Да, очень. Сюда , если можно : Talla2kDOGukr.net И еще вопрос сразу. У меня ситуация такая, что я сперва должен что-то отправить, а потом устройство ответит. А во всех серверах наоборот...они ждут. а потом отвечают.
-
> И еще. Ранее я использовал 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мс и иду дальше. Далее спустя какое-то время клиент посылает пакет и первый байт этого пакета я теряю. Видимо это связано с тем, что я начал чтение одного байта, но оно было прервано .
|