seeseekey.net - Invictus Deus Ex Machina

Unter .NET kann man Assem­blies mit einem „Strong Name“ ver­se­hen. Die­ser sorgt dafür das dass Assem­bly ein­deu­tig iden­ti­fi­ziert wer­den kann. Möchte man einen sol­chen erstel­len so benö­tigt man zuerst ein Schlüs­sel­paar wel­ches mit dem „Strong Name Uti­lity“ ange­legt wird:

sn –k keypair.snk

Das Tool befin­det sich dabei im Win­dows SDK Ord­ner (z.B. C:\Program Files (x86)\Microsoft SDKs\Windows\v7.0A) wobei sich die Ver­sion des SDKs durch­aus unter­schei­den kann. Die erzeugte Snk-Datei wird dabei dem Pro­jekt hin­zu­ge­fügt. Anschlie­ßend stellt man in den Pro­jekt­ein­stel­lun­gen unter „Signing“ die ent­spre­chende Datei ein. In die­sem Tab ist es auch mög­lich eine neue Schlüs­sel­da­tei zu gene­rie­ren, so das man für den Schritt der Erzeu­gung nicht auf das „Strong Name Uti­lity“ ange­wie­sen ist.

Die Projektoptionen im Visual Studio

Die Pro­jek­top­tio­nen im Visual Studio

Bei der Signie­rung ist es wich­tig dar­auf zu ach­ten, das alle Biblio­the­ken eben­falls mit einem Strong Name ver­se­hen sind, sonst ver­wei­gert das Stu­dio die Signie­rung der Anwen­dung. Möchte man aus dem signier­ten Assem­bly den öffent­li­chen Schlüs­sel extra­hie­ren, so kommt wie­der das „Strong Name Uti­lity“ zur Anwendung:

sn -e Assembly.exe public.pk

Das Schlüs­sel­paar wel­ches man erzeugt hat, kann man dabei für alle eige­nen Anwen­dun­gen benut­zen. Es ist nicht nötig, für jede Anwen­dung ein eige­nes Schlüs­sel­paar zu erzeu­gen, da sich der „Sim­ple­Name“ bei jedem Assem­bly unterscheidet.

Wei­tere Infor­ma­tio­nen gibt es unter:
http://msdn.microsoft.com/en-us/magazine/cc163583.aspx
http://msdn.microsoft.com/en-us/library/h4fa028b%28v=vs.80%29.aspx

Mit der Instal­la­tion einer Ver­sion des Visual Stu­dios lan­den eine Menge Tools auf dem Rech­ner des Benut­zers. Eines der Tools wel­che dabei lei­der nicht auf der Platte lan­det, ist der CLR Pro­fi­ler, wel­cher auch aus dem Hause Micro­soft stammt.

Eine Auswertung des CLR Profilers

Eine Aus­wer­tung des CLR Profilers

Mit die­sem Tool kann die Inne­reien sei­ner .NET Soft­ware, wie die Spei­cher­brauch im Heap, die Hand­le­an­for­de­run­gen und ähn­li­ches ana­ly­sie­ren und aus­wer­ten. Her­un­ter­ge­la­den wer­den kann der CLR Pro­fi­ler unter http://www.microsoft.com/en-us/download/details.aspx?id=16273.

Wenn man mal auf der Suche nach einem klei­nen Zeit­ver­treib ist, kann man natür­lich so eini­ges aus­den­ken, so z.B. eine eigene CPU Archi­tek­tur. Dabei muss man diese nicht in Hard­ware gie­ßen, son­dern es reicht wenn man diese emu­liert. Und so ent­stand in eini­ger Zeit eine CPU wel­che auf den Namen „Struc­tura“ hört. Ein Design­ziel war es dabei, die CPU nur mit den nötigs­ten Maschi­nen­be­feh­len aus­zu­stat­ten. Das führte bei der „Struc­tura“ zu fol­gen­den Befeh­len (hier als Assem­bler Mne­mo­nics dargestellt):

  • [0] - JUMP
  • [1] - ADD
  • [2] - COPY

