Спортивный сокетный интерес

Все, что вы хотели знать о программизме, но боялись спросить.
MarkM
Пользователь
Сообщения: 113
Зарегистрирован: 24 сен 2003, 21:52

Сообщение MarkM »

[quote "vg"]
В работе клиента есть одна особенность – возможно большие таймауты, когда пользователь пошёл покурить.
[/quote]
[trn]Obychno propraitory protokoly tipa Net8 derzhat setevye soedinenija skol'ko ugodno dolgo. No inogda Set' (fairvol) nastroeny tak chtoby rvat' takie dolgo spjashie soedinenija.
Esli dlja tebja eto problema, to tebe podhodit SOAP. On delaet odin zapros, i vozmozhno poluchaet odin otvet i zakryvaet soedinenie. Obychno eto HTTP. A on sam v principe odnozaprosnyj.
Pri takoj sheme Sessija Prilozhenija podderzhivatsja "vyshe" transportnogo protokola.
Tut opjat' dilemma - kak dolgo hranit' sessiju v pamjati? Ona zanimaet resursy. Delo oslozhnjaetsja tem, chto ne v primer sessii vstroennoj v transportnyj protokol, sessija urovnja prilozhenija ne znaet prichiny prostaivanija. Libo uzer medlenno vvodit dannye, libo uzhe ubil svoj klient(brauzer) i uvolilsja. :)

