Wie funktioniert ein Webchat?

Wie viel Auf­wand ist es, einen offe­nen Chat­room mit tem­po­rä­rem Nut­zer­ma­nage­ment zu ent­wi­ckeln?

Die­se Fra­ge wur­de mir über das Kon­takt­for­mu­lar und mehr­fach auf “gutefrage.net” gestellt. Und da Erfah­rungs­wer­te die bes­ten Wer­te sind, habe ich spon­tan ein tech­ni­sches Expe­ri­ment gestar­tet, und einen ein­fa­chen Chat­room pro­gram­miert. Der fol­gen­de Arti­kel ist kein Tuto­ri­al zur Pro­gram­mie­rung eines Cha­tes und ent­hält daher kei­ne Code-Beispiele. Viel­mehr gehe ich rela­tiv aus­führ­lich auf die ver­schie­de­nen Mecha­nis­men, ver­wen­de­ten Tech­ni­ken und Architektur-Modelle (m)einer Chat­platt­form ein. Infor­ma­ti­sches Fach- oder Vor­wis­sen wird daher nicht benö­tigt. Das Experiment-Ergebnis fin­det man unter chat.mcjh-medien.de

Theorie:

  1. Grund­struk­tur und Kom­po­nen­ten
    Chat­rooms haben eine ser­ver­sei­ti­ge und eine cli­ent­sei­ti­ge Kom­po­nen­te. All­ge­mein sind Ser­ver Kno­ten­punk­te in Netz­wer­ken, die Ver­bin­dun­gen zu ande­ren Kno­ten­punk­ten her­stel­len und Funk­tio­na­li­tä­ten anbie­ten kön­nen. Cli­ents sind Nut­zer­ma­schi­nen, die über ein Netz­werk von Ser­vern mit­ein­an­der kom­mu­ni­zie­ren und/oder deren Funk­tio­na­li­tä­ten in Anspruch neh­men. Im Fal­le des Chat­rooms benö­ti­gen wir einen Ser­ver für die Hand­ha­bung der Nach­rich­ten und Nut­zer­ver­wal­tung und einen Cli­ent, der den Chat­room als Funk­tio­na­li­tät im Netz­werk “Inter­net” ver­wen­den kann.
  2. Kom­mu­ni­ka­ti­on
    Die bei­den Kom­po­nen­ten müs­sen mit­ein­an­der kom­mu­ni­zie­ren. Hier­zu muss ein Kom­mu­ni­ka­ti­ons­pro­to­koll fest­ge­legt wer­den. Aktiv kom­mu­ni­ziert wird aus­schließ­lich sei­tens des Cli­ents mit­tels Ajax-Requests (Request=“Anfrage”). AJAX ist eine Abkür­zung für “Asyn­ch­rou­nus Java­script And XML” und ermög­licht es, nach dem Laden einer Sei­te wei­te­re Infor­ma­tio­nen nach­zu­la­den und Infor­ma­tio­nen an den Ser­ver zu schi­cken. Als Schnitt­stel­le benö­tigt der Ser­ver eine Adres­se, an die der Cli­ent sei­ne AJAX-Nachrichten schickt. Sie darf aus­schließ­lich im Kom­mu­ni­ka­ti­ons­pro­to­koll defi­nier­te Nach­rich­ten ver­ar­bei­ten und ent­hält, wenn ein Nut­zer sie auf­ruft, kei­ne Inhal­te.
    Um eine Ver­wir­rung zu ver­hin­dern, möch­te ich an die­ser Stel­le kurz ein paar Ter­mi­ni defi­nie­ren.
    NACHRICHTEN sind Pake­te, die zwi­schen Cli­ent und Ser­ver im Zuge des Kom­mu­ni­ka­ti­ons­pro­to­kolls ver­schickt wer­den. Eine Nach­richt, die der Nut­zer im Chat absen­det, wird von mir als Chat­nach­richt bezeich­net und in einer Nach­richt vom Cli­ent zum Ser­ver geschickt.
  3. Server-Verhalten
    Der Ser­ver führt die “Daten­bank” und Logik zur Nutzer- und Chat­ver­wal­tung. Es gibt bei mei­nem Vor­ha­ben kei­ne Nut­zer­ac­counts, son­dern nur tem­po­rär ver­ge­be­ne Nut­zer­na­men. Es soll kein Name mehr­mals gleich­zei­tig ver­wen­det wer­den kön­nen. Auf eine vali­de Anfra­ge des Cli­ents ant­wor­tet der Ser­ver mit einer im Kom­mu­ni­ka­ti­ons­pro­to­koll fest­ge­leg­ten Ant­wort. Der Ser­ver muss in der Lage sein, vor dem Ver­ar­bei­ten von Nach­rich­ten die Inhal­te auf bös­ar­ti­gen Code zu prü­fen und die­sen gege­be­nen­falls her­aus­zu­lö­schen. Wenn ein Cli­ent sich über 2 Minu­ten nicht mehr beim Ser­ver zurück­mel­det, soll der Nut­zer­na­me wie­der frei­ge­ge­ben wer­den. Beim Aus­log­gen wird der Nut­zer­na­me wie­der frei­ge­ge­ben.
  4. Client-Verhalten
    Der Cli­ent soll in der Lage sein, bei einer neu­en Chat­nach­richt, die von einem ande­ren Nut­zer gesen­det wur­de, einen von drei wähl­ba­ren Benach­rich­ti­gungs­tö­nen abzu­spie­len. Die Laut­stär­ke soll ein­stell­bar sein. Zudem soll er mög­lichst latenz­frei neue Chat­nach­rich­ten vom Ser­ver laden. Optio­nal soll der Chat­ver­lauf nach Emp­fang neu­er Chat­nach­rich­ten nicht nach unten scrol­len. Eine Nut­zer­lis­te zeigt an, wie vie­le Leu­te gera­de im Chat­room ange­mel­det sind und wie sie hei­ßen. Wäh­rend der Lade­vor­gän­ge soll der Nut­zer einen klei­nen Lade­krin­gel sehen. Wäh­rend des Tip­pens soll der Nut­zer und auch alle ande­ren Nut­zer ange­zeigt bekom­men, dass man tippt. Nach­rich­ten, die aus­schließ­lich aus Leer­zei­chen bestehen, sol­len weder abge­schickt wer­den kön­nen, noch soll beim Leerzeichen-Spam das “Typing”-Symbol für die Nut­zer ange­zeigt wer­den. Das Absen­den einer Nach­richt soll sowohl durch Knopf­druck als auch durch das Drü­cken der Enter-Tasten mög­lich sein.
  5. Sicher­heit
    Um Website-Aufrufe einem ein­deu­ti­gen Nut­zer zuord­nen zu kön­nen, weist die von mir ver­wen­de­te Script­spra­che PHP jedem Nut­zer eine Session-ID zu. Die­se besteht — abhän­gig vom Ser­ver — aus bis zu 40 Zei­chen des eng­li­schen Alpha­bets und der ara­bi­schen Zah­len und ist damit sehr schwer errat­bar. Ein zufäl­lig gene­rier­ter Code pro Nut­zer­na­me soll zusätz­lich ver­hin­dern, dass nicht ein­ge­logg­te Nut­zer durch Raten der Session-ID Chat­nach­rich­ten im Namen von ande­ren schrei­ben kön­nen. Eine Chat­nach­richt wird nur dann vom Ser­ver akzep­tiert, wenn Session-ID, Nut­zer­na­me und Code der Nach­richt nach­weis­lich zuein­an­der gehö­ren.
    Damit fin­di­ge Nut­zer nicht die Text­da­ten­hal­tung aus­fin­dig machen und dort die Infor­ma­tio­nen von aktu­ell akti­ven Nut­zern aus­le­sen kön­nen, wird das ent­spre­chen­de Ver­zeich­nis für exter­ne Zugrif­fe blo­ckiert.

