Поскольку это «начало работы», мой ответ будет придерживаться простой реализации, а не масштабируемой. Лучше сначала почувствовать себя комфортно с простым подходом, а потом все усложнить.
1 - Связывание и прослушивание
Мне кажется, ваш код в порядке, лично я использую:
serverSocket.Bind(new IPEndPoint(IPAddress.Any, 4444));
Вместо того, чтобы идти по маршруту DNS, но я не думаю, что в любом случае есть реальная проблема.
1,5 - Прием клиентских подключений
Просто упомяну это для полноты ... Я предполагаю, что вы делаете это, иначе вы не дойдете до шага 2.
2 - Получение данных
Я бы сделал буфер немного длиннее 255 байт, если только вы не ожидаете, что все сообщения вашего сервера будут максимум 255 байт. Я думаю, что вам нужен буфер, который может быть больше размера пакета TCP, поэтому вы можете избежать нескольких операций чтения для получения одного блока данных.
Я бы сказал, что выбор 1500 байт должен подойти, или, может быть, даже 2048 для хорошего круглого числа.
С другой стороны, возможно, вы можете избежать использования byte[]
для хранения фрагментов данных и вместо этого обернуть свой клиентский сокет на стороне сервера в NetworkStream
, обернутый в BinaryReader
, чтобы вы могли читать компоненты вашего Направление сообщения из сокета, не заботясь о размерах буфера.
3 - отправка данных и указание длины данных
Ваш подход будет работать просто отлично, но он, очевидно, требует, чтобы было легко рассчитать длину пакета, прежде чем вы начнете его отправлять.
В качестве альтернативы, если формат вашего сообщения (порядок его компонентов) разработан таким образом, что в любое время клиент сможет определить, должны ли последовать дополнительные данные (например, код 0x01 означает, что next будет int и строка, код 0x02 означает, что следующим будет 16 байтов и т. д. и т. д.). В сочетании с подходом NetworkStream
на стороне клиента это может быть очень эффективным подходом.
Чтобы быть в безопасности, вы можете добавить проверку принимаемых компонентов, чтобы убедиться, что вы обрабатываете только здравые значения. Например, если вы получили указание на строку длиной 1 ТБ, возможно, где-то произошло повреждение пакета, и может быть безопаснее закрыть соединение и заставить клиента повторно подключиться и «начать заново». Этот подход дает вам очень хорошее поведение при всех непредвиденных сбоях.
4/5 - Закрытие клиента и сервера
Лично я бы выбрал просто Close
без дальнейших сообщений; Когда соединение закрыто, вы получите исключение для любого блокирующего чтения / записи на другом конце соединения, которое вам придется обслуживать.
Так как вам все равно приходится обслуживать «неизвестные разъединения», чтобы получить надежное решение, делать разъединение более сложным, как правило, бессмысленно.
6 - неизвестные отключения
Я бы не стал доверять даже статусу сокета ... соединение может умереть где-нибудь на пути между клиентом и сервером, и клиент, или сервер не заметят.
Единственный гарантированный способ сообщить о неожиданно оборванном соединении - это когда вы в следующий раз попытаетесь отправить что-то по соединению. В этот момент вы всегда получите исключение, указывающее сбой, если что-то пошло не так с соединением.
В результате единственный надежный способ обнаружить все неожиданные соединения - реализовать механизм ping, при котором в идеале клиент и сервер будут периодически отправлять сообщение на другой конец, что приводит только к ответному сообщению. показывая, что «пинг» был получен.
Чтобы оптимизировать ненужные эхо-запросы, вам может потребоваться механизм «тайм-аута», который отправляет эхо-запрос только тогда, когда другой трафик не получен с другого конца в течение заданного периода времени (например, если последний сообщение от сервера старше x секунд, клиент отправляет эхо-запрос, чтобы убедиться, что соединение не оборвалось без уведомления).
Более продвинутый
Если вам нужна высокая масштабируемость, вам придется искать асинхронные методы для всех операций с сокетами (Accept / Send / Receive). Это варианты «Начало / Конец», но они намного сложнее в использовании.
Я рекомендую не пытаться делать это, пока у вас не заработает простая версия.
Также обратите внимание, что если вы не планируете масштабировать дальше, чем несколько десятков клиентов, это на самом деле не будет проблемой. Асинхронные методы действительно необходимы только в том случае, если вы планируете масштабироваться до тысяч или сотен тысяч подключенных клиентов, в то время как ваш сервер не умирает напрямую.
Возможно, я забыл целый ряд других важных предложений, но этого должно быть достаточно, чтобы вы получили достаточно надежную и надежную реализацию, чтобы начать с