powered by simpleCommunicator - 2.0.53     © 2025 Programmizd 02
Форумы / WinForms, .Net Framework [игнор отключен] [закрыт для гостей] / Как лучше организовать передачу большого объема данных через сокеты по TCP?
11 сообщений из 11, страница 1 из 1
Как лучше организовать передачу большого объема данных через сокеты по TCP?
    #38985476
WinterGraveyard
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Гость
Возникла необходмость передавать большой объем данных (XML) через сокет. Сервер начинает передачу при приеме определенной команды - например, "getxmldata":
Код: c#
1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
14.
15.
16.
17.
18.
19.
20.
21.
22.
23.
24.
25.
26.
27.
28.
29.
30.
31.
32.
33.
34.
35.
36.
37.
38.
39.
40.
41.
42.
static readonly Encoding encoding = Encoding.GetEncoding(1251);
void StartAccepting()
{
  var ipAddress = IPAddress.Parse(Properties.Settings.Default.IPAddress);
  var localEndPoint = new IPEndPoint(ipAddress, Properties.Settings.Default.IPPort);
  using(var listener = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp))
  {
    listener.Bind(localEndPoint);
    listener.Listen(10);
    var handler = listener.Accept();
    while (true)
    {
      var sb = new StringBuilder();
      var dataPresent = false;
      while (handler.Available != 0)
      {
        var bytes = new byte[1024];
        var received = handler.Receive(bytes);
        var text = encoding.GetString(bytes, 0, received);
        sb.Append(text);
        dataPresent = true;
      }
      if (!dataPresent) continue;
      if ("getxmldata".Equals(sb.ToString().Trim('\0').Trim(), StringComparison.OrdinalIgnoreCase))
        SendXml(handler);
    }
  }
}

static void SendXml(Socket handler)
{
  var xdBytes = encoding.GetBytes(GetXmlData());
  const int bufferLength = 1024;
  var position = 0;
  while(position<xdBytes.Length)
  {
    handler.Send(xdBytes, position,
      position+bufferLength<xdBytes.Length ? bufferLength : xdBytes.Length-position,
      SocketFlags.None);
    position += bufferLength;
  }
}


Клиент, подключившись к серверу, и отправив команду, начинает прием XML:
Код: c#
1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
14.
15.
16.
17.
18.
19.
20.
21.
22.
23.
24.
25.
26.
27.
28.
29.
30.
31.
32.
33.
34.
35.
36.
37.
38.
39.
static readonly byte[] getDataMessage = Encoding.GetEncoding(1251).GetBytes("getxmldata");
static readonly Encoding transferEncoding = Encoding.GetEncoding(1251);
const int DataTimeout = 500;

void StartTcpClient()
{
  using(var client = new TcpClient())
  {
    var asyncResult = client.BeginConnect(
      IPAddress.Parse(Properties.Settings.Default.IPAddress),
      Properties.Settings.Default.IPPort,
      null, null);
    if (!asyncResult.AsyncWaitHandle.WaitOne(TimeSpan.FromSeconds(10.0)))
      throw new Exception("Превышено время ожидания отклика");
    client.EndConnect(asyncResult);
    using(var stream = client.GetStream())
    {
      stream.Write(getDataMessage, 0, getDataMessage.Length);
      var sw = new Stopwatch();
      sw.Start();
      while(!stream.DataAvailable)  // Между отсылкой сообщения, и появлением данных
        if (sw.Elapsed>DataTimeout) // есть некоторый промежуток, в котором stream.DataAvailable==false
          break;
      sw.Stop();
      var sb = new StringBuilder();
      var dataPresent = false;
      while(stream.DataAvailable)
      {
        var buffer = new byte[1024];
        var received = stream.Read(buffer, 0, buffer.Length);
        var text = transferEncoding.GetString(buffer, 0, received);
        sb.Append(text);
        dataPresent = true;
      }
      if (dataPresent)
        File.WriteAllText(Properties.Settings.Default.DataFile, sb.ToString());
    }
  }
}


