Соединения TCP / IP Socket в .NET

Для моего текущего проекта мне нужно запросить данные XML через соединение сокета tcp / ip. Для этого я использую класс TcpClient:

Dim client As New TcpClient()
client.Connect(server, port)

Dim stream As NetworkStream = client.GetStream()
stream.Write(request)
stream.Read(buffer, 0, buffer.length)

// Output buffer and return results...

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

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

Стоит ли изучать PHP в 2026-2027 годах?
Стоит ли изучать PHP в 2026-2027 годах?
Привет всем, сегодня я хочу высказать свои соображения по поводу вопроса, который я уже много раз получал в своем сообществе: "Стоит ли изучать PHP в...
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Приемы CSS-макетирования - floats и Flexbox
Приемы CSS-макетирования - floats и Flexbox
Здравствуйте, друзья-студенты! Готовы совершенствовать свои навыки веб-дизайна? Сегодня в нашем путешествии мы рассмотрим приемы CSS-верстки - в...
Тестирование функциональных ngrx-эффектов в Angular 16 с помощью Jest
В системе управления состояниями ngrx, совместимой с Angular 16, появились функциональные эффекты. Это здорово и делает код определенно легче для...
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
4
0
5 243
4
Перейти к ответу Данный вопрос помечен как решенный

Ответы 4

Ответ принят как подходящий

Вы создаете цикл для чтения.

Stream.Read возвращает int для байтов, которые он прочитал до сих пор, или 0, если достигнут конец потока.

Итак, это похоже на:

int bytes_read = 0;
while (bytes_read < buffer.Length)
   bytes_read += stream.Read(buffer, bytes_read, buffer.length - bytes_read);

Обновлено: теперь вопрос в том, как вы определяете размер буфера. Если ваш сервер сначала отправляет размер, ничего страшного, вы можете использовать приведенный выше фрагмент. Но если вам нужно читать, пока сервер не закроет соединение, вам нужно использовать try / catch (что является хорошей идеей, даже если вы знаете размер) и использовать bytes_read, чтобы определить, что вы получили.

int bytes_read = 0;
try
{
   int i = 0;
   while ( 0 < (i = stream.Read(buffer, bytes_read, buffer.Length - bytes_read) )
      bytes_read += i;
}
catch  (Exception e)
{
//recover
}
finally
{
if (stream != null)
   stream.Close();
}

Я так и сделал, но основной поток закрывался между чтениями. Как это объяснить?

Kevin Pang 03.10.2008 23:14

Поместите его в блок try / catch. Если поток закрыт, скорее всего, ваш сервер не работает должным образом.

Sunny Milenov 03.10.2008 23:17

Кроме того, я не знаю, сколько байтов я должен получать для каждого ответа.

Kevin Pang 03.10.2008 23:17

Сервер в порядке. Я отследил ответ с помощью программы, и она действительно отправляет полный ответ. Проблема в том, что соединение поддерживает только определенное количество байтов за передачу. После завершения передачи он закрывает базовый поток.

Kevin Pang 03.10.2008 23:18

Чтение может возвращать 0 в EOF - я думаю, было бы лучше проверить это в цикле, потому что либо он будет возвращать 0, либо выбросить.

Lou Franco 03.10.2008 23:18

Чтение не гарантирует полного чтения потока. Он возвращает количество фактически прочитанных байтов и 0, если байтов для чтения больше нет. Вы должны продолжать цикл, чтобы прочитать все данные из потока.

Это возможный способ сделать это и получить в «ответе» строку ответа. Если вам нужен массив байтов, просто сохраните ms.ToArray ().

string response;

TcpClient client = new TcpClient();
client.Connect(server, port);
using (NetworkStream ns = c.GetStream())
using (MemoryStream ms = new MemoryStream())
{
    ns.Write(request);

    byte[] buffer = new byte[512];
    int bytes = 0;

    while(ns.DataAvailable)
    {
        bytes = ns.Read(buffer,0, buffer.Length);
        ms.Write(buffer, 0, bytes);
    }

    response = Encoding.ASCII.GetString(ms.ToArray());
}

Я настоятельно рекомендую вам попробовать WCF для таких задач. Это дает вам после не столь крутой кривой обучения много преимуществ по сравнению с обычным обменом данными через сокеты. Для поставленной задачи, я согласен с предыдущими ответами, вы должны использовать цикл и динамически выделять память по мере необходимости.

Другие вопросы по теме