Das bedeu­tet das es unter ande­rem keine Nichtope­ra­tion wie z.B. NOOP bzw. NOP im Befehls­satz der CPU gibt. Der Grund dafür ist ganz ein­fach. Eine Nichtope­ra­tion lässt sich durch einen JUMP um null Byte emu­lie­ren, was im End­ef­fekt nur eine Erhö­hung des „Instruc­tion Coun­ters“ kurz „IC“ zur Folge hat. Da JUMP auf den Opcode 0 gelegt wurde führt, dies zu einer inter­es­san­ten Reak­tion der CPU wenn sie das Ende des Pro­gramms erreicht und ver­sucht den rest­li­chen Spei­cher zu inter­pre­tie­ren. Die CPU inter­pre­tiert das als Sprung an die Adresse 0 und beginnt mit dem Pro­gramm von vorne.

Der Befehls­auf­bau der Maschi­nen­be­fehle stellt sich dabei so da:

JUMP (Breite: 40 Byte)
  [Int64|Befehlswort - 0]
  [Int64|Adressinterpretation - 0/ANCTAAV 1/ACTAAV 2/RNCTAAV 3/RCTAAV]
      ANCTAAV - Adress not contains target adress as value
      ACTAAV - Adress contains target adress as value
      RNCTAAV - Register not contains target adress as value
      RCTAAV - Register contains target adress as value
  [Int64|Sprungbedingung - 0/NONE 1/ZERO 2/POS 3/NEG 4/OVF]
  [Int64|Sprungadressierung - 0/ABS 1/REL] 
  [Int64|Adresse oder Wert]

ADD (Breite: 32 Byte)
  [Int64|Befehlswort - 1]
  [Int64|Modus - 0/RAR 1/RANR 2/RAV] 
      RAR - Register and register
      RANR - Register and negative register
      RAV - Register and value
  [Int64|Register] 
  [Int64|Register oder Wert]

COPY (Breite: 40 Byte)
  [Int64|Befehlswort - 2] 
  [Int64|Modus - 0/NACTAAV 1/FACTAAV 2/SACTAAV 3/BACTAAV]
    NACTAAV - No adress contains target adress as value
    FACTAAV - First adress contains target adress as value
    SACTAAV - Second adress contains target adress as value
    BACTAAV - Both adress contains target adress as value
  [Int64|Menge an kopierenden Daten in Byte]
  [Int64|Register, Speicheradresse oder ZERO] 
  [Int64|Register oder Speicheradresse]

Jeder Opcode auf der CPU ist 8 Byte lang und wird als Int64 inter­pre­tiert. Die „Struc­tura“ ist mit den Regis­tern A bis Z aus­ge­stat­tet, was 26 All­zweck­re­gis­ter mit einer Breite von 8 Byte sind. Neben die­sen All­zweck­re­gis­tern besitzt es das Spe­zi­al­re­gis­ter IC bei wel­chem es sich um den „Instruc­tion Coun­ter“ han­delt und einige Flags mit den Namen „Zero“, „Posi­tive“, „Nega­tive“ und „Over­flow“. Beim Start beginnt die CPU mit der Aus­füh­rung des Pro­gramms ab der Adresse 0. Eine Beson­der­heit ist, das die CPU kei­nen Stack und keine Inter­rupts unterstützt.

Damit Peri­phe­rie ange­spro­chen wer­den kann, gibt es bestimmte Berei­che im Spei­cher in wel­che sich diese Geräte ein­blen­den. Der Bereich für Ein­blen­dun­gen ist dabei ab der Adresse 9.000.000.000.000.000.000 (7CE66C50E2840000) bis 9.223.372.036.854.775.807 (7FFFFFFFFFFFFFFF) zu finden.

Die Gra­fik­karte wird dabei ab der Adresse 9.000.000.000.000.000.000 bis ein­schließ­lich 9.099.999.999.999.999.999 ein­ge­blen­det. In den ers­ten 4096 Byte befin­det sich dabei der Kon­fi­gu­ra­ti­ons­block der Gra­fik­karte, wel­cher Infor­ma­tio­nen über die Auf­lö­sung und andere Ein­stel­lun­gen enthält.

