JmAbuDabi
@JmAbuDabi

Почему при tcp соединении постоянно приходит «мусор»?

Сообщение отправляю так:
protected TcpClient clientSocket;
       protected bool SendMessage(byte[] outStream)
        {
            try
            {
                clientStream = clientSocket.GetStream();
                clientStream.Write(outStream, 0, outStream.Length);
                clientStream.Flush();
            }
            catch
            {
                return false;
            }
            return true;
        }


Сообщение получаю так, метод класса обработчика клиента пришедшего на сервер.
protected bool ReceiveMessage()
        {
            int bytesRead = 0;
            try
            {
                byte[] header = new byte[4];
                bytesRead = clientStream.Read(header, 0, 4);
                if (bytesRead != 0)
                {
                    MemoryStream ms = new MemoryStream(header);
                    BinaryReader br = new BinaryReader(ms);
                    int messageLength = br.ReadInt32();
                    br.Close();
                    bytesRead = 0;
                    receiveMessage = new byte[messageLength];
                    bytesRead = clientStream.Read(receiveMessage, 0, messageLength);
                }
            }
            catch
            {
                //a socket error has occured
                return false;
            }
            if (bytesRead == 0)
            {
                //the client has disconnected from the server
                return false;
            }
            return true;
        }


Первые 4 байта это размер данных, потом сами данные. Вычитываю сколько был передан размер. Пакет переменной длины.
Дальше идёт разбор сообщения.
При трасировке кода Сервера и Клиента. Из одной виртуалки в другую приходят данные корректно. Но если "остпустить в свободное плаванье" т.е. запустить их на выполнение без всяких BreakPoint'ов, то вылетает ошибка на строчке receiveMessage = new byte[messageLength]; Либо Арифметическая ошибка, либо OutMemoryException.

Так, как почему-то переменная messageLength принимает то отрицательное(чаще всего одно и тоже) значение, а иногда положительное значение. Причем по модулю это значение очень огромно. При этом в сокете доступно для чтения ровно messageLength, если бы он был корректно прочитан. Получается, что информация доходит, но мусорная(и постоянно одинаковая).

Две машины под VirtualBox 4.2.28, пробовал на младшей, старшей. Всё одно. Host ОС Ubuntu 12.04.5.
У машин две сетевые, одна как NAT, вторая Host-Only.

UPDATE:

outStream формирую так:

byte[] outStream;
            using (MemoryStream mstream = new MemoryStream())
            {
                // Sending portion of File
                BinaryWriter bw = new BinaryWriter(mstream);
                bw.Write(12);                     //Command
//Вместо коментария пишем еще данных с помощью bw
                bw.Flush();
                int messageLength = (int)bw.BaseStream.Length;
                MemoryStream msOut = new MemoryStream();
                BinaryWriter bwOut = new BinaryWriter(msOut);
                bwOut.Write(messageLength);
                bwOut.Flush();
                mstream.WriteTo(msOut);
                outStream = msOut.ToArray();
                bwOut.Close();
            }
  • Вопрос задан
  • 792 просмотра
Решения вопроса 1
Neuroware
@Neuroware
Программист в свободное от работы время
Можно читать данные пока они есть, столько сколько их есть, не обязательно знать их длину. Рабочий пример (не для консоли, но сути не меняет)
Сервер
namespace TCP_Server
{
    public partial class Form1 : Form
    {
        public Form1()
        {
            InitializeComponent();
            MethodInvoker mi = new MethodInvoker(delegate
            {
                int port = 999; //взяли порт из файла
                TcpListener l = new TcpListener(port); //и на нем у нас висит сервер
                l.Start(); //запущен
                while (true)
                {
                    TcpClient cl = l.AcceptTcpClient();
                    Connection c = new Connection();
                    c.cl = cl;
                    Thread t = new Thread(new ThreadStart(c.Work));
                    t.IsBackground = false;
                    t.Start();
                }
            });
            mi.BeginInvoke(null, null);
        }