Erwar­te­te Arbeits­zeit für das Pro­jekt: 2 bis 3 Arbeits­ta­ge à 8 Stun­den.

Praxis

  1. Der Ser­ver wird mit­tels der Script­spra­che PHP und einer Daten­hal­tung in Text­da­tei­en umge­setzt. Dies ist defi­ni­tiv kei­ne effi­zi­en­te Lösung, genügt aber für die Durch­füh­rung des Expe­ri­men­tes, da die­ser Chat nicht den Anspruch hat, meh­re­re tau­send Nut­zer gleich­zei­tig zu ver­wal­ten. Zur Umset­zung des Pro­jek­tes bie­tet sich die soge­nann­te MVC-Struktur an. MVC steht für “Model, View, Con­trol­ler” und beschreibt ein Architektur-Konzept in der Software-Entwicklung. Im Model geschieht die Daten­hal­tung, die View erzeugt die Benut­zer­ober­flä­che und der Con­trol­ler ist das Bau­teil dazwi­schen, um Ein­ga­ben von der View in das Model zu trans­por­tie­ren.
    Der Cli­ent wird im Brow­ser des Nut­zers mit HTML und CSS dar­ge­stellt. Die HTML-Struktur erzeugt die View des Ser­vers bei Auf­ruf der Sei­te. Für die Logik im Cli­ent ist Java­script ver­ant­wort­lich. Die Ein­stel­lun­gen zu Benach­rich­ti­gungs­ton und Scroll­ver­hal­ten wer­den im soge­nann­ten “local­S­to­ra­ge” des Browser-Fensters mit­tels Java­script abge­spei­chert. So kann garan­tiert wer­den, dass die Ein­stel­lun­gen auch nach einem Rel­oad der Sei­te noch vor­han­den sind.
  2. Für die Kom­mu­ni­ka­ti­on wer­den sie­ben Nach­rich­ten­ty­pen fest­ge­legt: “heart­beat”, “check­for­news”, “check­fo­ru­sers”, “use­ris­ty­ping”, “messa­ge”, “pro­to­col” und “suc­cess”.
    Die letz­ten bei­den Nach­rich­ten­ty­pen sind gene­ri­sche Bau­tei­le, die nicht wei­ter rele­vant sind und nur für mög­li­che Erwei­te­run­gen defi­niert wur­den.
    Die Nach­rich­ten zwi­schen Cli­ent und Ser­ver wer­den im soge­nann­ten JSON-Format ver­schickt. Dies ist ein für Mensch und Maschi­ne ein­fach les­ba­res und schlan­kes Text­da­ten­for­mat. Aus­ge­schrie­ben könn­te eine JSON-Nachricht bei­spiels­wei­se so aus­se­hen:
    {"type":"heartbeat", "username":"Max Mustermann", "timestamp":"12345678"}
    

    Jede Nach­richt ent­hält ein Attri­but “type” und typ­ab­hän­gi­ge wei­te­re Attri­bu­te.

    • “heart­beat” sen­det alle 30 Sekun­den (1÷30 Hz) eine Nach­richt an den Ser­ver. Die­se Nach­richt trans­por­tiert einen Nut­zer­na­men und einen Zeits­tem­pel und mel­det den ent­spre­chen­den Cli­ent als aktiv.
    • “check­for­news” prüft auf dem Ser­ver, ob es neue Chat­nach­rich­ten gibt. Die Nach­richt wird mit 1 Hz (eine Anfra­ge pro Sekun­de) gesen­det. Sie ent­hält einen “lastupdate”-Zeitstempel des Cli­ents und ermög­licht es dem Ser­ver zu prü­fen, ob es aktu­el­le­re Nach­rich­ten gibt.
    • “check­fo­ru­sers” wird eben­falls mit einer Fre­quenz von 1 Hz an den Ser­ver gesen­det. Sie ent­hält eben­falls einen Zeits­tem­pel der letz­ten Nut­zer­lis­ten­ak­tua­li­sie­rung und ver­langt vom Ser­ver im posi­ti­ven Fall eine aktua­li­sier­te Nut­zer­lis­te.
    • “use­ris­ty­ping” wird gesen­det, wenn ein Nut­zer beginnt oder auf­hört, eine Chat­nach­richt zu ver­fas­sen, und trans­por­tiert einen Nut­zer­na­men und eine Flag (=“Wahr­heits­wert”), ob der Nut­zer tippt (“true”), oder nicht (“fal­se”).
    • “messa­ge” wird gesen­det, wenn ein Nut­zer eine Chat­nach­richt durch den “Sende”-Button oder das Drü­cken der Enter­tas­te absen­det. Die­ser Nach­rich­ten­typ ent­hält neben der Chat­nach­richt auch den Nut­zer­na­men, den indi­vi­du­el­len Nut­zer­code und den Timestamp der Uhr­zeit, zu der die Nach­richt (nicht die Chat­nach­richt) erzeugt bzw. abge­schickt wur­de.
    • “pro­to­col” ist ein gene­ri­scher Nach­rich­ten­typ und kann einen Belie­bi­gen Inhalt zum Ser­ver trans­por­tie­ren. Der Ser­ver kann nativ die­sen Nach­rich­ten­typ nicht ver­ar­bei­ten und wird nicht dar­auf reagie­ren.
    • “suc­cess” ist eben­falls ein gene­ri­scher Nach­rich­ten­typ und ist geeig­net, um den Ser­ver nach dem Erfolg einer Ope­ra­ti­on zu fra­gen. Der Ser­ver kann nativ die­sen Nach­rich­ten­typ nicht ver­ar­bei­ten und wird nicht dar­auf reagie­ren.
  3.  Der Ser­ver erhält die gera­de beschrie­be­nen Nach­rich­ten und muss die­se kon­se­quent ver­ar­bei­ten. Nur vali­de und genau die­se oben defi­nier­ten Nach­rich­ten­ty­pen wer­den berück­sich­tigt, um Sicher­heits­lü­cken zu ver­mei­den.
    Anhand des “type”-Attributes kann der Ser­ver den Nach­rich­ten­typ iden­ti­fi­zie­ren und ent­spre­chend ver­ar­bei­ten:
    • Bei “heart­beat” wird der Ein­trag des mit­ge­lie­fer­ten Nut­zer­na­mens auf den mit­ge­lie­fer­ten Zeits­tem­pel aktua­li­siert.
    • “check­for­news” ver­an­lasst den Ser­ver, den mit­ge­lie­fer­ten Zeits­tem­pel a mit dem Zeits­tem­pel b der letz­ten emp­fan­ge­nen Chat­nach­richt zu ver­glei­chen. Ist Zeits­tem­pel b grö­ßer, wird der Ser­ver mit dem kom­plet­ten Chat­ver­lauf ant­wor­ten.
      Die­ses Vor­ge­hen ist sehr ein­fach, aber ab einer gewis­sen Chat­län­ge nicht mehr effek­tiv.
    • “check­fo­ru­sers” funk­tio­niert wie “check­for­news”, jedoch wird hier nun die letz­te cli­ent­sei­ti­ge Aktua­li­sie­rung der Nut­zer­lis­te mit der letz­ten ser­ver­sei­ti­gen Aktua­li­sie­rung ver­gli­chen und gege­be­nen­falls geant­wor­tet.
    • “use­ris­ty­ping” ver­än­dert den Ein­trag eines Nut­zers in der Nut­zer­da­ten­hal­tung. Ist die Flag “true”, wird dem Nut­zer­ein­trag für den Wert “isty­ping” eben­falls “true” ein­ge­tra­gen, andern­falls wird “fal­se” ein­ge­tra­gen. Der Zeits­tem­pel der letz­ten Nut­zer­lis­ten­ak­tua­li­sie­rung wird aktua­li­siert. Die nächs­te “check­fo­ru­sers” wird bei allen Nut­zern dem­entspre­chend die aktua­li­sier­te Nut­zer­lis­te als Ant­wort ent­hal­ten.
      Hier könn­te ser­ver­sei­tig und im Hin­blick auf die zu über­tra­gen­den Daten­men­gen Auf­wand gespart wer­den, indem vor dem Schrei­ben geprüft wird, ob der Ein­trag bereits dem neu­en Wert ent­spricht.
    • Eine “messa­ge” wird, wenn ID, Nut­zer­na­me und mit­ge­lie­fer­ter Code zuein­an­der gehö­ren, mit dem Ankunft-Zeitstempel in die Chat­da­ten­hal­tung ein­ge­tra­gen. Die nächs­te “check­for­news” wird bei allen Nut­zern dem­entspre­chend den neu­en Chatein­trag als Ant­wort erhal­ten. Soll­te die gesen­de­te Chat­nach­richt Javascript-Code ent­hal­ten, wird der ver­ant­wort­li­che Nut­zer ohne Wei­ter­ver­ar­bei­tung der Nach­richt aus dem Chat gebannt.
  4. Der Cli­ent erhält vom Ser­ver Ant­wor­ten auf Nach­rich­ten. Die­se wer­den vom Java­script je nach Antwort-Typ ver­ar­bei­tet.
    • “heart­beat” wird nicht beant­wor­tet.
    • “check­for­news” wird im Fal­le von Neu­ig­kei­ten mit einer Nach­richt vom Typ “ServerResponse_ChatMessages” beant­wor­tet. Die­se Nach­richt ent­hält den kom­plet­ten Chat­ver­lauf. Der Cli­ent aktua­li­siert in die­sem Fall den Zeits­tem­pel des letz­ten Updates auf den Zeits­tem­pel des neu­es­ten Ein­tra­ges in der gera­de erhal­te­nen Nach­richt und schreibt den Chat­ver­lauf zudem in sei­ne Daten­hal­tung. Anschie­ßend wird das HTML für den Chat­ver­lauf erzeugt und in das Chat­fens­ter ein­ge­fügt. Dann prüft der Cli­ent, von wem die letz­te Chat­nach­richt gesen­det wur­de und spielt gege­be­nen­falls den Benach­rich­ti­gungs­ton ab.
    • “check­fo­ru­sers” wird ähn­lich beant­wor­tet mit dem Typ “ServerResponse_UserList”. Die Ant­wort ent­hält die Lis­te der Nut­zer und deren Tipp-Zustand (“true”/“false”). Auch hier wird der Cli­ent das HTML erzeu­gen und in den Bereich der Nut­zer­lis­te ein­fü­gen.
    • “use­ris­ty­ping” wird nicht beant­wor­tet.
    • “messa­ge” wird nicht beant­wor­tet.