Ab der Adresse 9.100.000.000.000.000.000 bis ein­schließ­lich 9100000000000008191 wird die Tas­ta­tur bzw. deren Ein­ga­be­puf­fer ein­ge­blen­det. Die ers­ten zwei Byte im Spei­cher der CPU sind dabei ein UInt16 wel­cher den aktu­el­len Zei­chen­in­dex der Tas­ta­tur in ihrem Tas­ta­tur­puf­fer angibt. Jede Ein­gabe auf der wird dabei vom Tas­ta­tur­ge­rät in Form eines Bytear­rays mit der Größe von fünf Byte über­tra­gen. Das erste Byte gibt dabei den Modi­fier an, die rest­li­chen 4 Bytes ent­hal­ten das erzeugte Zei­chen in Form eines UTF-32 Zeichens.

Der schematische Aufbau der CPU und deren Peripherie

Der sche­ma­ti­sche Auf­bau der CPU und deren Peripherie

Der Assem­bler für die CPU unter­stützt eine Reihe von Mne­mo­nics, wel­che anschlie­ßend in die Maschi­nen­spra­che über­setzt wer­den. Diese sind:

ABS [Register]
ADD [Register] [Register oder Wert]
CLR [Register oder ALL]
COPY [Int64|Menge an kopierenden Daten in Byte] [Register oder Speicheradresse] [Register oder Speicheradresse]
DIV [Register] [Register oder Wert]
LOAD [Speicheradresse] [Register]
DEC [Register]
INC [Register]
JUMP [Sprungbedingung|NONE|ZERO|POS|NEG|OVL] [Sprungadressierung|ABS|REL] [Adresse oder Wert]
MOD [Register] [Register oder Wert]
MUL [Register] [Register oder Wert]
NEG [Register]
NOOP
SHIFTL [Register] [Register oder Wert]
SHIFTR [Register] [Register oder Wert]
WRITE [Register] [Speicheradresse]

Beim Assem­bler han­delt es sich genau­ge­nom­men nicht um einen Assem­bler der rei­nen Lehre, son­dern um eine Kreu­zung aus Assem­bler, Macroas­sem­bler und Hoch­spra­che. Für die kom­ple­xe­ren Kom­man­dos wie „MUL“ oder „SHIFTL“ wer­den bestimmte Regis­ter wäh­rend der Berech­nung belegt (im Fall von „MUL“ z.B. das W, X, Y und Z Regis­ter). Die vom Kom­mando beleg­ten Regis­ter sind dabei in der Doku­men­ta­tion auf­ge­führt. Das führt natür­lich dazu das eine sol­che Mul­ti­pli­ka­tion nach der Auf­lö­sung aus sehr vie­len Befeh­len besteht:

COPY 8 Y A;
COPY 8 Z ZERO;
ADD Z 3;
COPY 8 X ZERO;
ADD Y 0;
JUMP POS REL 72;
JUMP ZERO REL 32;
ADD X 1;
ADD Z 0;
JUMP POS REL 72;
JUMP ZERO REL 32;
ADD X 1;
COPY 8 A ZERO;
ADD Y 0;
JUMP POS REL 184;
JUMP ZERO REL 144;
COPY 8 W Y;
ADD Y -W;
ADD Y -W;
COPY 8 W ZERO;
ADD Z 0;
JUMP POS REL 184;
JUMP ZERO REL 144;
COPY 8 W Z;
ADD Z -W;
ADD Z -W;
COPY 8 W ZERO;
ADD A Y;
ADD Z -1;
JUMP POS REL -104;
COPY 8 W X;
ADD W -2;
JUMP ZERO REL 368;
COPY 8 W X;
ADD W -1;
JUMP ZERO REL 112;
COPY 8 W X;
ADD W 0;
JUMP ZERO REL 144;
COPY 8 W A;
ADD A -W;
ADD A -W;
COPY 8 W ZERO;
COPY 8 X ZERO;
COPY 8 Y ZERO;
COPY 8 Z ZERO;

Wäh­rend der Ent­wick­lung gab es dabei einige inter­es­sante Ideen wie man bestimmte Sachen im Assem­bler lösen könnte. So zum Bei­spiel fol­gen­des Problem:

ADD A 555;  //Fülle den Register A mit dem Wert 555
COPY 8 B A; //Kopiere Register A zu B
DEC B; //Reduziere B um 1
COPY 8 *A B; //kopiere der Wert von B an die Adresse welche im A Register A definiert ist (555)
COPY 8 D *A; //Kopiere das was ab 555 steht in den Register D