        private void Form1_Load(object sender, EventArgs e)
        {

        }

        public class Connection
        {
            public TcpClient cl;
            public void Work()
            {
                NetworkStream NWS = cl.GetStream();
                //BinaryReader R = new BinaryReader(NWS); //принятие
                //BinaryWriter W = new BinaryWriter(NWS); //отправка
                if (NWS.CanRead)
                {
                    StringBuilder fullServerReply = new StringBuilder();
                    byte[] readBuffer = new byte[cl.ReceiveBufferSize];
                    int numberOfBytesRead = 0;
                    do
                    {
                        numberOfBytesRead = NWS.Read(readBuffer, 0, readBuffer.Length);
                        fullServerReply.AppendFormat("{0}", Encoding.UTF8.GetString(readBuffer, 0, cl.ReceiveBufferSize));
                    } while (NWS.DataAvailable);
                    var s = fullServerReply.ToString();
                    Console.WriteLine(s); // тут твои данные
                }
            }
        }
    }
}


Клиент

using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.IO;
using System.Linq;
using System.Net.Sockets;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;

namespace TCP_Client
{
    public partial class Form1 : Form
    {
        public Form1()
        {
            InitializeComponent();
        }
        public int port = 999; //умолчание
        public string adres = "127.0.0.1"; //умолчание
        NetworkStream NWS;
        BinaryReader R;
        BinaryWriter W;
        private void Form1_Load(object sender, EventArgs e)
        {
            reg();
        }

        private void reg()
        {
            //создадим сокетное подключение для общения с сервером
            TcpClient client = new TcpClient(adres, port); //IP адрес сервера и порт на котором он висит
            NWS = client.GetStream();
            R = new BinaryReader(NWS); //поток для принятия данных
            W = new BinaryWriter(NWS); //поток для отправки данных

            //ну и тут ваше что-то
        }

        static byte[] GetBytes(string str)
        {
            byte[] bytes = new byte[str.Length * sizeof(char)];
            System.Buffer.BlockCopy(str.ToCharArray(), 0, bytes, 0, bytes.Length);
            return bytes;
        }

        static string GetString(byte[] bytes)
        {
            char[] chars = new char[bytes.Length / sizeof(char)];
            System.Buffer.BlockCopy(bytes, 0, chars, 0, bytes.Length);
            return new string(chars);
        }

        private void button1_Click(object sender, EventArgs e)
        {
            reg();
            W.Write(textBox1.Text);
            NWS.Flush();
        }
    }
}


Данные можно было бы упаковать в класс вместе с чек суммой и отправлять после сериализации, одновременно можно их еще и сжать (чтобы меньше пришлось передавать), вот пример реализации сериализации со сжатием:
string FileName = "Signes.db";
        public void Save()
        {
            //Сохраняем резервную копию
            if (File.Exists(FileName))
            {
                File.Copy(FileName, FileName + DateTime.Now.ToShortDateString() + ".bak", true);
            }
            BinaryFormatter bf = new BinaryFormatter();
            //откроем поток для записи в файл
            using (FileStream fs = new FileStream(FileName, FileMode.Create, FileAccess.Write, FileShare.ReadWrite))
            using (GZipStream gz = new GZipStream(fs, CompressionMode.Compress, false))
            {
                bf.Serialize(gz, Signes);//сериализация
            }
        }
        public void Load()
        {
            if (!File.Exists(FileName))
            {
                Signes = new List<BaseSign>(); //указать тип нового объекта
                Save();
            }
            BinaryFormatter bf = new BinaryFormatter();
            using (FileStream fs = new FileStream(FileName, FileMode.Open, FileAccess.Read, FileShare.Read))
            using (GZipStream gz = new GZipStream(fs, CompressionMode.Decompress, false))
            {
                Signes = (List<BaseSign>)bf.Deserialize(gz); //указать тип объекта
            }
        }
Ответ написан
Пригласить эксперта
Ваш ответ на вопрос

Войдите, чтобы написать ответ

Войти через центр авторизации
Похожие вопросы