На небольших объемах данных все работает прекрасно, но вот на больших (для тестирования использовался XML, в виде файла весящий 4,5 Мб) происходит следующее: во время отсылки данных сервером в промежутке между очередным Socket.Send, у клиента в цикле получения stream.DataAvailable становится равным False (хотя на сервере все еще крутится цикл отправки данных), клиент выходит из цикла приема данных (получив лишь часть XML - где-то 1/3), и закрывает соединение с сервером, в результате чего последний вылетает по SocketException (Удаленный хост принудительно разорвал существующее подключение) при попытке отослать очередную порцию данных.
Хотел сделать проверку в цикле на stream.DataAvailable тоже в течение какого-то промежутка (аналогично проверке после отсылки команды) - но может получиться так, что клиент отошлет две подряд команды достаточно быстро, и клиентский цикл, не дополучив до конца первый XML, начнет его дозаписывать данными начала второго XML - это не устраивает абсолютно. Второй вариант - дополнять данные маркером окончания передачи (каким-нибудь [EOF]), и проверять его наличие в очередной порции данных - но тогда при сбое сервера в процессе передачи клиенский цикл приема данных зависнет. Как тут лучше сделать?
...
Рейтинг: 0 / 0
Как лучше организовать передачу большого объема данных через сокеты по TCP?
    #38985623
Фотография Изопропил
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
WinterGraveyardКак тут лучше сделать?
использовать самый обыкновенный HTTP протокол или просто вебсервис(WCF)
и не заниматься низкоуровневым сокетом
...
Рейтинг: 0 / 0
Как лучше организовать передачу большого объема данных через сокеты по TCP?
    #38985631
WinterGraveyard
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Гость
Изопропил,

если бы это можно было сделать - я бы так и поступил. Но в моем случае выбора нет: сервер, отдающий данные - стороняя программа, вышеприведенный код - на коленке смоделированная ситуация (и в случае сторонней программы возникают всё те же ошибки при тех же условиях).
...
Рейтинг: 0 / 0
Как лучше организовать передачу большого объема данных через сокеты по TCP?
    #38985654
Dima T
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
В TCP данные приходят кусками, поэтому клиент не должен рвать соединение если принимать нечего. Надо просто ждать следующую порцию.
Ищи способ определить что данные получены полностью. Если это XML - жди завершающий тэг.

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

Чтобы намертво не повисло при зависании сервера можно сделать какой-то таймаут после которого останавливать прием на клиенте.
...
Рейтинг: 0 / 0
Как лучше организовать передачу большого объема данных через сокеты по TCP?
    #38985656
WinterGraveyard
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Гость
Т.е. в данном случае отпадают способы использования маркеров начала/окончания передачи - это я расфантазировался в процессе написания смоделированного сервера. Модификации подлежит только клиентский код, но вот как? Одна из идей - проверять уже считанные данные на предмет того, что они - well-formed XML, и если это так, то выходить из цикла чтения, а если не так, и NetworkStream.DataAvailable=false в течение некоторого времени, то считать, что был сбой в передаче, и делать попытку нового запроса данных. Единственное, что смущает - придется ведь уже всё считанное ранее целиком пытаться запихнуть в XmlReader, и на каждом шаге считывания объем проверяемых данных будет всё больше и больше - насколько это скажется на производительности в случае хотя бы вышеупомянутого тестового объема (4,5 Мб)?
...
Рейтинг: 0 / 0
Как лучше организовать передачу большого объема данных через сокеты по TCP?
    #38985664
Фотография Алексей К
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
WinterGraveyardХотел сделать проверку в цикле на stream.DataAvailable...

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

WinterGraveyardНо в моем случае выбора нет: сервер, отдающий данные - стороняя программа.Если автор не удосужился добавить в протокол информацию о границах блоков данных, то можешь читать, пока не получишь валидный XML. Вероятно за один раз может прийти несколько XML документов, это следует тоже учитывать.
...
Рейтинг: 0 / 0
Как лучше организовать передачу большого объема данных через сокеты по TCP?
    #38985701
Dima T
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
WinterGraveyardЕдинственное, что смущает - придется ведь уже всё считанное ранее целиком пытаться запихнуть в XmlReader, и на каждом шаге считывания объем проверяемых данных будет всё больше и больше - насколько это скажется на производительности в случае хотя бы вышеупомянутого тестового объема (4,5 Мб)?
Как вариант можно разбирать как строку. Найди открытие первого тэга и ожидай его закрытия.
...
Рейтинг: 0 / 0
Как лучше организовать передачу большого объема данных через сокеты по TCP?
    #38985878
