C # Отправка нескольких объектов через один сокет - PullRequest
3 голосов
/ 11 апреля 2011

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

Клиент:

var buffer = new byte[1024];
var binFormatter = new BinaryFormatter();
using (var ms = new MemoryStream())
{
    int bytesRead;
    while ((bytesRead = _clientReceive.Receive(buffer)) > 0);
    {
        ms.Write(buffer, 0, bytesRead);
    } 

    ms.Position = 0;
    message = (Message)binFormatter.Deserialize(ms);
} 

сервер:

var gm = new Message { Message = "ObjectType", Object = Data };

using (var mem = new MemoryStream())
{
    var binFormatter = new BinaryFormatter();
    binFormatter.Serialize(mem, gm);
    var gmData = mem.ToArray();
    client.Send(gmData, gmData.Length, SocketFlags.None);
    client.Close();
}

, но если я хочу отправить несколько сообщений (поставьтеполный код клиента в цикле, а не вызов client.Close()), как я могу определить, когда клиент получил полный объект?Поместив код клиента в такой цикл:

while (_isReceivingStarted)
{
    var buffer = new byte[1024];
    var binFormatter = new BinaryFormatter();
    using (var ms = new MemoryStream())
    {
        int bytesRead;
        while ((bytesRead = _clientReceive.Receive(buffer)) > 0)
        {
            ms.Write(buffer, 0, bytesRead);
        }
        ms.Position = 0;
        message = (Message) binFormatter.Deserialize(ms);
    }
    // Do stuff then wait for new message
}

клиент будет зависать на _clientReceive.Receive(buffer), потому что он не получает Close () от клиента и никогда не получает 0 полученных байтов.Если я закрою соединение на сервере, оно будет зациклено, а затем произойдет ошибка в Deserialize MemoryStream вместо блокировки в _clientReceive.Receive(buffer) в ожидании отправки другого объекта.

Надеюсь, это имеет смысл.Есть указатели?

Ответы [ 5 ]

3 голосов
/ 11 апреля 2011

При использовании подобного сокета, я бы использовал что-то вроде protobuf-net вместо BinaryFormatter (предостережение / раскрытие: я написал) используя Base128 для стиля префикса (один из параметров) и некоторый известный тег (другой параметр), например 1, на другом конце вы можете просто использовать DeserializeItems<Foo>(...) (снова указав Base128 и 1) .

Это правильно выберет элементы , не пытаясь перечитать до тех пор, пока поток не закроется, когда он будет yield break. Каждый объект возвращается (yield return) отдельно, поэтому он не будет пытаться использовать весь поток перед тем, как предоставить вам данные.

При желании его также можно использовать с разнородными данными, но однороднее проще.

3 голосов
/ 11 апреля 2011

Я очень рекомендую посмотреть Windows Communication Foundation .Он будет обрабатывать всю эту сантехнику для вас, включая сериализацию и десериализацию ваших типов по проводам, через несколько настраиваемых каналов и т. Д.

1 голос
/ 11 апреля 2011

Вы можете отправить сообщение заголовка, которое сообщает получающей стороне, сколько байтов ожидать.Это похоже на директиву Content-Length в HTTP.Другой вариант - создать пользовательскую строку завершения, которую вы отправляете в конце (очевидно, она не может появляться побитно где-либо в двоичной полезной нагрузке сериализованных объектов, поэтому первое решение - это то, что я бы сделал).

0 голосов
/ 11 апреля 2011

Если вы хотите пойти по этому пути, вот несколько советов:

  • Отправка X байтов не гарантирует, что вы получите их за один прием () на стороне клиента. Вы могли бы иметь MemoryStream на клиенте и буфере. Используйте буфер для получения и записи в MemoryStream до тех пор, пока Receive не вернет 0.
  • Когда вы закончите отправку данных, используйте client.Shutdown (SocketShutdown.Send) (или .Both) перед вызовом Close (). Это предотвратит TCP RST на клиенте.
  • Если вы хотите сериализовать несколько объектов, просто сериализуйте их один за другим на сервере. Затем клиент буферизует все входящие данные, а когда Receive () возвращает 0, перемещает позицию в клиентском MemoryStream в 0 и начинает десериализацию объектов один за другим до тех пор, пока ms.Position == ms.Length.
0 голосов
/ 11 апреля 2011

Загляните в класс TcpListener:

http://msdn.microsoft.com/en-us/library/system.net.sockets.tcplistener.aspx

Или воспользуйтесь предложением Рида и загляните в WCF.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...