none
Socketprogrammierung mit dem CompactFramework RRS feed

  • Frage

  • Hallo Leute,
    wenn ich mit meinem Socket
    mysocket = New Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp)
    

    mittels mysocket.Send(eine_lange_zeichenfolge)  wegsende, dann funkioniert das nur wenn die Zeichenfolge unterhalb einer bestimmten Größe bleibt.
    Ist die Zeichenfolge darüber, dann wird diese auf maximal 8760 Zeichen beschränkt.

    Die Eigenschaft mysocket.SendBufferSize (wie unter FW 3.5) steht mir im CF nicht zur Verfügung.

    Wie kann ich dieses Problem umgehen?

    Vielen Dank im Voraus und auch noch ein schönes Fest
    Christian Tauschek

     


    Christian Tauschek



    Samstag, 24. Dezember 2011 08:41

Antworten

  • Hallo Christian

    woran erkennst du, dass es auf 8760 Zeichen (Bytes?) beschränkt wurde?
    Gibt es (denn schon) beim Send eine Exception?
    Falls du dies jedoch auf der Empfangsseite (recv) beobachtest, dann ist dies schlicht  'by design' , denn TCP ist ein Stream-Protokoll, da gibt es keine 'Pakete' und auch keinen Zusammenhang zwischen der Länge bei Send und dem was bei Recv ankommt (wenn doch, dann ist dies rein 'zufällig' weil zB gerade optimal).
    TCP darf 'by design' völlig frei 'fragmentieren', solange die Fragmente garantiert und in der richtigen Reihenfolge per mehrmaligem Recv gelesen werden können (braucht also immer eigenen Code/Schleife).

    Weiter sprichst du von 'Zeichen', wobei TCP nur rohe Bytes überträgt. Wichtig ist die korrekte Rückwandlung nach Ascii/Ansi/Utf/Unicode-Encoding, wobei es hier für Multi-Byte Zeichencodes spezielle Empfangs-Logik braucht, damit nicht mitten drin fragmentiert wird!

    Das ganze gilt überall, C++, .NET und Linux, Mac usw, wäre absolute TCP-Grundlage.

    Samstag, 24. Dezember 2011 08:58
  •         Do
                countBytes = socket.Receive(bytes) 
                charlen += Encoding.UTF8.GetDecoder.GetChars(bytes, 0, countBytes, chars, charlen)


    Christian,

    der wesentliche Punkt ist wiederum falsch.
    Encoding.UTF8.GetDecoder gehört einmalig vor die Do-Schleife, GetChars muss dann in der Schleife gegen eben dieselbe Decoder-Instanz aufgerufen werden.
    Lies genannte  MSDN  und studiere die Problematik, die das dortige VB-Beispiel eigentlich aufzeigen will  (und dies auch sehr anschaulich macht, UTF-8 muss man aber eh kennen).

    PS zur Sicherheit, je nach Ablauf deiner Kommunikation:
    falls dein Sender zwei 'Objekt-Pakete' rasch nacheinander sendet (oder der Empfänger mal länger beschäftigt ist), kann es anstelle der Fragmentierung genauso (zufällig, by-design mit TCP-Streams) auch eine 'Teil-Fusion' geben, wodurch deine Funktion ReceiveObjektPaket  dann zeitweise  (über -EOF- hinaus) das zweite Objekt-Paket 'anfrisst'!

    Samstag, 24. Dezember 2011 23:44