Kak zametil Marmot, SOAP eto tozhe dyra v sekjuriti. Cherez nego vrode kak vozmozhno ispolnit' proizvol'nyj kod na servere. (Marmot pust' menja popravit esli cho ne tak)
No v obshem sluchae SOAP bol'she podhodit dlja slabosvjazannyh geterogennyh sred chem rodnye protokoly baz dannyh.

Ne ponjal tvoih voprosov po konkretnoj realizacii na MSVC. Imho oni offtopik.
Za iskljucheniem "porjadka baijtov". Gury pravy. Kto to dolzhen sledit' i translirovat' tuda-sjuda, v sluche esli klient i server sidjat na raznyh "endian" platformah.
[/trn]
[/trn]
vg
Маньяк
Сообщения: 2803
Зарегистрирован: 29 май 2003, 22:29
Откуда: Магадан - Миссиссага

Сообщение vg »

Marmot, Mark,

В топике было неоднократно сказано про SOAP и XML при решении рассматриваемой задачи (про DBMS). Вначале немного об этом. А в самом конце замечания общего характера.


1. *** ПРО «ПРЕИМУЩЕСТВА» SOAP ***

Сравним на примере два подхода при использовании техники «вычитывания данных по полям», как было показано в самом начале. Один подход - на сокетах с использованием самодельного протокола, другой – с использованием SOAP.

1.1. На сокетах

В начале топика был фрагмент кода клиента, в котором был такой вызов:

Код: Выделить всё

if ( FAILED( rc = QueryIntegerField( hQuery, "id", &ival) ) )
{
	…
	…
}
Этот вызов реализует запрос клиента размером в 18-байт (12-байт на заголовок, 4-байта на дескриптор hQuery, 2-байта на имя поля «id» ) в виде:

Код: Выделить всё

заголовок
msg_size	= 18 байт
command		= UC_QUERYINTEGER
param		= 0
	checksum	= 21980
error		= 0

данные
hQuery		= 301989888
FieldName	= «id»
Здесь макро UC_QUERYINTEGER (числовой код команды) определено, например, #define UC_QUERYINTEGER 16. Весь этот буфер и записывается в сокет.

Сервер знает, каков формат «сообщения по протоколу» для запросов клиента с полем в заголовке command = UC_QUERYINTEGER. Поэтому, сделав «свою работу» он возвращает отклик размером 16-байт (заголовок + результат) или 12-байт (только заголовок), в зависимости от значения ошибки error, см. выше. Например, он может вернуть клиенту отклик:

Код: Выделить всё

заголовок
msg_size	= 16 байт
command		= UC_QUERYINTEGER
param		= 0
	checksum	= 14962
error		= QUERYINTEGER_S_RECORDSET

данные
FieldValue	= 1
Здесь макро QUERYINTEGER_S_RECORDSET определено по аналогии с COM, например, так:

Код: Выделить всё

#define FACILITY_ITFPROTO		FACILITY_ITF
#define SCODE_BASE			0x200
// 
// Клиент запросил целое поле (UC_QUERYINTEGER) набора данных объекта IQueryInterface. 
//
#define QUERYINTEGER_E_RECORDSET	MAKE_HRESULT(1, FACILITY_ITFPROTO, SCODE_BASE + 115) 
#define QUERYINTEGER_S_RECORDSET	MAKE_HRESULT(0, FACILITY_ITFPROTO, SCODE_BASE + 115)
Этот отклик читает клиент из сокета (за два раза). Как было сказано – в LAN работает быстро. В инет – с практической точки зрения работать не будет (имеется в виду эффективность)

1.2. С использованием SOAP

Я не спец в XML и SOAP. Могу наделать ошибок. Но по существу, надеюсь, там правильно.

Запрос клиента, аналогичный тому, что в п.1.1, может выглядеть так:

Код: Выделить всё

<?xml version="1.0" encoding="UTF-8" standalone="no" ?> 
<SOAP-ENV:Envelope ...>
   <SOAP-ENV:Body ...>
	<SOAPSDK1:QueryInteger ...>
		<hQuery>301989888</hQuery> 
		<fieldname>ID</fieldname> 
	</SOAPSDK1:QueryInteger>
  </SOAP-ENV:Body>
</SOAP-ENV:Envelope>
Соответствующий ответ сервера - так:

Код: Выделить всё

<?xml version="1.0" encoding="UTF-8" standalone="no" ?> 
<SOAP-ENV:Envelope ...>
  <SOAP-ENV:Body ...>
	<SOAPSDK1:QueryIntegerResponse ...>
		<HRESULTResponse>262771</HRESULTResponse> 
		<Result>1</Result> 
	</SOAPSDK1:QueryIntegerResponse>
  </SOAP-ENV:Body>
</SOAP-ENV:Envelope>
Здесь код запроса (поле error) помещён непосредственно в soap:body в качестве возвращаемого значения, а не в fault:

Код: Выделить всё

<SOAP-ENV:Fault>
	<faultcode>SOAP-ENV:Server</faultcode> 
	<faultstring>Query failed</faultstring> 
	…
	<detail>
		<mserror:errorInfo ...>
		…
	</detail>
</SOAP-ENV:Fault>
Это связано с теми же проблемами, которые присутствуют в COM программировании в среде VB, и о которых справедливо замечал Woozy в одном из здешних топиков. (там мы говорили, что если клиенту необходимо анализировать HRESULT с не установленным битом ошибки, то это следует делать путём передачи HRESULT кода в параметрах метода).

Возникает вопрос, а чем лучше то, что «на сокетах», п.1., или то, что «на soap»? Ни лучше, ни хуже, если речь идёт о коде клиента, который приведён в начале топика и инет. Это просто не будет работать и для SOAP, и для протокола «на сокетах».

1.3. Почему это не работает

Код (а вернее техника), который приведён в самом начале, - совершенно нормальный для LAN. По такому же «сценарию» строят клиентские приложения в LAN и с использованием ODBC, и ADO, и BDE. И не важно, что строчки кода будут другими, если программер будет использовать не обёрточное API (как в коде выше), а непосредственно обращаться к методам интерфейсов COM-объектов, скажем, ADO. Стереотип в LAN в том, что данные обычно вычитывают «порциями» и при этом ещё обращаются к серверу с «промежуточными» запросами, типа QueryNext, QueryEof. При этом набор данных держится открытым на сервере до тех пор, пока клиент с ним работает. Клиент делает запрос QueryEof, сервер отвечает - «нет». Тогда клиент делает следующий запрос QueryInteger, сервер отвечает откликом со значением требуемого поля. Потом клиент просит переместить указатель текущей записи набора данных на серверной стороне – QueryNext, сервер – выполняет. И т.д.

Не сложно показать, что при такой технике, даже если узлы расположены на «расстоянии» 2-3 хопов, и, предполагая любое разумное минимальное RTT, – клиентское приложение будет ждать «вечность». Работать, конечно, будет. Только захочется закрыть приложение раньше, чем заполнится какой-то ListBox.

2. *** КАК МОЖНО БЫЛО БЫ СДЕЛАТЬ ***

В принципе решение на поверхности. Следует любые результаты возвращать сразу, а не по частям. Это означает, что на запрос клиента QueryOpen( hQuery, «select * from Spr» ) сервер должен возвращать весть набор данных сразу, в одном отклике. Это делает архитектуру клиента в принципе отличной от того, что обычно принято делать в LAN. Клиент должен прочитать все данные из сокета в буфер, заполнить массив неких структур CRecordset так, чтобы из него можно было б извлекать данные в коде, как показано в самом начале. Другими словами все танцы с бубном вокруг QueryEof, QueryNext и т.д. должны происходить на стороне клиента, а не сервера. И это не представляется очень простой задачей. Кстати для сервера в этом случае есть некоторое облегчение. После записи в сокет всего массива он может закрыть открытые наборы данных, освободив драгоценные ресуры.

2.1. На сокетах

2.1.1) Пользователь перечисляет все поля в инструкции SELECT.
Здесь та ситуация, когда пользователь использует инструкции типа select fld1, fld2, …from tbl. Соответствующее «расширение» рассматриваемого протокола легко построить. Клиент знает, сколько полей он хочет получить. Серверу останется только передать в области данных (после основного заголовка) дополнительный заголовок, описывающий структуру полей и число записей. В принципе, за основу можно было бы взять вообще формат аналогичный DBF (было время, когда для DOS вообще писал DBF «руками», на С – и ничего страшного). Можно было б ограничится и более простыми заголовками, в которых перечисляются только длины в байтах каждого поля. Дело клиента – как интерпретировать последовательность байт для каждого из полей. В этом случае, правда, придётся заменить доступ к полю записи «по имени» доступом «по номеру» поля.
Но одна вот проблема – как быть в этом случае с MEMO и с BLOB? Не знаю. Если б не это – задача не особо сложная.

