Код, который вы просматриваете, предназначен для эхо-сервера, который считывает некоторые данные, отправленные в сокет другим клиентом, печатает их на консоль, а затем отправляет их обратно клиенту, с которого они пришли. Это то, что делает код в пункте 5. Вот что происходит:
данные = новый байт [1024];
это просто выделение буфера для хранения данных, которые мы собираемся прочитать.
int receiveDataLength = client.Receive (data);
это получает данные из сокета. Прием является блокирующим вызовом, поэтому он не вернется, пока не найдутся данные для чтения ИЛИ, пока сокет не отсоединится или не возникнет ошибка, и в этот момент он выбрасывает. Переменная recceiveDataLength чрезвычайно важна, потому что она говорит нам, сколько байтов мы на самом деле читаем. Количество прочитанных байтов может быть меньше, чем размер буфера, потому что мы не знаем, сколько байтов было отправлено в сокет другим клиентом. Определив, сколько байтов было прочитано, мы можем быть уверены, что знаем, сколько данных на самом деле находится в нашем буфере. Если это возвращает 0, это означает, что соединение было закрыто другой стороной.
Console.WriteLine (Encoding.ASCII.GetString (data, 0, receiveDataLength));
Это просто преобразует буфер из произвольных значений байтов в строку ASCII. Обратите внимание, что receiveDataLength позволяет нам записывать только строку для количества байтов, которые были прочитаны из сокета. Если бы мы просто записали содержимое всего буфера, вы получили бы случайное дерьмо на консоли, так как любые данные, присутствующие в буфере, которые появляются ПОСЛЕ ТОГО, КАК ДАННЫЙ ДАННЫЙ неизвестен.
client.Send (data, receiveDataLength, SocketFlags.None);
Наконец, мы отправляем данные обратно на другой конец соединения, записывая их в сокет. Мы используем ReceiveDatalength, чтобы указать, что мы хотим отправлять только те байты, которые мы на самом деле читаем, а не весь буфер. Если бы вы отправляли здесь другое сообщение, вы бы хотели использовать длину в байтах отправляемого сообщения. Скорее всего, вам никогда не понадобится устанавливать какие-либо SocketFlags, так что не беспокойтесь об этом.
Теперь этот код предназначен для сервера, который хочет получать соединения. Чтобы установить соединение, код очень похож, за исключением того, что после вызова Socket.Bind (ip) вы будете вызывать Socket.Connect (remotehost) вместо Listen () и Accept (). Тогда код для записи данных в Socket такой же, как на шаге 5 выше.
И последнее: метод Socket.Accept () поначалу не интуитивно понятен, поэтому я объясню, что он делает. Вызов Listen () говорит сокету разрешить входящие соединения, но для того, чтобы реально использовать эти соединения для чего-то, вы должны их принять. Accept () блокирует, пока не будет установлено новое соединение, затем он возвращает НОВЫЙ объект сокета. Этот сокет похож на сокет, который вы слушаете, за исключением того, что этот сокет представляет собой пару сокетов, что означает, что на другом конце соединения есть другой аналогичный сокет, который будет взаимодействовать с этим. Вы должны использовать этот новый сокет, чтобы поговорить с клиентом, который подключился.
Как только вы получили новый сокет от Accept (), у вас все еще остается старый сокет, который вы изначально называли Accept (). Этот сокет все еще ожидает новых подключений, поэтому, если вы хотите разрешить большему количеству машин подключаться к этому сокету, вам нужно продолжать вызывать Accept (), пока вы больше не захотите принимать подключения (например, если вы не хотите, чтобы быть больше, чем какое-то произвольное количество соединений) или пока вы не захотите закрыть приложение. Теперь я уже говорил, что Accept () является блокирующим вызовом, поэтому, если вы вызовете его снова, он не позволит потоку, в котором вы находитесь, делать что-либо еще, пока не установится новое соединение. Это означает, что вам нужно выполнить одно из двух Вещи: либо вызовите BeginAccept для асинхронного приема новых клиентов, либо используйте BeginRead или BeginSend для асинхронной обработки ввода / вывода вашего сокета. Вы НЕ МОЖЕТЕ делать обе эти вещи в одном потоке. Это немного сложнее сделать, но посмотрите пример IPv6Sockets на MSDN (http://msdn.microsoft.com/en-us/library/8sd71sfs(VS.80).aspx)), и вы сможете увидеть, как это делается. Помните, что абсолютно необходимо, чтобы вы НЕ пытались принимать новые подключения и выполнять I / O для существующих соединений в том же потоке. Я действительно рекомендую делать все с сокетами асинхронно.
Если вы не хотите использовать все эти сокеты, вы также можете проверить класс TcpClient. Это тонкая оболочка для Socket, которая позволяет вам захватывать объект Stream из TcpClient и просто читать / записывать из этого потока. Его гораздо проще использовать, но он не такой мощный, как Socket, хотя для вашего приложения я сомневаюсь, что вам все равно придется использовать что-то необычное с Sockets, поэтому я бы использовал TcpClient.