Проблема с сокетами и ошибкой OutOfMemory - PullRequest
2 голосов
/ 02 июля 2011

У меня огромная проблема.Попытка создать приложение, которое должно состоять из двух частей: серверной и клиентской.Эти две части должны как-то общаться и обмениваться объектами.Я решил использовать сокеты, потому что я не знаком с WCF и могу тестировать обе части на одном компьютере (просто поместите их для прослушивания по адресу 127.0.0.1).

Теперь, когда я пытаюсь отправитьнекоторый "пользовательский" сериализуемый объект от клиента я получил исключение "OutOfMemory" на стороне сервера!Я читал о Sockets, способах отправки / получения объектов, я пробовал какой-то код, который нашел в сети, но не повезло!Я понятия не имею, что не так с моим кодом.Вот мой код:

Это тестовый класс, определенный в коде обеих сторон:

[Serializable]
    class MyClass
    {
        public string msg="default";
    }

Код отправки на стороне клиента (работает нормально):

private void cmdSendData_Click(object sender, System.EventArgs e)
    {
        try
        {
            MyClass test = new MyClass();

            NetworkStream ns = new NetworkStream(m_socWorker); //m_socWorker is socket
            BinaryWriter bw = new BinaryWriter(ns);

            MemoryStream ms = new MemoryStream();
            BinaryFormatter bf = new BinaryFormatter();
            bf.Serialize(ms, test);

            bw.Write(ms.ToArray());
            MessageBox.Show("Length is: " + ms.ToArray().Length); //length is 152!
            ns.Close();
        }
        catch(System.Net.Sockets.SocketException se)
        {
            MessageBox.Show (se.Message );
        }
    }

Серверкод стороны (тот, который вызывает проблемы):

public  void OnDataReceived(IAsyncResult asyn)
    {
        try
        {
            CSocketPacket theSockId = (CSocketPacket)asyn.AsyncState ;

            NetworkStream ns = new NetworkStream(m_socWorker);
            byte[] buffer = new byte[1024];
            ns.Read(buffer, 0, buffer.Length);

            BinaryFormatter bin = new BinaryFormatter();
            MemoryStream mem = new MemoryStream(buffer.Length);

            mem.Write(buffer, 0, buffer.Length);
            mem.Seek(0, SeekOrigin.Begin);
            MyClass tst = (MyClass)bin.Deserialize(mem); //ERROR IS THROWN HERE!

            MessageBox.Show(tst.msg);

            theSockId.thisSocket.EndReceive(asyn);

            WaitForData(m_socWorker);
        }
        catch (ObjectDisposedException )
        {
            System.Diagnostics.Debugger.Log(0,"1","\nOnDataReceived: Socket has been closed\n");
        }
        catch(SocketException se)
        {
            MessageBox.Show (se.Message );
        }
    }

Итак, я получил исключение при попытке десериализации.Понятия не имею, что не так.

Я угрожал своему коду: «Если вы будете продолжать вызывать проблемы, я сообщу вам о парнях из StackOverflow», так что вот я:)

Ответы [ 2 ]

1 голос
/ 02 июля 2011

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

  1. Вы создаете буфер размером 1024 байта и читаете в негоне проверяя, сколько на самом деле было прочитано;если входящий буфер имеет только 56 байтов, вы будете читать только 56 байтов (если только вы не используете блокирующее чтение на самом сокете, которое может истечь по тайм-ауту).Аналогично, в вашем буфере может быть 2000 байт, а это значит, что в буфере останется 976 байт, которые не будут обработаны, пока вы не получите больше данных.Это может быть целый объект, и клиент может ожидать ответа на него, прежде чем отправит больше.
  2. Затем вы берете этот буфер и копируете его снова в MemoryStream.Вместо этого просто возьмите перегрузку конструктора MemoryStream, который принимает буфер.Вы не будете копировать данные таким образом.
  3. Вы звоните EndReceive после того, как вы обработали входящие данные;хотя в действительности это не может привести к ошибке, это не соответствует духу Begin / End асинхронного шаблона старого стиля.Вы должны позвонить EndXXX в начале вашего обратного вызова, чтобы получить результат.

Я понимаю, что это не прямой ответ на ваш вопрос, но вам действительно нужнопересмотреть ваше решение не использовать WCF.

Я был в той же лодке, что и вы пару месяцев назад;Я раньше не пользовался WCF и не удосужился посмотреть, как в нем все работает.Для меня это был очень большой черный ящик, и я осуществлял связь на основе сокетов на других платформах, поэтому это было известное количество.Оглядываясь назад, мой выбор сделать шаг в WCF был лучшим решением, которое я мог принять.После того, как вы обдумаете некоторые понятия (привязки, контракты и способы использования различных атрибутов), разработка сервиса станет простой, и вам не придется тратить время на написание сантехники.

NetTcpBinding предоставляет привязку на основе TCP, которая может поддерживать долгоживущие соединения и сеансы (как я это использую), и даже заботится о сообщениях поддержки активности, чтобы поддерживать соединение открытым через надежные сеансы.Это так же просто, как включить флаг.Если вам нужно что-то более совместимое (то есть кроссплатформенное), вы можете написать собственную привязку, которая делает это и сохраняет ваш код как есть.

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

1 голос
/ 02 июля 2011

Там есть какой-то очень странный код:

  • предполагает, что мы читаем 1024 байта без проверки
  • копирует 1024 буфера
  • предполагает, что сериализованные данные равны 1024байт, не больше, не меньше
  • десериализуется из того, что

IMO есть ваша ошибка;Вы должны читать правильное количество байтов из потока (обычно в цикле).Как правило, вы будете зацикливаться, проверяя возвращаемое значение от Read до или мы прочитали количество данных, которое мы хотели, или мы получим EOF (return <= 0). </p>

или лучше;используйте сериализаторы, которые делают это для вас ... Например, protobuf-net имеет SerializeWithLengthPrefix и DeserializeWithLengthPrefix, которые обрабатывают все проблемы длины для вас.

Поскольку вы упоминаете «пользовательскую» сериализацию - если вы реализуете ISerializable, этоТакже возможно, что проблема там - но мы не можем увидеть это без кода.Кроме того, текущий буфер / поток настолько поврежден (извините, но это так), что я все равно сомневаюсь, что он так далеко зашёл.

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