LINUX.ORG.RU

Клиент-сервер. Связь с основными классами. (Qt)

 , ,


0

2

Сразу прошу прощения за нубовопросы (для кого-то).

Вот есть класс на стороне клиента, который обслуживает клиент-серверное общение (работает с сокетом). Есть паблик-метод ОтправитьСообщение, есть сигнал, который возвращает ответ.

Следующий шаг - использование этого класса для визуализации пользователю данных из БД. Как правильно (хотите, православно) это сделать? Вижу несколько вариантов, но не буду утверждать, что среди них есть правильный, поэтому и спрашиваю. :)

1. Сделать этот класс статичным, в нем какой-нить мутекс, который будет говорить, что в данный момент кто-то уже запросил данные из БД. Когда форме надо что-то узнать на сервере, она вызывает метод этого класса ОтправитьСообщениеИПрочитатьОтвет, который «взводит» мутекс (или ждет возможности), а далее - по понятной схеме: отправляет, ждет результата, получает его и возвращает ответ.

2. Тот же вариант с мутексом, но только класс не статичный, но он (класс) один для всех форм (прикладных классов), при создании которых в конструкторе передается ссылка на этот единичный экземпляр класса. Работа - похожая.

3. Для каждой формы (прикладного объекта) создавать свой уникальный класс-клиент, к которому уже системой сигналов-слотов реализовывать отправку сообщения и принятие ответа.

Вижу, что в последнем случае, весь траффик будет распараллелен по сокетам, т.е. один клиент-программа будет работать по множеству сокетов с сервером. Не скажется ли это на работоспособности?

В общем, хочу совета.

ЗЫ, лучше не знать и спросить, чем не спросить и дальше не знать. :)

★★★★★

я это все к чему... если я просто из каждого прикладного класса «подпишусь» на сигналы этого класса (если он будет один на инстанс программы), то сигнал получения ответа от сервера проинформирует всех подписчиков. А как узнать, кто данные запросил?

bvn13 ★★★★★ ()
Ответ на: комментарий от bvn13

А как узнать, кто данные запросил?

Либо я чего-то не распарсил, либо сделать в слоте "подписчика" проверку на то, запрашивал он данные, или нет...

solovey ★★ ()
Ответ на: комментарий от bvn13

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

Если я конечно правильно тебя понял.

staseg ★★★★★ ()
Ответ на: комментарий от solovey

ну вот мне как-то кажется, что во всех этих проверках можно утонуть...

bvn13 ★★★★★ ()
Ответ на: комментарий от staseg

да, я об этом тоже подумал, но уже после создания треда. но, почему-то я больше склоняюсь к моему третьему варианту. т.е. будет примерно такая схема классового взаимодействия:

Клиент.MainWindow (один экземпляр) - дочерние формы (много) - по одному Клиенту сервера на каждую форму (много) - КОННЕКТ - серверный клиент (класс, работающий с Клиентом сервера, много) - сервер (один)

bvn13 ★★★★★ ()
Ответ на: комментарий от bvn13

И у клиента, и у сервера есть лимит в 65k на число портов. Если ты можешь себе позволить пачками забивать серверные порты, то пожалуйста.

staseg ★★★★★ ()
Ответ на: комментарий от staseg

вооооот. я знал, что где-то есть подвох :)

bvn13 ★★★★★ ()
Ответ на: комментарий от staseg

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

bvn13 ★★★★★ ()
Ответ на: комментарий от bvn13

Значит, я все-таки не распарсил....

Кстати, staseg дело говорит, уникальные идентификаторы на каждый запрос делать надо в любом случае. А еще в посылку можно uid клиента воткнуть, тогда вообще никаких проблем с распознаванием "нужного" ответа не будет. И проверка существенно упростится.

solovey ★★ ()
Ответ на: комментарий от bvn13

ну, а в первых двух моих вариантах все клиентские запросы будут в очереди стоять. это ведь тоже не хорошо?

Зависит от задачи, в твоем случае, наверное, плохо.

может, по третьему варианту и для каждого запроса к серверу устанавливать соединение, а после запроса обрывать?

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

staseg ★★★★★ ()

скастую-ка я сюда уважаемых quiet_readonly и trex6. Лишь бы не отвлечь их от важных дел...

bvn13 ★★★★★ ()
Ответ на: комментарий от staseg

Установка соединения может быть довольно затратной, особенно, если у тебя предусмотрена аутентификация.

да, предусмотрена. но, думаю, ее можно максимально приблизить к Скулю, ХП сделать.

я ни разу не видел клиентов, бобмардирующих сервер коннектами

а я этот метод где-то тут и вычитал. кто-то о таком рассказывал, мол, нефиг хранить соединение открытым: прочитал - и закрыл. но, мне тоже кажется, что слишком частые коннекты до добра не доведут.

вообще в голову ничего не приходит, кроме как выстроить все прикладные классы в одну очередь или дать им возможность работать параллельно. Только вот что будет лучше? Хотя... я не думаю, что один пользователь задействует на своем клиенте +100500 объектов прикладных классов, чтобы на сервере закончились сокеты. :) да даже в таком случае, максимум, что придется - поднять второй инстанс сервера. Но, не думаю, что до такого дойдет. :)

bvn13 ★★★★★ ()

срочно учи erlang. тебе нужен актор, ответственный за базу.

nanoolinux ★★★★ ()
Ответ на: комментарий от nanoolinux

:) erlang - хорошо. что такое акор, ответственный за базу, предполагаю. вроде его и делаю.

bvn13 ★★★★★ ()
Ответ на: комментарий от bvn13

Ну так и в чём проблема? Синглтон - плохо. Делай фабрику или передавай shared_ptr на свой DataBaseProxy всем. В нём собственно один метод будет - ask_db

shared_ptr <DataBaseRespond>
DataBaseProxy::ask_db(const DataBaseRequest &req)
{
	MutexLocker m(mutex_);
	switch(req.type()) {
		case DataBaseRequest::weather_req:
			return get_weather_from_db();
		case DataBaseRequest::currency_price_req:
			return get_currency_price_from_db();
		default:
			throw DataBaseProxyException("Bad db request", DataBaseRespond::bad_request);
	}
}

shared_ptr <DataBaseRespond>
DataBaseProxy::get_weather_from_db()
{
	return make_shared <DataBaseRespond> (db_->sql("SELECT FROM currency..."));
}

shared_ptr <DataBaseRespond>
DataBaseProxy::get_currency_price_from_db()
{
	return make_shared <DataBaseRespond> (db_->sql("SELECT FROM weather..."));
}

nanoolinux ★★★★ ()
Вы не можете добавлять комментарии в эту тему. Тема перемещена в архив.