forum.boolean.name

forum.boolean.name (http://forum.boolean.name/index.php)
-   Общее (http://forum.boolean.name/forumdisplay.php?f=139)
-   -   Правильная организация Server<-> client (http://forum.boolean.name/showthread.php?t=20463)

RegIon 31.10.2016 10:16

Правильная организация Server<-> client
 
Как правильно реализовать обмен данных между сервером и клиентом при условии, что клиентов несколько (4 штуки максимум) и сервер постоянно что-то должен выполнять (т.е не должно быть блокировки)?

Само детище на C# и как я понял в тренде сейчас async/await ,но я не знаю как это использовать, a не круто делать так:
* Поток регистрации клиентов
* Поток логики
* По-потоку на каждого клиента

Еще желательно, что бы CPU-frendly это все было, так как десктопная приложенька, которая предполагается, будет работать фоном.

Тыкните в правильное направление.

Andvrok 31.10.2016 12:00

Ответ: Правильная организация Server<-> client
 
Почему «не круто» делать через потоки?

RegIon 31.10.2016 13:13

Ответ: Правильная организация Server<-> client
 
Цитата:

Сообщение от Andvrok (Сообщение 309603)
Почему «не круто» делать через потоки?

Процессорное время.


Какого фига все, что после обведенного красным НИКОГДА не выполняется??
Я ради эксперимента даже использовал обычный метод, и все равно, даже в дебагере дальше этого места не идет, и главное нету исключений вообще никак.

Асинхронная запись работает, а асинхронное/синхронное чтение НЕТ!

Andvrok 31.10.2016 14:48

Ответ: Правильная организация Server<-> client
 
Я не особо по асинхронности, но сейчас сделал по примеру в мсдн и всё работает.

RegIon 31.10.2016 15:09

Ответ: Правильная организация Server<-> client
 
Цитата:

Сообщение от Andvrok (Сообщение 309613)
Я не особо по асинхронности, но сейчас сделал по примеру в мсдн и всё работает.

:dontknow:

У меня TcpClient.Stream, писаться в него пишется, а читаться - нифига. :wild:


RegIon 31.10.2016 19:11

Ответ: Правильная организация Server<-> client
 
Оказалость, что если указать локальным хостом для TcpListener "127.0.0.1", то можно словить забавный баг - до него нельзя достучаться по IP локальной сети, у меня это 192.168.1.3, а если получается, то только сервер не может из него читать - только писать.

h1dd3n 31.10.2016 20:44

Ответ: Правильная организация Server<-> client
 
Цитата:

Сообщение от Andvrok (Сообщение 309603)
Почему «не круто» делать через потоки?

Очевидно что нормальный сервер многопоточный (иначе не будут использоваться все процессоры).
Однако создавать по потоку на каждого клиента - это самый плохой способ. Большую часть времени потоки будут тупо ничего не делать а ждать данных от i/o. В итоге будут жрать память и жрать процессорное время. Короче херовая масштабируемость.
Впрочем, если там 3-4 клиента то разница вообще незначительная.
Цитата:

Оказалость, что если указать локальным хостом для TcpListener "127.0.0.1", то можно словить забавный баг - до него нельзя достучаться по IP локальной сети, у меня это 192.168.1.3, а если получается, то только сервер не может из него читать - только писать.
Много раз писал небольшие локальные серваки на дотнете, ни разу такого не наблюдал. Брандмауэр/антивирус? Версия фреймворка?

moka 31.10.2016 23:18

Ответ: Правильная организация Server<-> client
 
Чтение с клиентов данных и их подготовка (десериализация) к употреблению в игровом цикле. Это одним потоком с использованием async/await.

Игровой цикл в своём потоке, выполняется фиксированное раз в секунду. Чем выше частота логики, тем лучше (обычно), но и CPU стоимость будет выше.

Цикл рассылки данных, может быть по той же частоте как и игровая логика, а может быть реже в два раза. Зависит от ситуации. Но должен выполнятся также конкретное раз в секунду, и сразу же после игрового цикла. Собирает данные с игрового цикла, и рассылает всем клиентам используя async/await.

Использовать поток на каждого клиента - не масштабируется, и жрёт много RAM'а, также усложняет всю логику синхронизации данных между потоками.

RegIon 31.10.2016 23:42

Ответ: Правильная организация Server<-> client
 
Цитата:

Сообщение от h1dd3n (Сообщение 309617)
Много раз писал небольшие локальные серваки на дотнете, ни разу такого не наблюдал. Брандмауэр/антивирус? Версия фреймворка?

.net 4.6, брадмауэр спроисл разрешение - я дал.

Сменил IP на 0.0.0.0 вместо 127.0.0.1 - все работает.

Цитата:

Чтение с клиентов данных и их подготовка (десериализация) к употреблению в игровом цикле. Это одним потоком с использованием async/await.
Тут небольшая проблема.
Пусть у нас 4 клиента, шаг опроса -10 ms , сделаем до каждого асинхронный запрос на чтение данных, а придет он только через 10 + ping.

У меня сейчас Один поток логики с Thread.Sleep(10), который асинхронно обрабатывает подключение, которое асинхронно запускает цикл чтения у подключившегося клиента (кароче, поток фактически запускает). Это асинхронное чтение десериализует и вызывает делигат с получеными данными, на который подписан поток логики.

Вроде работает, только не могу с входного потока прочитать, не закрыв его:wild:.
Вернее, у меня клиент на Java. Хоть очищай, хоть не очищай выходой поток - пока его не закроешь (что нельяз, так как закроется сокет) - данные не уйдут:wild:
в c# это решилось Writer.autoFlush, а у BufferedWriter в Java такого нет и flush не помогает.

moka 01.11.2016 00:30

Ответ: Правильная организация Server<-> client
 
Я с Java работал лет 6 назад, и как раз работал с потоками и веб сокетами, там есть возможность опрашивать есть ли в наличии для чтения данные из сокета, и затем получать если есть, совмещаешь это с асинком и будет ок.

Гугли.

Nex 01.11.2016 09:50

Ответ: Правильная организация Server<-> client
 

RegIon 01.11.2016 10:17

Ответ: Правильная организация Server<-> client
 
Цитата:

Сообщение от Nex (Сообщение 309625)

4 подобных ролика посмотрел и в вкурил как это работает. Так же как в JS, только вызывается не калбек - а продолжается как Corotutine в Unity.

impersonalis 01.11.2016 12:01

Ответ: Правильная организация Server<-> client
 
Топикстартер, ты что - переделал первый пост? Когда я читал его первый раз, там была фраза, что мол, потоки плохо без особой мотивации - что и вызвало недоумение.

Вообще - солидарен с mok-иной архитектурой. Вроде ещё механизм "пул потоков" по теме может пригодиться, но я с ним не работал.

RegIon 01.11.2016 12:10

Ответ: Правильная организация Server<-> client
 
Цитата:

Сообщение от impersonalis (Сообщение 309630)
Топикстартер, ты что - переделал первый пост? Когда я читал его первый раз, там была фраза, что мол, потоки плохо без особой мотивации - что и вызвало недоумение.

Вообще - солидарен с mok-иной архитектурой. Вроде ещё механизм "пул потоков" по теме может пригодиться, но я с ним не работал.

Нет, не переделал:4to: . async/await - не потоки как таковые.

h1dd3n 01.11.2016 20:21

Ответ: Правильная организация Server<-> client
 
Судя по всему в этой теме смешалось в кучу все: кони, люди, потоки, таски, async/await, threadpool...
  • Создавать потоки вручную (использовать класс Thread) в современном C#/.NET нужно только в 0.00001% случаях, и ситуация ТС под этот случай не подходит.
  • В C# нынче (и вообще-то уже довольно давно) оперируют тасками - Task, Task<T>. Таск это просто задача, сами по себе таски никакого отношения к потокам не имеют. Таски вообще совершенно отдельная от async/await фича.
  • Для того чтобы сервер нормально масштабировался хорошо бы чтобы потоки не блокировались и переиспользовались. Async/await к этому отношения не имеет. Асинхронные серверы были в дотнете ЕМНИП со 2.0 версии фреймворка - IAsyncResult паттерн.
  • Когда мы создаем таск (Task), то скорее всего (такое поведение по умолчанию), мы ставим эту задачу на выполнение шедулеру (TaskScheduler).
  • Шедулер по своему внутреннему алгоритму как-то выполняет задачи. Дефолтный шедулер использует ThreadPool. В дотнете есть и другие шедулеры. Можно также написать свой шедулер. Но это не понадобится (скорее всего).
  • Async/await - это просто синтаксический сахар C# (на уровне clr об async/await даже не нужно ничего знать). Самое главное понять что он никакой магии не делает.
  • Использовать Thread.Sleep(10) - гиблое дело. Надо использовать await Task.Delay(10). То есть пока мы ждем эти 10мс, поток может пойти и выполнить какую-нибудь работу.
  • При использовании TPL (таски, шедулер и Ко) совсем не обязательно что продолжение будет выполняться тем же потоком. То есть: есть 2 задачи - прочитать из 1 сокета, и обработать, и прочитать из 2 сокета и обработать. Предположим действие происходит следующим образом: поток A обрабатывает первую задачу, он доходит до чтения и дальше ничего делать не может пока данные не придут. Конечно он идет делать другую полезную задачу - обрабатывать задачу 2. Но пока он это делал в 1 задаче из i/o пришли данные и надо бы выполнение то продолжить, но т.к. поток А занят, этим займется поток Б (который возьмется из тредпула).
  • Пункт выше объясняет почему создавать потоки вручную - идиотизм. Может получиться так что: создали поток, внутри потока, где-то есть await. Значит все продолжение после await может быть выполнено потоком из тредпула. Получается что созданный вручную поток потерян навсегда и вообще не будет использоваться.

Тебе надо - создать таск который в бесконечном цикле принимает соединения. Когда принимаешь соединение создаешь таск с бесконечным циклом в котором принимаешь данные.
Далее есть 2 варианта - если у тебя архитектура запрос/ответ (ну как http примерно) то сразу же начинаешь обрабатывать. Если у тебя что-то типа реалтайм сервера, то ты полученные данные просто куда-то кладешь (в какое-то потокобезопасное хранилище). Нету никакого "шага" в виде 10мс на чтение. Читаешь сколько можешь.
Вот на обработку (если мы говорим о реалтайм сервере) уже может быть шаг предположим 8мс. Каждый раз в этом реалтайм цикле (а это тоже таск, не поток), ты берешь все данные из этого "потокобезопасного хранилища" и обрабатываешь их, потом снова уходишь в Task.Delay.


Часовой пояс GMT +4, время: 19:31.

vBulletin® Version 3.6.5.
Copyright ©2000 - 2024, Jelsoft Enterprises Ltd.
Перевод: zCarot