Fer­tig ist der Chat­room.

Ergebnis und Fazit

Ins­ge­samt hat die Ent­wick­lung ca 3,5 Arbeits­ta­ge (28 Stun­den) gedau­ert. In Anbe­tracht der Tat­sa­che, dass ich zu Beginn auf kei­ne prak­ti­sche Erfah­rung zur Ent­wick­lung von Chat­rooms zurück­grei­fen konn­te, ist dies eine durch­aus zufrie­den­stel­len­de Bilanz. Wich­tig zu beach­ten ist hier­bei, dass für das Design die Bootstrap-Bibliothek ver­wen­det wur­de und die Arbeits­dau­er sich daher aus­schließ­lich auf HTML und Logik-Programmierung bezieht. Das weni­ge, selbst getipp­te CSS kann ver­nach­läs­sigt wer­den.

Das Haupt­pro­blem des Pro­jek­tes bestand in der Brow­ser­kom­pa­ti­bi­li­tät der Java­scrip­te und in der Kom­mu­ni­ka­ti­on zwi­schen Cli­ent und Ser­ver, die trotz des theo­re­tisch kon­se­quent durch­ge­führ­ten Pro­to­kolls ein robus­tes Error­hand­ling braucht und ger­ne Pro­ble­me macht.

Wie ver­hält sich der Ser­ver, wenn er inva­li­de Nach­rich­ten bekommt? Wie wer­den Son­der­zei­chen behan­delt, Hack­ver­su­che geahn­det etc? Wie hand­habt man einen Nut­zer­time­out? Was macht der Cli­ent, wenn er anstel­le einer “ServerResponse_x”-Nachricht trotz aus­führ­li­chen Exception-Handlings des Ser­vers einen PHP-Fehler geschickt bekommt? Wie wird die Bann­lis­te geführt, ohne dabei bestim­me IP-Adressen ver­se­hent­lich für immer aus­zu­sper­ren?

