Intereting Posts
Обнаружение, если программа была запущена Visual Studio, в отличие от запуска из проводника Windows MVVM Light: как отменить регистрацию Messenger .NET Listview Refresh Как передать пространство имен методу? Как преобразовать IEnumerable в ObservableCollection? Почему это регулярное выражение без специального символа соответствует более длинной строке? Журналирование Log4net не работает с параллельными streamами Как проверить права на чтение и запись в папке на C # Нужны предложения по извлечению данных из файла .docx / .doc, а затем в SQL Server WCF – реализация клиента, который требует шифрования одного элемента soap: body, как? Не существует в текущем контексте (Visual C #) Как получить экземпляр базового classа из производного classа Как я могу сказать, что механизм маршрутизации Web API / Castle Windsor использует другой экземпляр базы данных в моем репозитории? Hangfire и VB.NET – настройки, заданные в classе запуска приложений Могу ли я наследовать конструкторы?

Способ записи TcpClient гарантирует, что данные доставляются на сервер?

У меня есть отдельный stream на клиенте и сервере, которые читают / записывают данные в / из сокета.

Я использую синхронный TcpClient im (как предложено в документации): https://msdn.microsoft.com/cs-cl/library/system.net.sockets.tcpclient%28v=vs.110%29.aspx

Когда соединение закрыто .Read () /. Write () выдает исключение. Означает ли это, что когда метод .Write () не бросает, данные были доставлены правильно другой стороне или мне нужно реализовать пользовательскую логику ACK ?

Я читал документацию как для classа Socket, так и для TcpClient, и ни один из них не описывает этот случай.

Все, что означает возвращаемый метод send() (или любая обертка, которую вы используете, например Socket или TcpClient ) в streamовом, блокирующемся интернет-сокете, состоит в том, что байты помещаются в буфер отправляющей машины.

MSDN Socket.Send() :

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

А также:

Успешное завершение отправки не означает, что данные были успешно доставлены.

Для .NET основной реализацией является WinSock2, документация: send () :

Успешное завершение функции отправки не означает, что данные были успешно доставлены и получены получателю. Эта функция только указывает, что данные были успешно отправлены.

Вызов для send() возврата не означает, что данные были успешно доставлены на другую сторону и прочитаны потребляющим приложением.

Когда данные не подтверждены вовремя или когда другая сторона отправляет RST, Socket (или любая другая обертка) станет в неисправном состоянии, что приведет к сбою следующей функции send() или recv() .

Поэтому, чтобы ответить на ваш вопрос:

Означает ли это, что когда метод .Write () не бросает, данные были доставлены правильно другой стороне или мне нужно реализовать пользовательскую логику ACK?

Нет, это не так, и да, вы должны – если для вашего приложения важно, чтобы он знал, что другая сторона прочитала это конкретное сообщение.

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

В этом случае вы можете изменить свой протокол, чтобы определенные сообщения имели требуемый ответ, который должен получить приемник. Обратите внимание, что внедрение протокола приложения на удивление легко сделать неправильно. Если вы склонны, вы можете реализовать различные streamи сообщений, связанные с протоколом, используя конечный автомат , как для сервера, так и для клиента.

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

См. Также Как проверить емкость буфера передачи TCP для обеспечения доставки данных , выяснить, было ли передано сообщение по tcp , C socket: отправляет ли ждать завершения recv?

