Форум: "Сети";
Текущий архив: 2011.12.04;
Скачать: [xml.tar.bz2];
ВнизРабота с сокетами (теория) Найти похожие ветки
← →
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;
Скачать: [xml.tar.bz2];
Память: 0.53 MB
Время: 0.004 c