Dies sind Fra­gen, die prä­zi­se beant­wor­tet und behan­delt wer­den wol­len.

Eine opti­ma­le Lösung ist die­se Soft­ware — gera­de in den Punk­ten Sicher­heit und Per­for­mance — sicher­lich nicht. Die zeit­li­chen Kos­ten durch das stän­di­ge Öff­nen, Lesen und Schrei­ben der Text­da­tei­en des Ser­vers sind dabei nur ein Glied einer gan­zen Ket­te von Opti­mie­rungs­mög­lich­kei­ten.

Dass aus­schließ­lich bei einer “messa­ge” wirk­lich die Iden­ti­tät des Sen­ders geprüft wird, ist eine sehr opti­mis­ti­sche Lösung. Man kann zwar nur sehr schwer im Namen eines Ande­ren Chat­nach­rich­ten ver­fas­sen. Aller­dings wäre es “rela­tiv” ein­fach, einen Cli­ent durch das Erra­ten der Session-ID und das Fäl­schen von “heartbeat”-Nachrichten am Time­out zu hin­dern, obwohl der ent­spre­chen­de Nut­zer längst nicht mehr auf der Chat­sei­te ist — etwa durch einen Brow­ser­ab­sturz.

Wür­de man ein wirk­lich siche­res und nicht mani­pu­lier­ba­res Sys­tem auf­bau­en wol­len, müss­te jede Nach­richt ent­spre­chen­de Authen­ti­fi­zie­rungs­in­hal­te mit­lie­fern und auch die zusätz­li­chen, nut­zer­spe­zi­fi­schen Codes müss­ten regel­mä­ßig geän­dert wer­den. Eine ver­schlüs­sel­te Ver­bin­dung wäre zudem unver­zicht­bar.

