C # клиент-серверный протокол / модель вопроса

2

Я пытаюсь концептуально работать через модель для приложения-сокета клиент-сервер, которое я пишу в С# (как клиент, так и сервер). Мой сервер должен будет обрабатывать сразу несколько клиентов, и желательно сразу несколько запросов от клиента. Я разработал контейнер для моего сообщения, где я отправлю заголовок с фиксированной длиной в начале каждого сообщения, которое будет содержать (помимо прочего) длину сообщения. У меня есть некоторый опыт программирования сокетов в С#, поэтому мне удобно использовать асинхронные сокеты.

Главное, с чем я столкнулся, концептуально, мне нужно, чтобы клиент и сервер могли получать сообщения в любое время. Клиент установит соединение и останется "вошедшим в систему" ​​(например, IM-клиент), и ему нужно будет как получать данные в произвольные моменты времени, так и делать запросы в произвольные моменты времени. Я также хотел, чтобы часть моего протокола получала ответ на каждый сделанный запрос (будь то от сервера к клиенту или от клиента к серверу).

Я хотел бы иметь возможность использовать один сокет, если это возможно. Я считаю, что я мог бы сделать эту работу с использованием двух сокетов, один для создания запросов server- > client и один для клиент- > серверных запросов, но я не хочу лишних сложностей с двумя портами/соединениями. Однако, используя один сокет, я не уверен, как управлять отправкой запросов и получать ответы, когда они могут чередоваться.

Я не могу найти примеры похожих серверов или клиентов в моем поиске. Спасибо всем, кто предлагает любые ides.

Теги:
sockets
model
tcp
client-server

2 ответа

2
Лучший ответ

Если вы используете TCP, то вам обязательно придется иметь один сокет для каждого клиента на стороне сервера, а также сокет для прослушивания соединений. Для UDP вы можете сделать это все с одним сокетом. Независимо от того, сделайте следующее для вашего одного сокета UDP или для каждого клиентского сокета TCP:

У вас есть BeginReceive в любое время и BeginWrite, когда происходит необходимое событие (т.е. пользователь нажимает кнопку или появляется сообщение о том, что вам нужно что-то делать).

EDIT: в ответ на ваш комментарий способ, которым я бы справился с этим, состоял бы в том, чтобы включить идентификатор запроса в ваш заголовок. Всякий раз, когда отправка запроса (с любого конца) включает уникальное значение для этого сообщения. Используйте Dictionary<RequestId, RequestState> (с соответствующими подстановками для типов) и посмотрите, связано ли входящее сообщение с ранее существующим запросом. Кроме того, вы можете указать, что все идентификаторы с высоким набором бит - это запросы, исходящие от клиента, и идентификаторы с высоким битом, очищенным от сервера, чтобы избежать конфликтов:

Server                      Client
Request 0x00 -------------> Starts processing

Starts processing <-------  (unrelated) Request 0x80

Request 0x00 complete <---- Sends response to 0x00

Sends response to 0x80 ---> Request 0x80 complete

Это система, в которой используется протокол AOL OSCAR для (возможно медленных) запросов состояния.

EDIT2: Hm, хорошо... вы действительно хотите заблокировать его? Что-то, что вы могли бы сделать, это иметь отдельный поток, обрабатывающий вызовы Send/Receive. Этот поток будет связываться с основным потоком через потокобезопасную очередь "отправить сообщение" и аналогичную "полученную сообщение" psuedo-queue. Причина, по которой я называю последнюю очередь psuedo, заключается в том, что вы хотели бы, чтобы возможность выводить сообщения из очереди из очереди. Основной поток помещает сообщение в очередь отправки, а затем блокирует очередь приема. Каждый раз, когда поток сокета обновляет очередь приема, основной поток просыпается и проверяет, есть ли сообщение, которое оно хочет. Если это так, то потребуется (не по порядку) и закончить желаемую операцию. Если сообщение еще не существует, оно просто будет заблокировано в очереди. Когда операция завершена, она только возобновит нормальную работу и получит сообщения в очереди от очереди приема и обработает их или сделает что-то еще.

В этом смысл? Прошу прощения, если это смущает...

  • 0
    Да. Но предположим, что сервер запрашивает у клиента что-то, что занимает некоторое время. Между тем клиент запрашивает у сервера что-то. В BeginReceive моего сервера, как я узнаю, что эти входящие данные являются запросом клиента или ответом на запрос сервера? Это то, с чем я борюсь. У меня есть основы асинхронного сокета. Благодарю.
  • 0
    Извините, все разговоры о сокетах заставили меня думать, что ваша проблема была на этом уровне; как это обновление?
Показать ещё 2 комментария
3

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

Но на стороне сервера вам нужно открыть ServerSocket, который будет привязываться к TCP-порту и принимать соединения на этом порту; каждое соединение, которое вы принимаете, является новым Socket. Таким образом, у вас есть сокет для каждого клиента, и вам, вероятно, понадобится один поток для каждого клиента.

Вам необходимо установить протокол для сообщений, которые вы будете передавать через сокет. Вы можете либо brew создать собственный протокол, либо просмотреть его в зависимости от того, что вы хотите сделать. Обычно вы сначала отправляете два или четыре байта с длиной для остальной части сообщения, поэтому другой стороне известно, чтобы прочитать, что много байтов из потока; это сообщение. Начало сообщения обычно является типом сообщения; в очень простом сценарии вы можете сказать, что "1" - это клиентский запрос, "2" - ответ сервера, "3" - это клиентское эхо, "4" - ответ сервера на сервер, "5" - сервер echo, "6" - это клиентский эхо-ответ и т.д. Таким образом вы получаете сообщение, анализируете его, а затем вы знаете его запрос от клиента или ответ на сообщение, отправленное с сервера, например.

Ещё вопросы

Сообщество Overcoder
Наверх
Меню