WinterGraveyard
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Гость
Dima TКак вариант можно разбирать как строку. Найди открытие первого тэга и ожидай его закрытия.
Получилось даже немного проще: выяснилось, что закрывающий тэг всегда один и тот же, и он уникален в пределах документа. Поэтому получилось как-то так:
Код: c#
1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
14.
15.
16.
17.
18.
19.
20.
21.
22.
23.
24.
25.
26.
27.
28.
29.
30.
31.
32.
33.
34.
35.
36.
37.
38.
39.
40.
41.
42.
43.
44.
45.
46.
static readonly byte[] getDataMsg = Encoding.GetEncoding(1251).GetBytes("getxmldata");
static readonly Encoding transferEncoding = Encoding.GetEncoding(1251);
static readonly Regex endDataRegex = new Regex(@"\<\/root\s*\>", RegexOptions.Compiled);
const int DataTimeout = 2000;
static void StartTcpClient()
{
  using(var client = new TcpClient())
  {
    var asyncResult = client.BeginConnect(
      IPAddress.Parse(Properties.Settings.Default.IPAddress),
      Properties.Settings.Default.IPPort,
      null, null);
    if (!asyncResult.AsyncWaitHandle.WaitOne(TimeSpan.FromSeconds(10.0)))
      throw new Exception("Превышено время ожидания отклика");
    client.EndConnect(asyncResult);
    using(var stream = client.GetStream())
    {
      while(true)
      {
        stream.Write(getDataMsg, 0, getDataMsg.Length);
        var sw = new Stopwatch();
        sw.Start();
        while(!stream.DataAvailable)
          if (sw.ElapsedMilliseconds > DataTimeout)
            break;
        sw.Stop();
        var sb = new StringBuilder();
        while (true)
        {
          while(stream.DataAvailable)
          {
            var buffer = new byte[1024];
            var received = stream.Read(buffer, 0, buffer.Length);
            var data = transferEncoding.GetChars(buffer, 0, received);
            sb.Append(data);
          }
          if (endDataRegex.IsMatch(sb.ToString(sb.Length<=20 ? 0 : sb.Length-20, sb.Length<20 ? sb.Length : 20)))
            break;
        }
        File.WriteAllText(Properties.Settings.Default.DataFile, sb.ToString());
        Console.WriteLine("Press Esc to exit, any other key to continue");
        if (Console.ReadKey(true).Key == ConsoleKey.Escape) break;
      }
    }
  }
}


Спасибо.
...
Рейтинг: 0 / 0
Как лучше организовать передачу большого объема данных через сокеты по TCP?
    #39058276
DoomUnit
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
не хотелось новую тему создавать. вопрос первый - какой программой можно опросить тсп порт и узнать жив ли он? второе - какие есть хорошие инструкции как написать свой тсп клиент?
...
Рейтинг: 0 / 0
Как лучше организовать передачу большого объема данных через сокеты по TCP?
    #39058346
Фотография Cat2
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Модератор форума
DoomUnitне хотелось новую тему создавать. вопрос первый - какой программой можно опросить тсп порт и узнать жив ли он? второе - какие есть хорошие инструкции как написать свой тсп клиент?
А зря не хотелось.
В Правилах же написано - "Один вопрос - одна тема".
...
Рейтинг: 0 / 0
Как лучше организовать передачу большого объема данных через сокеты по TCP?
    #39058896
bazile
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
DoomUnitкакой программой можно опросить тсп порт и узнать жив ли он?
PsPing
...
Рейтинг: 0 / 0
11 сообщений из 11, страница 1 из 1
Форумы / WinForms, .Net Framework [игнор отключен] [закрыт для гостей] / Как лучше организовать передачу большого объема данных через сокеты по TCP?
Целевая тема:
Создать новую тему:
Автор:
Закрыть
Цитировать
Найденые пользователи ...
Разблокировать пользователей ...
Читали форум (0):
Пользователи онлайн (0):
x
x
Закрыть


Просмотр
0 / 0
Close
Debug Console [Select Text]