Im D Regis­ter sollte nun der Wert 554 ste­hen. Mög­lich wird dies dadurch, das die CPU weiß das sie nicht den Wert nach A kopie­ren soll, son­dern den Wert wel­cher in A ent­hal­ten ist, als Ziel­adresse benutzt. Theo­re­tisch wäre dies auch anders lös­bar gewe­sen. So hätte der Assem­bler das * in selbst­mo­di­fiz­tie­ren­den Quell­code auf­lö­sen kön­nen. Aus einem „COPY 8 *A D“ wäre dann in etwa fol­gen­der Quell­text erzeugt worden:

COPY 8 (IC+32) A;
COPY B [IC+32];

Das Pro­blem an die­ser Geschichte ist das der Assem­bler bei die­ser Vari­ante sehr viel über die Inne­reien der CPU wis­sen muss. So muss z.B. genau defi­niert sein wann der IC erhöht wird, da die­ser für den selbst­mo­di­fi­zier­ten Code benö­tigt wird. Zur Erkä­rung: Die erste Zeile kopiert die Adresse wel­che in A liegt in die nächste Zeile und modi­fi­ziert somit den COPY Befehl wäh­rend der Lauf­zeit. Dadurch wird die Adres­sie­rung über den Regis­ter­wert möglich.

Inter­es­sant ist auch die Berech­nung eines Sprung­zie­les. Möchte man z.B. fol­gen­des Pro­gramm ausführen:

ADD A 7;
JUMP NONE REL -104

stellt sich die Frage wie man die rela­tive Sprung­weite aus­rech­net. Hier­bei kommt es auf die Breite der Befehle an. Bei den Grund­be­feh­len (ADD, COPY und JUMP) ent­spricht diese den in der Maschi­nen­co­de­be­schrei­bung ange­ge­be­nen Breite. Andere Befehle wie „MUL“ oder „SHIFTL“ zäh­len zu den kom­ple­xen Befeh­len, da diese im Maschi­nen­code aus meh­re­ren Anwei­sun­gen bestehen.

Hier besteht die ein­fachste Mög­lich­keit darin, den JUMP Befehl im ers­ten Moment mit Fan­ta­sie­wer­ten zu fül­len. Anschlie­ßend wird der Emu­la­tor mittels:

Structura.exe program.asm -disassemble -withIC

auf­ge­ru­fen. Bei der Dar­stel­lung mit­tels „withIC“ wird der Wert des ICs am Anfang des Befehls und am Ende des Befehls angezeigt.

(256/296)            JUMP ZERO REL 32;
(296/328)            ADD X 1;
(328/360)            ADD Z 0;

Möchte man nun also zum Befehl „ADD Z 0;“ sprin­gen so rech­net man 328-296 und hat so das rich­tige Sprung­ziel errechnet.

Und natür­lich fehlt auch noch eine Umset­zung in Hard­ware, aber bis das soweit ist, kann es natur­ge­mäß dau­ern. Bei der Struc­tura han­delt es sich natür­lich nicht um eine effi­zi­ente CPU, son­dern um eine bei wel­cher ver­sucht wurde, die CPU inter­nen Befehle auf ein Mini­mum zu begren­zen und das Design als sol­ches ein­fach zu hal­ten. Dies schlägt sich unter ande­rem darin nie­der, das jedes Befehls­wort 8 Byte lang ist, und somit für 3 Befehle über­di­men­sio­niert, aber dafür die Ein­fach­heit der Maschine intern gege­ben ist.

Und natür­lich erkauft man sich dies mit eini­gen Nach­tei­len, so sind die Bit­ver­schie­bungs­ope­ra­tion eigent­lich eine der schnells­ten in einer CPU. Durch die Emu­la­tion über zusätz­li­chen Assem­bler­code wird es eine der lang­sams­ten Ope­ra­tion. Aber eine CPU mit einem gro­ßen Befehls­satz zu emu­lie­ren, ist schließ­lich ein­fach und das war nicht der Sinn der Übung.

Anschauen kann man sich das unter der GPLv3 ste­hende Pro­jekt unter https://github.com/seeseekey/Structura/. Eine vor­kom­pi­lierte Ver­sion zum tes­ten gibt es hier zum Down­load. In der Imple­men­ta­tion befin­den sich sicher­lich noch einige Feh­ler auf wel­che mich gerne hin­ge­wie­sen wer­den darf. Jetzt müsste nur noch jemand Linux auf das Sys­tem por­tie­ren, wobei dies ohne wei­te­res nicht funk­tio­nie­ren sollte, das die CPU Dinge wie Inter­rupts nicht unter­stützt. Aller­dings wäre es natür­lich durch­aus mög­lich ein eige­nes mini­ma­les Betriebs­sys­tem für das emu­lierte Sys­tem zu schreiben.