2.1.2) Пользователь не перечисляет поля в инструкции SELECT.
Здесь та ситуация, когда пользователь использует инструкции типа «select * from tbl». Задача аналогична п.3.3.1, но сложнее. Если нет MEMO и BLOB – может помочь дополнительный заголовок «DBF» в области данных. С учетом того, что MEMO и BLOB используют почти всегда– ситуация тупиковая.


2.2. С использованием SOAP

Не вижу здесь (из-за того, что не знаю) особых облегчений, даже с учётом того, что SOAP имеет встроенную поддержку для передачи массивов сложных структурированных данных. Маперы SOAP не помогут, поскольку предполагают описания вполне конкретных структур данных в WSDL-документах. Т.е. в схеме

Код: Выделить всё

<types>
  <schema ...>
  ...
  <complexType  name ='Recordset'>
    ...
  </complexType>
  </schema>
</types>
нужно писать чего-то вполне определённое. Это не относится к рассматриваемой задаче, поскольку SELECT принципиально предполагает, что структура данных может быть любой. Ну, не генерировать же WSDL-документы каждый раз, после очередного запроса клиента, и не переинициализировать же объекты SOAP-сервера и SOAP- клиента. Впрочем, не знаю. Я здесь – не разбираюсь. Мармот пусть поправит.