@ Ответ CodeCaster верен и освещает документацию .NET, определяющую поведение .Write (). Вот несколько полных тестовых кодов, чтобы доказать, что он прав, а другие ответы, говорящие о том, что «TCP гарантирует доставку сообщений», однозначно ошибочны:

 using System; using System.Net; using System.Net.Sockets; using System.Text; using System.Threading; namespace TestEvents { class Program { static void Main(string[] args) { // Server: Start listening for incoming connections const int PORT = 4411; var listener = new TcpListener(IPAddress.Any, PORT); listener.Start(); // Client: Connect to listener var client = new TcpClient(); client.Connect(IPAddress.Loopback, PORT); // Server: Accept incoming connection from client TcpClient server = listener.AcceptTcpClient(); // Server: Send a message back to client to prove we're connected const string msg = "We are now connected"; NetworkStream serverStream = server.GetStream(); serverStream.Write(ASCIIEncoding.ASCII.GetBytes(msg), 0, msg.Length); // Client: Receive message from server to prove we're connected var buffer = new byte[1024]; NetworkStream clientStream = client.GetStream(); int n = clientStream.Read(buffer, 0, buffer.Length); Console.WriteLine("Received message from server: " + ASCIIEncoding.ASCII.GetString(buffer, 0, n)); // Client: Close connection and wait a little to make sure we won't ACK any more of server's messages Console.WriteLine("Client is closing connection"); clientStream.Dispose(); client.Close(); Thread.Sleep(5000); Console.WriteLine("Client has closed his end of the connection"); // Server: Send a message to client that client could not possibly receive serverStream.Write(ASCIIEncoding.ASCII.GetBytes(msg), 0, msg.Length); Console.WriteLine(".Write has completed on the server side even though the client will never receive the message. server.Client.Connected=" + server.Client.Connected); // Let the user see the results Console.ReadKey(); } } } 

Следует отметить, что выполнение происходит обычно через программу, а serverStream не указывает, что второй .Write не был успешным. Это несмотря на то, что нет никакого способа, чтобы второе сообщение могло быть доставлено его получателю. Для более подробного изучения того, что происходит, вы можете заменить IPAddress.Loopback более длинным маршрутом на ваш компьютер (например, иметь порт маршрутизации маршрутизатора 4411 на компьютер разработки и использовать внешний вид IP-адреса вашего модема) и контролировать этот порт в Вирешарке. Вот как выглядит вывод:

Захват экрана порта Wireshark

Порт 51380 является случайно выбранным портом, представляющим клиентский TcpClient в коде выше. Есть два пакета, потому что эта настройка использует NAT на моем маршрутизаторе. Итак, первый SYN-пакет – это мой компьютер -> мой внешний IP. Второй пакет SYN – мой маршрутизатор -> мой компьютер. Первый пакет PSH является первым серверомStream.Write. Второй пакет PSH является вторым серверомStream.Write.

Можно утверждать, что клиент делает ACK на уровне TCP с пакетом RST, но 1) это не имеет отношения к использованию TcpClient, поскольку это означает, что TcpClient является ACKing с закрытым соединением и 2) рассматривает, что происходит, когда соединение полностью отключен в следующем абзаце.

Если я прокомментирую строки, которые удаляют stream и закрывают клиент, и вместо этого отключитесь от моей беспроводной сети во время Thread.Sleep, консоль печатает тот же результат, и я получаю это от Wireshark:

Активность Wireshark при отключении беспроводной сети

В принципе, .Write возвращается без исключения, даже если пакет PSH даже не отправлен, не говоря уже о получении ACK.

Если я повторю вышеописанный процесс, но отключу свою беспроводную карту вместо того, чтобы просто отключиться, ТОГДА второй .Write выбрасывает исключение.

Итог, @ Ответ CodeCaster однозначно корректен на всех уровнях, и более одного из других ответов здесь неверны.

TcpClient использует протокол TCP, который сам гарантирует доставку данных. Если данные не будут доставлены, вы получите исключение. Если исключение не выбрасывается – данные были доставлены.

См. Описание протокола TCP здесь: http://en.wikipedia.org/wiki/Transmission_Control_Protocol

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

Поэтому TCP предлагает гарантированную доставку данных в том смысле, что вы всегда знаете, получил ли получатель ваши данные или нет

Итак, чтобы ответить на ваш вопрос, вам не нужно внедрять пользовательскую логику ACK при использовании TcpClient, поскольку она будет избыточной

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

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

Я согласен с Денисом.

Из документации (и моего опыта): этот метод будет блокироваться до тех пор, пока не будут записаны все байты или не будет выбрано исключение при ошибке (например, отключить). Если метод возвращается, вам гарантировано, что байты были доставлены и прочитаны другой стороной на уровне TCP .

Vojtech – Я думаю, вы пропустили документацию, так как вам нужно посмотреть на Stream, который вы используете.

См. Метод MSDN NetworkStream.Write в разделе примечаний:

Метод Write блокирует до тех пор, пока не будет отправлено запрошенное количество байтов или не будет выбрано SocketException.

Заметки

  • Обеспечение того, что сообщение было правильно прочитано приложением для прослушивания, является еще одной проблемой, и структура не может гарантировать этого.
  • В асинхронных методах (таких как BeginWrite или WriteAsync) это другая игра с мячом, так как метод возвращается немедленно, и механизм для обеспечения завершения отличается (EndWrite или завершение задачи в паре с кодом).