Im Hin­blick auf Per­for­mance habe ich bereits eini­ge Anmer­kun­gen im Text hin­ter­las­sen. Ist es wirk­lich sinn­voll, dass man “check­for­news” und “check­fo­ru­sers” als getrenn­te Nach­rich­ten schickt? Prak­tisch betrach­tet — gera­de im Hin­blick auf Daten­vo­lu­men und Rechen­leis­tung — nein, denn es ver­ur­sacht schon allein für den Fall, dass es kei­ne Neu­ig­kei­ten vom Ser­ver gibt, den dop­pel­ten Daten­strom. Dass stets bei einer ein­zi­gen neu­en Chat­nach­richt der kom­plet­te Chat­ver­lauf in eine Nach­richt gestopft und gesen­det wird, ist — wie bereits gesagt — alles ande­re als res­sour­cen­freund­lich. Ab einer gewis­sen Chat­län­ge kann man (vor allem bei Mobil­ge­rä­ten) davon aus­ge­hen, dass die Ren­der­dau­er der Chat­dar­stel­lung in den Sekun­den­be­reich gerät. Das könn­te schon bei weni­gen Nut­zern dazu füh­ren, dass der Chat über­haupt nicht mehr ange­zeigt wird, da durch den stän­di­gen Nach­schub an aktua­li­sier­ten Chat­ver­läu­fen der Brow­ser nicht mehr hin­ter­her­kommt.