Events unter C# sind so eine Sache. Im Nor­mal­fall funk­tio­nie­ren sie ohne Pro­bleme. Span­nend wird das ganze wenn man Events in einer Anwen­dung zwi­schen ver­schie­de­nen Thre­ads ver­schi­cken möchte. Dabei kann es näm­lich pas­sie­ren das Events ver­lo­ren gehen weil sie nicht emp­fan­gen wer­den. Abhilfe schafft hier die Klasse „ThreadSafeEvent“:

public class ThreadSafeEvent
{
  EventHandler internalEventHandler;
  readonly object internalEventHandlerLock=new object();

  public event EventHandler Event
  {
    add
    {
      lock(internalEventHandlerLock)
      {
        internalEventHandler+=value;
      }
    }
    remove
    {
      lock(internalEventHandlerLock)
      {
        internalEventHandler-=value;
      }
    }
  }

  public virtual void Fire(object sender, EventArgs e)
  {
    EventHandler handler;

    lock(internalEventHandlerLock)
    {
      handler=internalEventHandler;
    }

    if(handler!=null)
    {
      handler(sender, e);
    }
  }
}

Möchte man nun z.B. der Klasse „Ent­ries“ eine sol­ches Event hin­zu­fü­gen, so sieht das ganze wie folgt aus:

public ThreadSafeEvent EntrySelected=new ThreadSafeEvent();

Nun kann das Event gefeu­ert wer­den, das ganze wird mit­tels der „Fire“ Methode bewerk­stel­ligt. Die­ser über­gibt man den Sen­der und zusätz­li­che Argu­mente in Form eines „Even­tArgs“ bzw. einer davon abge­lei­te­ten Klasse.

EntrySelected.Fire(this, new EntryEventArgs(entry));

Jede Klasse wel­che das Event nun emp­fan­gen möchte hängt sich an das ent­spre­chende Event.

Entries entries=new Entries();
Entries.EntrySelected.Event+=OnEntrySelected;

...

private void OnEntrySelected(object sender, EventArgs e)
{
  EntryEventArgs args=(EntryEventArgs)e;
  Console.WriteLine(args.Entry);
}

Und schon haben wir in unse­rer Anwen­dung ein sau­be­res und thre­ad­si­che­res Eventsystem.

Im Nor­mal­fall hat es gute Gründe das man auf bestimmte Eigen­schaf­ten einer Klasse nicht zugrei­fen kann. Manch­mal ist es aber den­noch nütz­lich genau dies zu tun. So zum Bei­spiel bei der „Net­workStream“ Klasse wel­che im .NET/Mono Frame­work zu fin­den ist. Diese hat dabei die Eigen­schaft „Socket“ wel­che „pro­tec­ted“ ist. Möchte man nun doch auf diese Eigen­schaft zugrei­fen, so muss man zu etwas Magie in Form von Reflec­tion greifen:

NetworkStream stream;
...
PropertyInfo pi=stream.GetType().GetProperty("Socket", BindingFlags.Public|BindingFlags.NonPublic|BindingFlags.Instance);
Socket socket=(Socket)pi.GetValue(stream, null);

Hier holt man sich mit­tels Reflec­tion die Eigen­schaft und cas­tet sie. Anschlie­ßend hat man dann in der Varia­ble „socket“ die ent­spre­chende Eigen­schaft und kann mit die­ser dann machen was man möchte.

Vor eini­ger Zeit hatte ich einen Arti­kel dar­über geschrie­ben, was mich an Mono­De­ve­lop stört. Seit heute gibt es da noch eine wei­tere Sache. So kann man in Mono­De­ve­lop Aus­drü­cke und Varia­blen aus­wer­ten, was bei der Ent­wick­lung doch sehr prak­tisch ist.

Das Fenster zum Überwachen von Ausdrücken und Variablen

Das Fens­ter zum Über­wa­chen von Aus­drü­cken und Variablen

