[C#] Dodanie integera i stringa do byte[]

0

Witajcie

Problem jest następujący - mam trzy zmienne, dwie Int32 (o nazwach msg_length i msg_id) oraz jeden string msg_text;
mam też buffer = byte[8+msg_text.Length] ( 8 dlatego, że są dwa Int32 zajmujące razem 8 bajtów, a msg_text.Length = bo jeden znak = jeden bajt).
I teraz pojawia się problem - o ile z obsługą pakietu przychodzącego (metoda Read w NetworkStreamie) nie ma problemu, to już z wysyłaniem takowego pojawia się.
Nie wiem bowiem jak zapisać do buffera Int32, albo msg_text (string). Int32 zajmie mi 4 bajty, więc nie moge po prostu napisać buffer[0] = msg_length, bo to tylko jeden bajt.

Ktoś mi pomoże?
Dzięki z góry, pozdrawiam.

0
baba napisał(a)

a msg_text.Length = bo jeden znak = jeden bajt

Ale to nie w stringu w C#. String zawiera tablicę znaków 16bitowych w UTF.

baba napisał(a)

Nie wiem bowiem jak zapisać do buffera Int32, albo msg_text (string). Int32 zajmie mi 4 bajty, więc nie moge po prostu napisać buffer[0] = msg_length, bo to tylko jeden bajt.

Żeby zamienić string na tablicę bajtów można użyć Encoding.ASCII.GetBytes():

string tekst = "Ala ma kota.";
byte[] bufor = Encoding.ASCII.GetBytes(tekst);

Ja bym kombinował tak, żeby skonwertować obie liczby i string, który już masz do nowego stringa, a potem przesłał. Tylko to pewno rodzi problemy przy odbiorze (jeśli się źle skonwertuje liczby).
0
somekind napisał(a)
baba napisał(a)

a msg_text.Length = bo jeden znak = jeden bajt

Ale to nie w stringu w C#. String zawiera tablicę znaków 16bitowych w UTF.

Owszem, string zawiera 2-bajtowe znaki. Ale dopóki nie potrzebujemy pełnego unicode'a, a zależy nam np. na oszczędności miejsca (jak to zazwyczaj ma miejsce przy byte[]), można spokojnie chary 2-bajtowe rzutować na pojedyncze byte'y.

        public static byte[] ToBytes(string s)
        {
            byte[] result = new byte[s.Length];
            for (int i = 0; i < s.Length; ++i) result[i] = (byte)s[i];
            return result;
        }

Z intem już zupełnie inaczej, jednak i do niego dużo lepiej napisać osobny konwerter, szczególnie, jeśli chcemy mieć wszystko binarnie, a nie w "plain text"...

        public static byte[] ToBytes(UInt32 i)
        {
            byte[] result = new byte[4];
            result[0] = (byte)(i % 256); i -= result[0];
            result[1] = (byte)(i % (256 * 256) / 256); i -= result[1]; ;
            result[2] = (byte)(i % (256 * 256 * 256) / (256 * 256)); i -= result[2];
            result[3] = (byte)(i / (256 * 256 * 256)); i -= result[3];
            return result;
        }

To chyba tyle...

0

Dzięki za odpowiedź.

Pojawił się kolejny problem - mianowicie nie wiem w jaki sposób, posiadając tablicę bajtów (byte[] buffer) o określonej wielkości odczytać z nich pierwsze cztery elementy i konwertować je na int32.
Mam wiec cztery elementy: buffer[0], buffer[1], buffer[2] i buffer[3].

Pobieram je ze strumienia, którym dane przepływają od klienta delphi. Zapisuje on je i wysyła w taki sposób

Buffer:=TMemoryStream.Create;
len:=26; // Jakiś przykladowy INT32 (to nie word, tylko integer
Buffer.WriteBuffer(len, 4); // Zapisuję do buffera, zajmuje 4 bajty (bo Int32)
form_main.ClientSocket.Socket.SendBuf(Buffer, 4); // Wysyłam buffer
Buffer.Free;

Rzecz jasna to tylko przykład, normalnie dodaje jeszcze trochę danych.

I teraz jak po stronie C# odczytać tego Inta? (w tym przykładzie musze jakoś zdobyć 26)
Pozdrawiam,

0

Jejku, po co pisać konwertery do wszystkiego, mimo, że są one już we frameworku?

using (System.IO.MemoryStream memoryStream = new System.IO.MemoryStream())
{
    int id = 666;
    string tekst = "teścik";

    byte[] tekstBytes = Encoding.GetEncoding(1250).GetBytes(tekst);
    byte[] lengthBytes = BitConverter.GetBytes(tekstBytes.Length);
    byte[] idBytes = BitConverter.GetBytes(id);

    memoryStream.Write(idBytes, 0, 4);
    memoryStream.Write(lengthBytes, 0, 4);
    memoryStream.Write(tekstBytes, 0, tekstBytes.Length);

    // odczyt
    memoryStream.Position = 0;

    byte[] idReadBuffer = new byte[4];
    memoryStream.Read(idReadBuffer, 0, 4);
    int idRead = BitConverter.ToInt32(idReadBuffer, 0);

    byte[] lengthReadBuffer = new byte[4];
    memoryStream.Read(lengthReadBuffer, 0, 4);
    int lengthRead = BitConverter.ToInt32(lengthReadBuffer, 0);

    byte[] tekstReadBuffer = new byte[lengthRead];
    memoryStream.Read(tekstReadBuffer, 0, lengthRead);
    string tekstRead = Encoding.GetEncoding(1250).GetString(tekstReadBuffer);
}
0

Dzięki za odpowiedzi jeszcze raz.
Ale jest pewien zonk - dajmy na to, mam mój NetworkStream (każdy klient ma inny).
Chce ODEBRAĆ te dane (Ty sam ustalileś id i tekst).

private static void HandleWelcomePacket(NetworkStream stream) { MemoryStream memoryStream = new MemoryStream(); byte[] buffer = new byte[4]; stream.Read(buffer, 0, 4); int id = BitConverter.ToInt32(buffer, 0); buffer = new byte[4]; stream.Read(buffer, 0, 4); int length = BitConverter.ToInt32(buffer, 0); buffer = new byte[512]; stream.Read(buffer, 0, 4); string tekst = BitConverter.ToString(buffer);
        byte[] tekstBytes = Encoding.GetEncoding(1250).GetBytes(tekst);
        byte[] lengthBytes = BitConverter.GetBytes(length);
        byte[] idBytes = BitConverter.GetBytes(id);
        memoryStream.Write(idBytes, 0, 4);
        memoryStream.Write(lengthBytes, 0, 4);
        memoryStream.Write(tekstBytes, 0, tekstBytes.Length);

        // odczyt
        memoryStream.Position = 0;

        byte[] idReadBuffer = new byte[4];
        memoryStream.Read(idReadBuffer, 0, 4);
        int idRead = BitConverter.ToInt32(idReadBuffer, 0);
        Console.WriteLine("ID pakietu: " + idRead.ToString()); // WYSKAKUJE KOSMICZNA LICZBA
        byte[] lengthReadBuffer = new byte[4];
        memoryStream.Read(lengthReadBuffer, 0, 4);
        int lengthRead = BitConverter.ToInt32(lengthReadBuffer, 0);

        byte[] tekstReadBuffer = new byte[lengthRead];
        memoryStream.Read(tekstReadBuffer, 0, lengthRead);
        string tekstRead = Encoding.GetEncoding(1250).GetString(tekstReadBuffer);
        Console.Write(tekstRead);
    }
Kod wysyłający dane z delphi
(id pakietu i teksT)
```delphi
procedure Send(id : integer; msg : string);
var Buffer:TMemoryStream;
var len, i : integer;
begin
Buffer:=TMemoryStream.Create;
msg := UTF8Encode(msg);
len:=Length(msg);
Buffer.WriteBuffer(len, 4);
Buffer.WriteBuffer(id, 4);
Buffer.Write(PCHAR(msg)^, len);
form_main.EntentaSocket.Socket.SendBuf(Buffer, 4);
form_main.EntentaSocket.Socket.Free;
Buffer.Free;
if(id = 800) then
begin
   form_main.Text.Text := '';
   form_main.Text.Focused();
end;
end;

nie bardzo wiem jak sie tym zająć...

0

Ale.. ja użyłem MemoryStream tylko po to, żeby mieć pod ręką jakikolwiek stream do pokazania jak można zapisywać i odczytywać dane bez tych żadnych pomocniczych metod.
Po co jeszcze raz pakujesz je do MemoryStream? Przecież możesz odebrać dane bezpośrednio z NetworkStream.

A dlaczego wychodzi taka dziwna liczba.. nie wiem. Podejrzyj jaką wartość ma bufor z tymi danymi. Być może Delphi w jakiś inny sposób wysyła dane.. Nie mam pojęcia.
Do tego ja założyłem, że ten tekst będzie przesłany w Windows-1250, ty go z Delphi wysyłasz w UTF8. Dobrze widzę? Nie znam kompletnie Delphi.

0

Poradziłem sobie z odczytem danych. Wielkie dzięki za pomoc - Twoj kod okazał się bardzo pomocny.

Pozdrawiam,

0

To ja jeszcze dopiszę dla potomności :)

A nie lepiej po prostu użyć BinaryReader/Writer na strumieniu? :)

1 użytkowników online, w tym zalogowanych: 0, gości: 1