Aber die­ses Expe­ri­ment hat  — ange­sichts der Aus­gangs­prä­mis­sen — nicht den Anspruch, prak­tisch per­fekt oder rea­li­täts­nah funk­tio­nal zu sein. Aus pädagogisch-didaktischer Sicht, zur Erklä­rung und Ver­an­schau­li­chung der ver­schie­de­nen Mecha­nis­men und Funk­tio­nen eines Chats, sind die­se Aspek­te mei­ner Mei­nung nach durch­aus sinn­voll und ver­tret­bar. Durch die ein­zel­nen Nach­rich­ten­ty­pen wird dem unbe­darf­ten Leser vor Augen geführt, wel­che Infor­ma­ti­ons­pa­ke­te und Struk­tu­ren stän­dig im Aus­tausch sind und inei­an­der grei­fen, wenn man einen Chat ver­wen­det. Und das war das Ziel die­ses Expe­ri­ments.

Nehmen Sie Kontakt auf!

Sie haben Fra­gen zu Inter­net und Co? Schrei­ben Sie eine Nach­richt über das Kon­takt­for­mu­lar!

Basics: Was ist ein Icon-Font?

Um zu erklä­ren, was ein Icon-Font ist, muss erst Klar­heit über die zwei bil­den­den Begrif­fe “Icon” und “Font” geschaf­fen wer­den.…
Wei­ter­le­sen!

Demo-Videos

Video und Film ist das ein­drucks­stärks­te Medi­um, über das wir heu­te ver­fü­gen. Um mit Film­ma­te­ri­al die maxi­ma­le Leis­tung erzie­len zu…
Wei­ter­le­sen!

Plugin-Entwicklung im Kundenauftrag: Teilautomatisierung der Newsletter-Inhalte

Der regel­mä­ßi­ge News­let­ter der Res­sour­cen­werk­statt Schu­bert ent­hält neben Pra­xis­tipps und Fach­bei­trä­gen zur Pädagogik-Branche auch eine Über­sicht über freie Plät­ze in…
Wei­ter­le­sen!