Pro­ble­ma­tisch wird es dann, wenn das Fens­ter dazu ver­lei­tet nach Feh­lern zu suchen, die eigent­lich nicht exis­tent sind. So kürzt es auto­ma­tisch den Wert der über­wach­ten Varia­ble ein. Selbst wenn man auf „Kopie­ren“ drückt, wird nur die gekürzte Fas­sung in die Zwi­schen­ab­lage gescho­ben. Dadurch kann es dann pas­sie­ren das man anstatt:

"ABCDEFGHIJKLMNOPQRSTUVWXYZ"

den Wert:

"ABCDEFGHIJKLMNOPQRS..."

aus­wer­tet. Möchte man an die­ser Stelle ganz sicher sein, so hilft nur der umständ­li­che Weg über die Lupe, in wel­cher der ganze Wert in einem extra Fens­ter ange­zeigt wird.

Bei Key­pass han­delt es sich um einen freien Pass­wort­ma­na­ger (GPL) wel­cher für Win­dows, Linux und Mac OS X ver­füg­bar ist. Unter Win­dows und Linux läuft das ohne Pro­bleme nur unter Mac OS X ist die Ober­flä­che nicht bedien­bar. Abhilfe schafft hier der Fork KeePassX.

Key­PassX in der neus­ten Alphaversion

Mit die­sem ist es in der neus­ten Alpha­ver­sion auch mög­lich die neue Ver­sion 2 der Daten­bank­da­teien zu lesen. Der ent­spre­chende Down­load dafür ist unter http://www.keepassx.org/dev/projects/keepassx/files zu finden.

Wei­tere Infor­ma­tio­nen gibt es unter:
http://de.wikipedia.org/wiki/KeePass
http://www.keepassx.org/news/2012/10/367

Bei Libtiff und libjpeg han­delt es sich um zwei oft ver­wen­dete Biblio­the­ken zum lesen von TIFF und JPG Dateien. Die Bibli­the­ken lie­gen dabei als C-Quelltext vor. Auf der Suche nach einer ent­spre­chen­den .NET Por­tie­rung bin ich auf der Seite http://bitmiracle.com/ fün­dig gewor­den. Dort gibt es .NET Por­tie­run­gen der Biblio­the­ken unter der „New BSD license“. Augen­schein­lich wer­den die Biblio­the­ken auch aktu­ell gehal­ten, so das der Abstand zwi­schen den Ori­gi­na­len nicht zu groß wird.

Wei­tere Infor­ma­tio­nen gibt es unter:
http://en.wikipedia.org/wiki/Libtiff
http://en.wikipedia.org/wiki/Libjpeg

Auf der Suche nach ein paar C#/CLR Interna bin ich auf die Web­seite „C# in Depth“ gesto­ßen, wobei man sagen muss das es sich dabei vor­ran­gig um ein Buch han­delt bzw. um die Web­seite zum gleich­na­mi­gen Buch han­delt. Aber auch die Web­seite gibt einige sehr inter­es­sante Infor­ma­tio­nen von sich, wel­che man vor allem in der „Arti­cles“ Sek­tion fin­det. So lege ich jedem, der sich für das Thema inter­es­siert, den Arti­kel Dele­ga­tes und Events ans Herz. Es gibt dort defi­ni­tiv die eine oder andere Erleuch­tung ;) Die Web­seite ist dabei unter http://csharpindepth.com zu finden.

Möchte man mit Webso­ckets unter .NET/Mono arbei­ten, so sollte man sich die ent­spre­chen­den Bibli­to­he­ken anschauen. Dabei gibt es die Aus­wahl zwi­schen eini­gen Bibliotheken:

Bei den jewei­li­gen Biblio­the­ken muss man dar­auf ach­ten, das die gewählte Biblio­thek den ent­spre­chen­den Webso­cket Stan­dard (den es mitt­ler­weile in 17 Revi­sio­nen gibt) unter­stützt. Ansons­ten funk­tio­nie­ren die Biblio­the­ken im gro­ßen und gan­zen ähn­lich, so das es sicher­lich auch eine Geschmacks­frage ist für wel­che Biblio­thek man sich entscheidet.

Wei­tere Infor­ma­tio­nen gibt es unter:
http://tools.ietf.org/html/rfc6455
http://de.wikipedia.org/wiki/WebSockets
http://en.wikipedia.org/wiki/WebSocket