2.3. С использованием XML

Здесь немного лучше, поскольку мы имеет возможность разметки отклика сервера «на лету». Например, сервер, мог бы возвратить такой отклик на запрос клиента «select * from tbl»:

Код: Выделить всё

<QuerySelectResponse>
	<HRESULTResponse>262453</HRESULTResponse> 
	<Record>
		<field …>
			<INTEGER>1</INTEGER>
		</field>

		<field …>
			<FLOAT>1.0</FLOAT>
		</field>

		<field …>
			<МЕМО>Привет мир</МЕМО>
		</field>
	</Record>
	<Record>
		…
		…
	</Record>
		…
		…
</QuerySelectResponse >
Я не рассматриваю здесь то, что часто говорят о снижении производительности для XML. В рассматриваемом XML-случае будет не снижение, а повышение производительности в 100 и более раз по сравнению с тем, что «на сокетах» в п.1. Там и причины. Правда, неясно, что мы получим в плане производительности для BLOB-полей с бинари. Имеется ввиду кодирование/декодирование base64.
Напоследок скажу, что даже «неправильный» XML-документ можно легко встроить в рассматриваемый «сокетный протоко». Всё равно парсить будем в сугубо индивидуальных целях. Получится некий гибрид.

Уродец? Как думаете?


3. *** В ЗАКЛЮЧЕНИЕ***

Я хотел бы расставить некоторые акценты на то, что было сказано в предыдущих постингах различных мемберов.

3.1. Говорили здесь о том, что, дескать, не надо DCOM. Хочу заметить, что DCOM была и есть одна из основных технологий построений распределённых решений Microsoft Windows для LAN. Это не я говорю. Так говорит Microsoft.

3.2. Говорили о DCOM и DBMS. Для доступа к DBMS в LAN DCOM, как таковой, не нужен. Достаточно ODBC, ADO, BDE.
DCOM нужен для решения других задач, например, построения приложений многозвенной архитектуры. А там хоть и DBMS.

3.3. Говорили о DCOM и межсетевых экранах. DCOM-трафик – это обычный трафик. Товарищи, не знающие Windows, не знают, что можно настроить DCOM так, чтобы он «слушал» порты в совершенно определённом диапазоне. Забывают (вернее не знают) товарищи и о том, что DCOM очень тесно интегрирован с политиками безопасности W2k. И там другие проблемы. Но это уже другая песня.

3.4. Говорили о сокетах и межсетевых экранах. Кстати это отчасти относится и к DCOM.
Здесь, на форуме, есть компетентные товарищи, которые раньше работали на узлах связи. Они не дадут соврать. Ни один оператор связи не фильтрует трафик для своих клиентов. Ни в России, ни зарубежём. Только в исключительных случаях и по особой договорённости оператора и клиента. Так, что разговоры «пропустит/непропустит» трафик маршрутиризатор одного из операторов – выглядят по меньшей мере неуклюжими и смешными. Товарищи программисты не знают того, что мало оператор не фильтрует трафик клиентов, он не фильтрует даже того, что обязаны фильтровать (RFC 2827). А уж, что ваш админ будет фильтровать, в вашей организации - так это его и спросите;))) Или с него спросите;)))

3.5. О серъёзности всего, что было написано. Было написано в топике честно – «для спортивного интереса». Ибо скучно. И никакого флейма не задумывалось. Но!

Народ, вы вдумайтесь!!! Выполнять запросы SQL – по Интернет ;)))) Да ещё на сокетах;)))

Пишу это, поскольку, как мне показалось, иногда реакция была не совсем адекватной. :lol: :lol: :lol:

ПС. Благодарности всем. Особенно, Mark, тебе. На мой взгляд в этой хоть и надуманной задачке «для спортивного» интереса, ты был правее всех правых.
Могу не согласиться с тобой только в части «текучки». Вопросы там хоть и простые были, но важные.
Ответить