Alle Antworten

  • Hallo Christian

    woran erkennst du, dass es auf 8760 Zeichen (Bytes?) beschränkt wurde?
    Gibt es (denn schon) beim Send eine Exception?
    Falls du dies jedoch auf der Empfangsseite (recv) beobachtest, dann ist dies schlicht  'by design' , denn TCP ist ein Stream-Protokoll, da gibt es keine 'Pakete' und auch keinen Zusammenhang zwischen der Länge bei Send und dem was bei Recv ankommt (wenn doch, dann ist dies rein 'zufällig' weil zB gerade optimal).
    TCP darf 'by design' völlig frei 'fragmentieren', solange die Fragmente garantiert und in der richtigen Reihenfolge per mehrmaligem Recv gelesen werden können (braucht also immer eigenen Code/Schleife).

    Weiter sprichst du von 'Zeichen', wobei TCP nur rohe Bytes überträgt. Wichtig ist die korrekte Rückwandlung nach Ascii/Ansi/Utf/Unicode-Encoding, wobei es hier für Multi-Byte Zeichencodes spezielle Empfangs-Logik braucht, damit nicht mitten drin fragmentiert wird!

    Das ganze gilt überall, C++, .NET und Linux, Mac usw, wäre absolute TCP-Grundlage.

    Samstag, 24. Dezember 2011 08:58
  • Hallo Thomas,
    danke für deine Antwort. (die 'Zeichen' sind natürlich Bytes - da war ich bei meinem Posting zu ungenau)

    Mittels

    countBytesFromClient = mysocket.Receive(bytes)
    

    habe ich bisher die Bytes eingelesen und auch ausgewertet, was bislang wegen der noch relativ kurzen Bytefolge auch ausreichend war.

    Jetzt mache ich es so, dass ich vom Client ein "<EOF>" mitschicke und in der Schleife solange bleibe bis ein solches "<EOF>" auftaucht.
    Mit meinem Schnelltest funktioniert es soweit ganz gut. Ich hoffe, dass das deinerseits auch so ok ist. Ansonsten bitte ich um Richtigstellung.

                            Dim countBytes As Integer
                            Dim strEmpfang As String = String.Empty
                            Do
                                countBytes = .Receive(bytes)
                                strEmpfang &= UTF8Encoding.GetString(bytes, 0, countBytes)
                            Loop Until strEmpfang.IndexOf("<EOF>") > -1
                            strEmpfang = strEmpfang.Replace("<EOF>", String.Empty)
    
    


    Die Geschichte mit dem Encoding usw. habe ich bei meiner Fragestellung zunächst absichtlich weggelassen, weil ich mit meiner Frage nur auf das Wesentliche eingehen wollte - was aber offensichtlich falsch war:-)

    mfg und ein schönes Fest
    Christian Tauschek

     





    Christian Tauschek
    Samstag, 24. Dezember 2011 10:28
  •                             strEmpfang &= UTF8Encoding.GetString(bytes, 0, countBytes)


    Christian,

    nur zur Sicherheit, diese Zeile mit UTF8Encoding.GetString wäre so eben gerade wie gesagt problematisch.
    (UTF-8 ist Multi-Byte, nur simple Ascii-7 Codes sind Single-Bytes. Als Bsp, bereits deutsche Umlaute sind 2 Bytes).

    Samstag, 24. Dezember 2011 10:39
  • Hallo Thomas,
    ich serialisiere Objekte sende diese übers Netz und deserialisiere diese mit dem XMLSerializer an der Gegenstelle wieder.
    Wenn ich nun das ASCIIEncoding verwende, dann enthalten Klassenmember (Strings) von den Objekten nach dem Deserialisieren nicht mehr die Sonderzeichen wie diese vor dem Serialisieren hatten.

    Nach dem Umstieg auf UTF8Encoding hatte ich das Problem nicht mehr, denn ich hatte urspünglich auch die ASCII-Encodierung verwendet.

    mfg
    Christian Tauschek

     

     


    Christian Tauschek
    Samstag, 24. Dezember 2011 13:10
  • Christian,

    UTF8Encoding selber wäre schon richtig (insbesondere mit XML uä die beste Wahl), aber du musst es dann für Socket-Streams auch noch richtig aufrufen!
    Mit deinem obigen Code besteht aber das Risiko, dass eben gerade 'Sonderzeichen'  (schon wieder) falsch rauskommen, bzw gar Exceptions.
    Und eigentlich noch schlimmer als je zuvor, da nicht vorhersehbar wegen der zufälligen TCP-Fragmentierung!
    Die Methode UTF8Encoding.GetString erwartet immer vollständige Zeichencodes (welche ja mehrere Bytes umfassen können), was aber durch die Fragmentierung bei Recv nicht mehr gewährleistet ist.

     

    Samstag, 24. Dezember 2011 13:27
  • Nachtrag:

    für deinen Fall ist die Decoder  Variante evtl besser geeignet, siehe auch Bsp Code unter

    http://msdn.microsoft.com/de-de/library/system.text.decoder.aspx

     

    Samstag, 24. Dezember 2011 13:45
  • Hallo Thomas,
    ich konnte es eben nicht lassen und habe deinen Vorschlag mit dem Decoder mal (hoffentlich richtig - wenn nicht, bitte um Info) umgesetzt.
    Im Prinzip funktioniert es so wie ich es mir vorstelle.

        Public Function ReceiveObjektPaket(ByVal socket As Socket) As String
            Dim bytes(ReceiveBufferSize) As Byte
            Dim chars(bytes.Length - 1) As Char
            Dim countBytes, charlen, iOfEOF As Integer
            Dim strChars As String
            Do
                countBytes = socket.Receive(bytes)  
                charlen += Encoding.UTF8.GetDecoder.GetChars(bytes, 0, countBytes, chars, charlen)
                strChars = CStr(chars)
                iOfEOF = strChars.IndexOf(EOF)
            Loop Until iOfEOF > -1
            Return strChars.Substring(0, iOfEOF)
        End Function
    

    mfg

    Christian Tauschek


    Christian Tauschek
    Samstag, 24. Dezember 2011 17:27
  •         Do
                countBytes = socket.Receive(bytes) 
                charlen += Encoding.UTF8.GetDecoder.GetChars(bytes, 0, countBytes, chars, charlen)


    Christian,

    der wesentliche Punkt ist wiederum falsch.
    Encoding.UTF8.GetDecoder gehört einmalig vor die Do-Schleife, GetChars muss dann in der Schleife gegen eben dieselbe Decoder-Instanz aufgerufen werden.
    Lies genannte  MSDN  und studiere die Problematik, die das dortige VB-Beispiel eigentlich aufzeigen will  (und dies auch sehr anschaulich macht, UTF-8 muss man aber eh kennen).

    PS zur Sicherheit, je nach Ablauf deiner Kommunikation:
    falls dein Sender zwei 'Objekt-Pakete' rasch nacheinander sendet (oder der Empfänger mal länger beschäftigt ist), kann es anstelle der Fragmentierung genauso (zufällig, by-design mit TCP-Streams) auch eine 'Teil-Fusion' geben, wodurch deine Funktion ReceiveObjektPaket  dann zeitweise  (über -EOF- hinaus) das zweite Objekt-Paket 'anfrisst'!

    Samstag, 24. Dezember 2011 23:44
  • Hallo Thomas,
    ich habe mir jetzt die Decoder-Sache nochmals genau angesehen und meine Funktion überarbeitet.
    Ich hoffe, dass diese jetzt auch in punkto Fragmentierung und Sonderzeichen richtig arbeitet, weil es sich jetzt um die gleiche Decoder-Instanz handelt.

    Die zweite Problematik (Client sendet 2 ObjektPakete hintereinander und es könnten daher vom 2. Paket Zeichen nach dem EOF des 1. Paketes 'weggefressen' werden) kann in meinem Fall nicht auftreten, weil bei meinem Programm der Server dem Client den Paketempfang bestätigen muss und erst nach dieser Bestätigung darf der Client wiederum EIN Paket senden.

    mfg

    Christian Tauschek

        Public Function ReceiveObjektPaket(ByVal socket As Socket) As String
            Dim bytes(ReceiveBufferSize) As Byte
            Dim chars(bytes.Length - 1) As Char
            Dim countBytes, charlen, iOfEOF As Integer
            Dim strChars As String
            Dim d As Decoder = Encoding.UTF8.GetDecoder
    
            Do
                countBytes = socket.Receive(bytes)
                charlen += d.GetChars(bytes, 0, countBytes, chars, charlen)
                strChars = CStr(chars)
                iOfEOF = strChars.IndexOf(EOF)
            Loop Until iOfEOF > -1
    
            Return strChars.Substring(0, iOfEOF)
        End Function
    

     

     


    Christian Tauschek
    Sonntag, 25. Dezember 2011 10:25