Проблема с сокетом и сериализацией C # - PullRequest
0 голосов
/ 24 июня 2010

Я реализовал модель клиента и сервера, которая использует Socket с потоком

Когда я хочу передать только строку от клиента к серверу, это работает. Но я хочу передать объект, и он выдает эту ошибку: «Попытка десериализации пустого потока»

Вот код:


Клиент:

ASCIIEncoding asen = new ASCIIEncoding();

MemoryStream memoryStream = new MemoryStream();
BinaryFormatter binaryFormatter = new BinaryFormatter();
Command command = new Command("meuLogin", "minhaSenha");
binaryFormatter.Serialize(memoryStream, command);

stream.Write(memoryStream.ToArray(), 0, memoryStream.ToArray().Length);

Сервер:

byte[] message = new byte[4096];
int bytesRead = 0;
bytesRead = clientStream.Read(message, 0, 4096);

MemoryStream memoryStream = new MemoryStream(bytesRead);
BinaryFormatter bf1 = new BinaryFormatter();
memoryStream.Position = 0;

Command command = (Command)bf1.Deserialize(memoryStream); 

Еще один вопрос: я скопировал класс Command из Client и вставил на сервер для десериализации. Это правильно?

Спасибо

Ответы [ 6 ]

2 голосов
/ 24 июня 2010

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

2 голосов
/ 24 июня 2010

Вы никогда не используете сообщение, которое вы прочитали из потока. Таким образом, поток памяти, из которого вы читаете, пуст.

Кстати, почему вы используете эти промежуточные MemoryStreams?

1 голос
/ 24 июня 2010

Чтобы ответить на ваш второй вопрос: для максимальной удобства обслуживания класс Command должен находиться в отдельной сборке, на которую ссылаются и клиент, и сервер.

Чтобы ответить на ваш первый вопрос: вы пытаетесь десериализоваться из пустого потока на вашем сервере, как вам говорит исключение. Вам нужно скопировать байты, которые вы прочитали из clientStream в memoryStream, прежде чем десериализовать из memoryStream. В качестве альтернативы используйте clientStream напрямую, а не memoryStream; это может потребовать пересмотра вашего протокола.

Наконец, я искренне согласен с @Andrey: подумайте об использовании WCF. Это намного лучше, чем сырые розетки.

0 голосов
/ 06 сентября 2012

Вместо передачи bytesRead в MemoryStream, который фактически является длиной потока байтов, вы должны передать 'message', поскольку это фактический поток байтов.Например,

MemoryStream memoryStream = new MemoryStream(message);

Когда вы передаете целочисленную переменную, компилятор выдает исключение, что поток пустой.

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

0 голосов
/ 25 июня 2010

Еще один вопрос: я скопировал класс Command из клиента и вставил на сервер для десериализации. Это правильно?

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

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

После этого ...

Клиент:

static void StreamToServer(TcpClient client, Command obj) {
  using (NetworkStream ns = client.GetStream()) {
    using (MemoryStream ms = new MemoryStream()) {
      BinaryFormatter formatter = new BinaryFormatter();
      formatter.Serialize(ms, obj);
      byte[] buf = ms.ToArray();
      ns.Write(buf, 0, buf.Length);
    }
  }
}

Сервер:

static Command ReadStream(TcpListener listener) {
  Command obj = null;
  using (TcpClient client = listener.AcceptTcpClient()) { // waits for data
    using (NetworkStream ns = client.GetStream()) {
      byte[] buf = new byte[client.ReceiveBufferSize];
      int len = ns.Read(buf, 0, buf.Length);
      using (MemoryStream ms = new MemoryStream(buf, 0, len)) {
        BinaryFormatter formatter = new BinaryFormatter();
        obj = formatter.Deserialize(ms) as Command;
      }
    }
  }
  return obj;
}

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

0 голосов
/ 25 июня 2010

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

MemoryStream memoryStream = new MemoryStream(message, 0, bytesRead);

Однако я согласен со Стивеном и другими.Либо используйте WCF, либо используйте кадрирование сообщений.

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