2008-09-18 12 views
7

Próbuję napisać klienta C# na serwerze napisanym w Javie. Serwer oczekuje 4-bajtowego (DataInputStread readInt() w języku Java) nagłówka komunikatu, a następnie aktualnej wiadomości.Wysyłanie 4-bajtowego nagłówka wiadomości z klienta C# do serwera Java

Jestem absolutnie nowy w C#, jak mogę wysłać ten nagłówek wiadomości do serwera Java? Próbowałem go na kilka sposobów (głównie próbę i błąd bez zbytniego zagłębiania się w język C#) i nic nie działało. Strona Java zakończyła się niepoprawną (bardzo dużą) długością wiadomości.

Odpowiedz

2

To proste, ale czy sprawdziłeś endianię? to może z łatwością być niedopasowanie pomiędzy endianness wysłaniu danych i endianness jesteś ODBIORCZA w

0

nie wiem C#, ale po prostu trzeba zrobić równowartość tego.

out.write((len >>> 24) & 0xFF); 
out.write((len >>> 16) & 0xFF); 
out.write((len >>> 8) & 0xFF); 
out.write((len >>> 0) & 0xFF); 
1

Jeśli będziesz wymieniać wiele danych, polecam zaimplementowanie (lub znalezienie) Stream-wrappera, który może pisać i czytać ints w porządku sieciowym. Ale jeśli naprawdę tylko trzeba napisać długość zrobić coś takiego:

using(Socket socket = ...){ 
    NetworkStream ns = new NetworkStream(socket);  
    ns.WriteByte((size>>24) & 0xFF); 
    ns.WriteByte((size>>16) & 0xFF); 
    ns.WriteByte((size>>8) & 0xFF); 
    ns.WriteByte(size  & 0xFF); 
    // write the actual message 
} 
11

Jest to, jak inne plakaty podkreśliło, aż do endianness.

Java DataInputStream oczekuje, że dane będą big-endian (kolejność bajtów sieciowych). Sądząc z dokumentacji Mono (dla odpowiedników takich jak BinaryWriter), C# zmierza w kierunku bycia little-endian (domyślnie dla Win32/x86).

Więc podczas korzystania z biblioteki standardowej klasy zmienić int 32bit „1” do bajtów, które produkują różne wyniki:

//byte hex values 
Java: 00 00 00 01 
    C#: 01 00 00 00 

Można zmienić sposób piszesz ints w C#:

private static void WriteInt(Stream stream, int n) { 
    for(int i=3; i>=0; i--) 
    { 
     int shift = i * 8; //bits to shift 
     byte b = (byte) (n >> shift); 
     stream.WriteByte(b); 
    } 
} 

EDIT:

bezpieczniejsze sposobem osiągnięcia tego celu byłoby:

private static void WriteToNetwork(System.IO.BinaryWriter stream, int n) { 
    n = System.Net.IPAddress.HostToNetworkOrder(n); 
    stream.Write(n); 
} 
2

Jak wszyscy tutaj już zauważyli, problem ten jest najprawdopodobniej spowodowany przez aplikację C# wysyłającą adresy w małej kolejności, podczas gdy aplikacja Java oczekuje ich w kolejności sieciowej (big-endian). Jednak zamiast jawnie przestawiać bajty w aplikacji C#, właściwym sposobem jest poleganie na wbudowanych funkcjach do konwersji z hosta na kolejność sieci (htons i polubienia) - w ten sposób twój kod będzie działał dobrze nawet podczas uruchamiania na maszynie big-endian.

Ogólnie rzecz biorąc, podczas rozwiązywania takich problemów uważam, że warto rejestrować poprawny ruch (np. Java na Java w twoim przypadku) przy użyciu narzędzi takich jak netcat lub wireshark, a następnie porównać go z nieprawidłowym ruchem, aby zobaczyć, gdzie to jest idzie źle. Dodatkową korzyścią jest to, że możesz również użyć netcata do wstrzyknięcia przechwyconych/wstępnie zarejestrowanych żądań do serwera lub wstrzyknięcia przechwyconych/wstępnie nagranych odpowiedzi do klienta. Nie wspominając już o tym, że można również zmodyfikować żądania/odpowiedzi w pliku i przetestować wyniki przed przystąpieniem do naprawy kodu.

0

Klasa Sysetm.Net.IPAddress ma dwie statyczne metody pomocnicze: HostToNetworkOrder() i NetworkToHostOrder(), które wykonują konwersję. Możesz użyć go z BinaryWriter nad strumieniem, aby zapisać prawidłową wartość:

using (Socket socket = new Socket()) 
using (NetworkStream stream = new NetworkStream(socket)) 
using (BinaryWriter writer = new BinaryWriter(stream)) 
{ 
    int myValue = 42; 
    writer.Write(IPAddress.HostToNetworkOrder(myValue)); 
}