Flashhilfe.de - Flash Community

Tutorial: Flash-Chat via Java Socketserver [Flash 10]

 


AntwortenRegistrieren Seite1  

Luzze#1
Benutzerbild von Luzze
Beiträge: 47
Registriert: Jul 2009

02.09.2009, 21:00

Hallo zusammen,

vor einigen Tagen benötigte ich für meine Webseite einen Chat, der in einem Flashgame integriert ist. Da Chats ja bekanntlich im allgemeinen einen hohen Traffic verursachen können, stellte sich mir die Frage, wie ich denn das ganze nun mit einer möglichst geringen Serverlast bewerkstellige. Die Lösung:

Persistente Socketbasierte Verbindungen. Der Vorteil liegt klar auf der Hand: Die Serverlast wird gesenkt, da man für jede Unterhaltung nur einmal eine Socketverbindung öffnet, die dann die ganze Zeit der Unterhaltung geöffnet bleibt. Der Server, der am anderen Ende eines Ports auf eine eingehende Nachricht "horcht", reagiert sofort und verteilt die Messages an alle Clients, für die diese Nachricht bestimmt ist. So erreicht man eine beinahe Echtzeit-Kommunikation.
Würde man z.B. im Vergleich, wie es ja viele existierende Chats tun, die Variante "PHP/MySQL/Flash" verwenden, würde dies einen 3x so hohen Traffic verursachen, und nicht nur HTTP-Server sondern auch Datenbankserver schwer belasten...Zumal die Performance bei steigender Clientzahl dann doch irgendwann zu wünschen übrig lässt.

Auf der Flashseite sind Socketverbindungen seit AS3 und der neuen Socket-Klasse problemlos zu integrieren und man kann wunderbare Chatclients erstellen.

Schwieriger wird es da schon auf der Serverseite. Entweder man hat bereits einen fertigen Socketserver (was im allgemeinen doch sehr schwierig sein dürfte, da ein fertiger Server meist eh nicht den eigenen Bedürfnissen entspricht) oder man entscheidet sich, selbst einen Server zu programmieren. Ich habe mich für die Variante 2 entschieden, die ich euch in diesem kleinen Tutorial näher bringen möchte.

Die Programmiersprache Java bietet hier optiomale Vorraussetzungen, um mit relativ wenig Code leistungsfähige Server zu erstellen, die problemlos auch gleichzeitig 2000 Clients verarbeiten können.

Ein Nachteil hat das ganze jedoch: Wenn man sich für einen Javaserver entscheidet  benötigt man einen Internetserver mit Rootzugriff oder man hat einen Provider, der die Installation der Java-Komponenten für einen vornimmt. Gestartet wird der Server nämlich via SSH-Konsole. Na ja, heutzutage gibt es Rootserver ja schon zum Preis eines besseren Webhostingpakets. Man sollte jedoch zumindest einen Dual-Core Server mit 2 Ghz RAM betreiben, dazu später mehr.

Jetzt kommen wir aber mal zum Code. Wir fangen mit dem Server an. Um Javacode zu schreiben reicht ein einfacher Texteditor, wie der aus Windows. Java ist eine Compiler-Sprache, zum ausführen und Testen des Servers empfehle ich die Java SDK, die Ihr z.B. hier downloaden könnt. Das Java Software Development Kit (JDK) umfasst die Laufzeitumgebung (JRE) und bietet zusätzlich Compiler, Debugger und Dokumentationswerkzeuge. Habt Ihr alles installiert, gehts ans programmieren :)

Der Server besteht in seiner fundamentalen Form aus gerade mal 2 Klassen.
Der Klasse "ChatServer" sowie der Klasse "ChatServerHandler".

Die Klasse ChatServer:

PHP:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
<?
import java
.io.IOException;
import java.net.ServerSocket;
import java.net.Socket;
import java.io.BufferedReader;
import java.io.InputStreamReader;
import java.io.OutputStreamWriter;
import java.io.PrintWriter;
import java.util.Vector;


public class 
ChatServer {

 
// Anfang Attribute
   
public static final int DEFAULT_PORT 9800//Standart-Port der verwendet wird, wenn kein anderer &uuml;bergeben wird
   
public static final int MAX_CLIENTS 1000//Anzahl der maximalen Clienten (Verbindungen)
   
public static final String SERVER_VERSION "1.0";
 
// Ende Attribute


  // Anfang Methoden
 
public static void main(String[] args) {  //Main-Methode
 
int port DEFAULT_PORT;
 
ServerSocket serverSocket null;    //Ein neues Objekt der Klasse ServerSocket wird erstellt
 
Socket socket null;
  try {
      if(
args.length 0)
         
port Integer.parseInt(args[0]);
         
System.out.println("\n[CHATSERVER v"+SERVER_VERSION+"]"); //Hier werden Informationen auf beim Starten auf der Konsole ausgegeben
         
System.out.println("Copyright deineSeite.de, all rights reserved\n");
         
System.out.println("\nMaximum Clients: "+MAX_CLIENTS);
         
System.out.println("running Chatserver on Port "+port+" successfull...");
         
System.out.println("listen for new Clients...");
         
  } catch(
NumberFormatException nfe) {
     
System.err.println("Usage: java ChatServer [port]"); //Ausgabe bei evtl. Fehlern
     
System.err.println("Where options include:");
     
System.err.println("\tport the port on which to listen.");
     
System.exit(0);
  }
  try {
     
serverSocket = new ServerSocket(port); //Eingehende Verbindung
     
while(true) {
         
socket serverSocket.accept(); // Die Verbindung wird akzeptiert
         
ChatServerHandler handler = new ChatServerHandler(socket); //und an ein neues Objekt der Handler-Klasse &uuml;bergeben
         
handler.start(); // Ein neuer Thread wird gestartet
     
}
  } catch(
IOException ioe) {
     
ioe.printStackTrace();
  } 
finally {
      try {
         
serverSocket.close();
      } catch(
IOException ioe) {
   
ioe.printStackTrace();
      }
  }
    }
 
// Ende Methoden
}
?>


Für die, die noch nie etwas in Java programmiert haben und auch keinerlei Erfahrung in der Objektorientierten Programmierung haben, ist der obige Code wahrscheinlich nicht mehr als verwirrender Kauderwelsch.

Es ist aber halb so schlimm wie es aussieht :)

Zu Beginn importieren wir ein paar Klassen, in denen verschiedene Methoden die für unseren Server nützlich sind enthalten sind. Z.B. enthalten die Klassen java.net.ServerSocket und java.net.Socket eben Methoden zur zum erstellen und Verwalten von Socketverbindungen. Die Klassen java.io.InputStreamReader und java.io.OutputStreamWriter werden benötigt, um Eingabe-Streams zu lesen und andersrum wieder zu senden (io = input/output) . Um Fehler abzufangen, die z.B. während des Sendens und empfangen auftreten können, importieren wir die Klasse java.io.IOException, die verschiedene Methoden zur Behandlung von Exceptions (Fehlern) bereit stellt. Java ist hier sehr genau und verlangt unter Umständen, das man sich um Probleme die auftreten können, kümmert - ansonsten verweigert der Compiler seinen Dienst. Das ist auch richtig so, denn bei Netzwerkverbindungen kann immer etwas schief gehen, die Verbindung bricht ab, Pakete kommen nicht oder falsch an etc. Würde man diese Probleme nicht in einer Ausnahme auffangen (catch), erhielte man die von Windows so bekannten "Ausnahmefehler" ;)

Am Anfang der Klasse deklarieren wir ein paar Klassenvariablen oder in diesem Fall Konstanten, die allgemeine Angaben wie den Standartport, die Anzahl der maximal erlaubten Clients sowie die Serverversion beinhalten. Dies dient nur dem Betreiber und ist natürlich für die Clients nicht sichtbar, da diese Informationen ja nur beim start des Server über die Konsole ausgegeben werden.

In der main-Methode, ein fester Bestandteil und Startpunkt jedes Java-Programms, beginnen wir damit beim Aufruf des Programms den gewünschten Port zu öffnen. Da es hier zu Problemen kommen könnte,welcher Art auch immer, wird dies in einem try / catch Block geschrieben (Versuche-->try, ansonsten -> catch). Man hat die Möglichkeit beim Start des Servers anhand eines Arguments einen Port zu übergeben, ansonsten wird der in der Konstante "port" festgelegte Port verwendet. Ist der Server erfolgreich gestartet, gibt er auf der Konsole eine Erfolgsmeldung mit Infos der Serverversion etc. wieder.

Ab diesem Zeitpunkt "horcht" der Server am geöffneten Port auf eingehende Verbindungen.

Im nächsten try/catch-Block erstellt der Server bei eingehender Verbindung einen neuen Socket und gibt diesen dann umgehend an die nächste Klasse weiter, um sofort wieder für neue Verbindungen zur Verfügung zu stehen. Diesen "NonBlocking-SocketServer" erhalten wir mit Hilfe von Threads, welche die nächste Klasse verwaltet und sich um die Clients kümmert.

Threads sind eigenständige gekapselte Prozesse, die meist neben dem Hauptprogramm laufen, damit das Hauptprogramm ungehindet seine Aufgabe erledigen kann, in dem Fall das warten auf/und akzeptieren von neuer Verbindungen. Vereinfacht gesagt handelt es sich hierbei um Multitasking. Für jeden neuen Client der sich anmeldet, wird ein eigener Thread erstellt, der sich dann ganz allein um den jeweiligen Clienten kümmert. Alle Threads werden in einer Art Liste, die die Java Vector-Klasse zur Verfügung stellt, gespeichert, und zwar mit all Ihren Eigenschaften...Auf diese Liste kann dann bei Bedarf ganz einfach zugegriffen werden, um z.B. einzelnen, ein paar oder allen Clienten eine Nachricht zu senden, Verbindungen zu trennen oder was auch immer.

Die Klasse ChatServerHandler, die die Threads und somit die Clienten verwaltet sieht folgendermassen aus:

PHP:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
<?
class ChatServerHandler extends Thread {

 
// Anfang Attribute1
 
static final String SERVER_VERSION "1.0";
  static final 
int MAX_CLIENTS 1000;
  static 
Vector<ChatServerHandlerhandlers = new Vector<ChatServerHandler>( MAX_CLIENTS );
  private 
boolean registered false// Hier wird festgehalten, ob der Client bereits registriert ist
 
private String username//Der Username, der vom Clienten an den Server &uuml;bergeben wird
 
private String ucolor//Die gew&auml;hlte Farbe, die der Client gew&auml;hlt hat
 
private String room;   //Der Chatraum, der &uuml;bergeben wurde
 
private Socket socket;
  private 
BufferedReader in;
  private 
PrintWriter out;
  private 
String login;
 
// Ende Attribute1


   
public ChatServerHandler(Socket socketthrows IOException {
       
this.socket socket;
 
in = new BufferedReader(
      new 
InputStreamReader(socket.getInputStream()));
 
out = new PrintWriter(
      new 
OutputStreamWriter(socket.getOutputStream()));
    }

 
// Anfang Methoden1
   
public void run() {
   
String line;
   
String message;

 
synchronized(handlers) {
     
handlers.addElement(this);
 
// add() not found in Vector class
 
}
  try {
  if (!
registered){ //Ausgabe, wenn der Client noch nicht registriert ist, also nach Aufbau einer neuen Verbindung
     
login in.readLine();
     
String[] splittArray login.split(">>"); //Die &uuml;bergebenen Werte des Clients werden anhand eines Trennzeichens in einem Array gespeichert
     
username=splittArray[0]; //Der Benutzername des Clienten
     
room=splittArray[1]; //Der Chatraum
     
ucolor=splittArray[2]; //Die Farbwahl
     
out.println("<b>Willkommen beim Chatserver</b><br>Serversoftware v"+SERVER_VERSION+"<br>------------------------------<br><font color=\"#"+ucolor+"\"><b>Angemeldet als "+username+"</b></font><br>");
     
out.flush();
     
registered=true//Variable registered wird auf true gesetzt
     
for(int i 0handlers.size(); i++) {
     
synchronized(handlers) {
               
ChatServerHandler handler =
              (
ChatServerHandler)handlers.elementAt(i);
              if (
handler.room.equals(room)){
             
handler.out.println("<i>***** "+username+" betritt den Raum *****</i>"); //Jedem Clienten wird die Mitteilung gesendet, das der neue Benutzer den Raum betreten hat
             
handler.out.flush();
         }
      }
    }
      }
      while((
line in.readLine()) != null) {
       if (
line.equalsIgnoreCase("/quit")) {
            break;
         }
     
String[] splittArray2 line.split(">>");
     
message=splittArray2[0];
     
room=splittArray2[1];
     
ucolor=splittArray2[2];
    for(
int i 0handlers.size(); i++) {
     
synchronized(handlers) {
               
ChatServerHandler handler =
              (
ChatServerHandler)handlers.elementAt(i);
              if (
handler.room.equals(room)){
             
handler.out.println("<font color=\"#"+ucolor+"\"><b>"+username+":</b> "+message+"</font>");
             
handler.out.flush();
         }
      }
    }
      }
  } catch(
IOException ioe) {
     
ioe.printStackTrace();
  } 
finally {
      try {
   
in.close();
   
out.close();
   
socket.close();
     for(
int i 0handlers.size(); i++) {
     
synchronized(handlers) {
               
ChatServerHandler handler =
              (
ChatServerHandler)handlers.elementAt(i);
              if (
handler.room.equals(room)){
             
handler.out.println("<i>***** "+username+" hat den Raum verlassen *****</i>");
             
handler.out.flush();
         }
      }
    }
      } catch(
IOException ioe) {
      } 
finally {
   
synchronized(handlers) {
       
handlers.removeElement(this);
    }
      }
  }
    }
 
// Ende Methoden1
}
?>



Diese Klasse hat es schon in sich. Zu Beginn werden erstmal wieder einige Variablen und KOnstanten für die Verwaltung des Clienten festgelegt.
Im Konstruktor (Der Methode ChatServerHandler()) werden dann erstmal Objekte für den Socket, den Inputstream und dem Outputstream erstellt.
Von der Superklasse Threads, von der unsere Klasse ChatServerHandler erbt, leiten wir die Methode run ab, in der alles wesentliche des Threads passiert.

Der Ablauf ist der folgende:

Unser Chat verfügt über die Möglichkeit, mehrere Räume zu verwalten, und die einzelnen Benutzer können Ihre Schriftfarbe selbst auswählen. Wenn sich nun
ein Client anmeldet, wird mit Ihm ein String an den Server übergeben der so aussieht:

Username>>Raumnummer>>Schriftfarbe

Der Server schaut erstmal, ob der Client schon angemeldet ist, ansonsten wird er erstmal der Liste hinzugefügt und an alle Clienten des Raumes wird eine Nachricht
gesendet, das "Username den Raum betritt". Sobald der User in der Vectorliste gespeichert ist, sind auch alle Variablen mitgespeichert, die sich innerhalb von run befinden...Was bedeutet, das jeder User seine eigene Variable room, ucolor usw. hat.

PHP:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
<?
if (!registered){ //Ausgabe, wenn der Client noch nicht registriert ist, also nach Aufbau einer neuen Verbindung
     
login in.readLine();
     
String[] splittArray login.split(">>"); //Die &uuml;bergebenen Werte des Clients werden anhand eines Trennzeichens in einem Array gespeichert
     
username=splittArray[0]; //Der Benutzername des Clienten
     
room=splittArray[1]; //Der Chatraum
     
ucolor=splittArray[2]; //Die Farbwahl
     
out.println("<b>Willkommen beim Chatserver</b><br>Serversoftware v"+SERVER_VERSION+"<br>------------------------------<br><font color=\"#"+ucolor+"\"><b>Angemeldet als "+username+"</b></font><br>");
     
out.flush();
     
registered=true//Variable registered wird auf true gesetzt
     
for(int i 0handlers.size(); i++) {
     
synchronized(handlers) {
               
ChatServerHandler handler =
              (
ChatServerHandler)handlers.elementAt(i);
              if (
handler.room.equals(room)){
             
handler.out.println("<i>***** "+username+" betritt den Raum *****</i>"); Jedem Clienten wird die Mitteilung gesendetdas der neue Benutzer den Raum betreten hat
              handler
.out.flush();
         }
      }
?>


Danach wird die Variable registered auf "true" gesetzt, was wichtig ist denn sonst würde diese Nachricht immer mitgesendet werden, was ja nicht Sinn der Sache ist :)
Im nächsten try-Block wird nun das "normale" Senden einer Nachricht behandelt:

PHP:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
<?
while((line in.readLine()) != null) {
       if (
line.equalsIgnoreCase("/quit")) {
            break;
         }
     
String[] splittArray2 line.split(">>");
     
message=splittArray2[0];
     
room=splittArray2[1];
     
ucolor=splittArray2[2];
    for(
int i 0handlers.size(); i++) {
     
synchronized(handlers) {
               
ChatServerHandler handler =
              (
ChatServerHandler)handlers.elementAt(i);
              if (
handler.room.equals(room)){
             
handler.out.println("<font color=\"#"+ucolor+"\"><b>"+username+":</b> "+message+"</font>");
             
handler.out.flush();
         }
      }
    }
?>


Hier wird zuerst geschaut, ob der Stream weder "null" ist, was bedeuten würde das die Verbindungen zum Clienten unterbrochen wurde, noch das der Client den Befehl "/quit" gesendet hat. In beiden Fällen würde der Server sonst an alle Clients im Raum die Nachricht:"Username hat den Raum verlassen" senden, was im finally-Block geschieht.

So, dies ist nun ein kleiner, aber feiner und vor allem schneller Chatserver der zudem nach belieben erweitert werden kann. Zum Beispiel hat man die Möglichkeit, weitere
Variablen an den Server zu übergeben und auswerten zu lassen, z.B. um Privat-Chats zu ermöglichen etc. Das könnte man dann so machen:

Username>>Raum>>uColor>>Privatraum

In dem Fall müsste man den Server beibringen, das er die Message nur an den oder die Benutzer in Raum/Privatraum senden sollen, was mit einer einfachen if-Abfrage zu erledigen ist.

Ich hoffe, ich konnte die funktionsweise des Server einigermassen verständlich machen, es ist natürlich ein schwieriges Thema und eigentlich auch kein Java-Anfänger-Stoff.Aber wenn man sich erstmal etwas genauer damit befasst hat, wird man sicher irgendwann auch die Hintergründe besser verstehen lernen.

Was noch zu erwähnen wäre:
Man sollte darauf achten, das man die maximale Anzahl gleichzeitiger Threads nicht zu hoch ansetzt. Threads werden vom Betriebssystem gesteuert und auch das hat seine Grenzen. Im allgemeinen gilt:

Je geringer die Leistung der Hardware, desto weniger maximale Threads sollte man wählen. Auch die Anzahl der Prozessorkerne ist ein entscheidener Faktor.
Zudem hat Java bei 32bit Systemen lediglich Zugriff auf 2 GB Arbeitspeicher. Wenn man zusätzlich den Server noch als HTTP-Server verwendet, sollte man also auf ein ausgewogenes Verhältnis achten.

Die besten Einstellungen sollte man für sich nach und nach Systemspezifisch ermitteln.

Nun zum Aufbau des Flash-Clients:

Wie wir bereits von unserem Server wissen, hat jeder User die Möglichkeit, in Räumen zu chatten sowie die Farbe seiner Schrift auszuwählen.
Wie also könnte der Aufbau eines solchen Clients sein ? Ein Beispiel:

Wir fügen folgende Komponenten auf auf unserer Bühne ein, die einfach alle auf das erste Schlüsselbild gesetzt werden können:

1 Textfeld als Eingabetext, Instanzname txtEingabe
1 Textfeld als dynamischer Text, Instanzname txtAusgabe, "Text als HTML anzeigen" muss eingestellt sein
1 Button, Instanzname btn_up
1 Button, Instanzname btn_down
1 Button, Instanzanme btn_send
1 Button, Instanzname btn_close
1 ColorPicker Komponente, Instanzname cPicker

Das soll es auch schon an Komponenten gewesen sein.
Bei dem Textfeld txtEingabe betten wir alle Gross,-und Kleinbuchstaben, sowie Zahlen und die Zeichen ,-@?()/&+ ein. Mehr nicht ! Der User soll natürlich nicht die Zeichenfolge >> als Message senden können, da diese ja beim Server als Trennzeichen gelten und es zu einem Fehler führen würde. Dies unterbinden wir also, indem wir einfach die erlaubten Zeichen in das Textfeld einbetten.

Jetzt fügen wir folgenden AS-Code ein, auch auf das erste Schlüsselbild:

ActionScript:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
//Import der benötigten Klassen
import flash.errors.*;
import flash.events.*;
import flash.net.Socket; //Das Herzstück unseres Chat-Clients: Die Socket-Klasse
import flash.utils.Timer;
import flash.events.TimerEvent;

// Wir erstellen ein neues Socket-Object
var socket:Socket=new Socket();

//Hier werden alle wichtigen Event-Listener erstellt:

btn_send.addEventListener(MouseEvent.CLICK,fClickSend);
btn_close.addEventListener(MouseEvent.CLICK,fClickClose);
btn_down.addEventListener(MouseEvent.MOUSE_OVER,fOverDown);
btn_up.addEventListener(MouseEvent.MOUSE_OVER,fOverUp);
btn_down.addEventListener(MouseEvent.MOUSE_OUT,fOutDown);
btn_up.addEventListener(MouseEvent.MOUSE_OUT,fOutUp);
addEventListener(MouseEvent.MOUSE_OVER,fOverButton);
cPicker.addEventListener(Event.CHANGE,fChangeColor);
socket.addEventListener(Event.CLOSE, closeHandler);
socket.addEventListener(Event.CONNECT, connectHandler);
socket.addEventListener(IOErrorEvent.IO_ERROR, ioErrorHandler);
socket.addEventListener(SecurityErrorEvent.SECURITY_ERROR, securityErrorHandler);
socket.addEventListener(ProgressEvent.SOCKET_DATA, socketDataHandler);
addEventListener(KeyboardEvent.KEY_DOWN, keyDownHandler);

//Diese Variable speichert die Schriftfarbe des Benutzers
var color:String="000000";

//Die Variable in der die Messages des Servers gspeichert werden
var response:String;

//Variablen für Username und Raumnummer, die idealerweise z.B. von einem PHP-Script beim einloggen übergeben werden
var username:String="Testname";
var room:String="1";

// Einige Timer, unter anderem um das Textfeld txt_Ausgabe scrollbar zu machen
var myTimerDown:Timer;
var myTimerUp:Timer;
var myTimerConn:Timer;

//wir initieren die Verbindung,mit 3 Sekunden Pufferzeit um alle benötigten Komponenten verfügbar zu haben
//Dafür kommt ein Timer-Object zum Einsatz

txtAusgabe.htmlText="Verbinde zum Chatserver"; //Verbindungsmeldung
myTimerConn=new Timer(500,6)
myTimerConn.addEventListener(TimerEvent. TIMER,fuWaitPoint);
myTimerConn.start();
var durchlauf:int=1;

function fuWaitPoint(e:Event) { // Diese Funktion bewirkt nur, das alle 500ms ein . an "Verbinde zum Gameserver" angehängt wird und startet nach der Pufferzeit die Verbindung
   if (durchlauf<6) {
      txtAusgabe.appendText(".");
   } else {
      fuConnect();
   }
   durchlauf++;
}

function fuConnect() {
   socket.connect('localhost',4000); //Hier wird die Verbindung gestartet. Natürlich muss hier der Host und der Port eueres SocketServers rein.
   myTimerConn.stop();//Der Puffertimer wird gestoppt
   myTimerConn.removeEventListener(TimerEvent. TIMER,fuConnect);
}

function fChangeColor(e:Event):void {
   color=cPicker.hexValue; //Der Variable "color" wird beim Wechsel der Farbe mit dem ColorPicker der neue Farbwert zugewiesen
}


//Diese Funktion sendet die erste Verbindungsanfrage
function sendRequest():void {
   response="";
   writeln(username+">>"+room+">>"+color);
   socket.flush();
}


//Diese Funktion sendet den im Argument übergebenen String über die Socketverbindung an den Server,also die eigentliche Message eines Users
function writeln(str:String):void {
   str+="\n";
   try {
      socket.writeUTFBytes(str);
   } catch (e:IOError) {
      trace(e);
   }
}


//Wir lesen die Antwort des Servers,speichern Sie in der Variable response und geben diese im Textfeld txt_Ausgabe aus
function readResponse():void {
   var str:String=socket.readUTFBytes(socket.bytesAvailable);
   response+=str;
   response=response.split("\n").join(" "); //Hier müssen ein paar Formatierungen gemacht werden, damit die Ausgabe besser aussieht :)
   txtAusgabe.htmlText=response;
   txtAusgabe.scrollV=txtAusgabe.maxScrollV; //Das Ausgabe-Textfeld wird an das Ende des Textes gescrollt
}

//Debug - Funktionen - im Gebrauch so nicht wichtig, aber für die Fehlersuche empfehlenswert
function closeHandler(event:Event):void {
   trace("closeHandler: " + event);
}


// Hier wird das Event "Connect", also Verbindung hergestellt aufgerufen
function connectHandler(event:Event):void {
   sendRequest(); //Es wird eine Anfrage an den Server gesendet - darf ich bitte rein ? :)
}

function ioErrorHandler(event:IOErrorEvent):void {
   txtAusgabe.appendText("Verbindungsfehler - Verbindung zum Server nicht möglich.\n");
   socket.close();
}

function securityErrorHandler(event:SecurityErrorEvent):void {
   trace("securityErrorHandler: " + event);
}

//Event-Funktion für eingehende Daten
function socketDataHandler(event:ProgressEvent):void {
   readResponse();
}

//Damit auch die Enter-Taste (Return) zum senden funktioniert, die Funktion für das Event

function keyDownHandler(event : KeyboardEvent):void {
   if (event.keyCode==Keyboard.ENTER) {
      fClickSend(null);
   }
}

//Wenn der Button Senden gedrückt wird,soll die Nachricht natürlich gesendet werden
function fClickSend(e:MouseEvent) {
   if (txtEingabe.text!="") {
      writeln(String(txtEingabe.text)+">>"+room+">>"+color); // Hier wird die Message gesendet.Allerdings brauchen wir nicht wie bei der Anmeldung unseren Usernamen mitsenden.
      socket.flush();
      txtEingabe.text="";
   }
}


// wenn der Button Verbindung beenden geklickt wird, werden vorsichthalber alle Eventlistener gelöscht, so kann auch kein Ereignis mehr an einen nicht mehr vorhandenen Socket ausgelöst werden,
// was zu einem Fehler führen würde.

function fClickClose(e:MouseEvent) {
   writeln("/quit");
   socket.close();
   btn_send.removeEventListener(MouseEvent.CLICK,fClickSend);
   btn_close.removeEventListener(MouseEvent.CLICK,fClickClose);
   btn_down.removeEventListener(MouseEvent.MOUSE_OVER,fOverDown);
   btn_up.removeEventListener(MouseEvent.MOUSE_OVER,fOverUp);
   btn_down.removeEventListener(MouseEvent.MOUSE_OUT,fOutDown);
   btn_up.removeEventListener(MouseEvent.MOUSE_OUT,fOutUp);
   removeEventListener(MouseEvent.MOUSE_OVER,fOverButton);
   cPicker.removeEventListener(Event.CHANGE,fChangeColor);
   socket.removeEventListener(Event.CLOSE, closeHandler);
   socket.removeEventListener(Event.CONNECT, connectHandler);
   socket.removeEventListener(IOErrorEvent.IO_ERROR, ioErrorHandler);
   socket.removeEventListener(SecurityErrorEvent.SECURITY_ERROR, securityErrorHandler);
   socket.removeEventListener(ProgressEvent.SOCKET_DATA, socketDataHandler);
   removeEventListener(KeyboardEvent.KEY_DOWN, keyDownHandler);
   btn_send.buttonMode=false;
   btn_send.useHandCursor=false;
   btn_down.useHandCursor=false;
   btn_up.useHandCursor=false;
   btn_up.mouseChildren=false;
   btn_close.useHandCursor=false;
   txtAusgabe.htmlText="<b>Verbindung beendet</b>";
}


//Hier wird den Scrollbuttons für das Ausgabetextfeld Leben eingehaucht. Wir verwenden zum Scrollen Timer und Positionsvariablen

//Nach unten scrollen bei Mouse-Over
function fOverDown(e:MouseEvent) {
   var i:int;
   myTimerDown=new Timer(100,txtAusgabe.maxScrollV);
   myTimerDown.addEventListener(TimerEvent. TIMER,fScrolldown);
   myTimerDown.start();

   function fScrolldown() {
      txtAusgabe.scrollV+=2;
      if (txtAusgabe.scrollV==txtAusgabe.maxScrollV) {
         myTimerDown.stop();
         myTimerDown.removeEventListener(TimerEvent. TIMER,fScrolldown);
      }
   }
}

//Nach oben scrollen bei Mouse-Over

function fOverUp(e:MouseEvent) {
   var i:int;
   myTimerUp=new Timer(100,txtAusgabe.maxScrollV);
   myTimerUp.addEventListener(TimerEvent. TIMER,fScrollUp);
   myTimerUp.start();

   function fScrollUp() {
      txtAusgabe.scrollV-=2;
      if (txtAusgabe.scrollV==txtAusgabe.maxScrollV) {
         myTimerUp.stop();
         myTimerUp.removeEventListener(TimerEvent. TIMER,fScrollUp);
      }
   }
}

//MOuse-Out Funktionen

function fOutDown(e:MouseEvent) {
   myTimerDown.stop();
}

function fOutUp(e:MouseEvent) {
   myTimerUp.stop();
}

//Funktion, um den Buttons auch die üblichen Mauszeiger zu verleihen

function fOverButton(e:MouseEvent) {
   btn_send.buttonMode=true;
   btn_send.useHandCursor=true;
   btn_send.mouseChildren=false;
   btn_down.useHandCursor=true;
   btn_down.mouseChildren=false;
   btn_up.useHandCursor=true;
   btn_up.mouseChildren=false;
   btn_close.useHandCursor=true;
   btn_close.mouseChildren=false;
}


So, um das ganze jetzt nicht noch mehr zu verlängern, werde ich zu dem Flash-Code nicht mehr viel sagen, er ist ausreichend kommentiert.

Ihr könnt euch auch hier die Quelltextdateien herunterladen, und nach Lust und Laune damit experimentieren. Bei Fragen stehe ich so gut ich kann zur Verfügung.

Quelldateien downloaden

Um den Server auf euerem System lokal zu testen, ruft Ihr die Konsole auf und gebt folgendes ein:

java ChatServer 4000

Hiermit startet der Server auf Port 4000. Ihr müsst vorher das Java SDK installiert haben. Danach könnt Ihr 2 der Flash-Clients starten, um den Chat zu testen.

Dieses Tutorial erhebt keinen Anspruch auf Vollständigkeit oder Perfektion, man könnte sicher immer etwas anders machen. Es sollen hier einfach mal die Möglichkeiten aufgezeigt werden, die Flash mit der Socket-Klasse zur Verfügung stellt, weit weg von dem lustigen Animationswerkzeug hin zur hochperformen Client/Serveranwendung.

Es gibt diverse Erweitungsmöglichkeiten, z.B. könnte man obigen Server in Verbindung mit Flash zu einem kleinen GameServer erweitern, der
Spielerdaten verwaltet, berechnet und an alle Spieler weitergibt. Viele Online-Pokerspiele verwenden z.B. schon genau diese Technik zwischen
Flash und Java Socketserver, dann allerdings mit der relativ neuen Java nio Klasse, die noch mehr "Highend Performance" ermöglicht, da keine Threads mehr benötigt werden.

Grüsse
luzze
Es gibt Leute, die können Ihren Stammbaum bis zu denen zurückverfolgen, die noch darauf saßen. ^^
Schlagwörter: Chat, Flash Chat, Java, Socketserver
Geändert von Luzze am 02.09.09 um 23:16 Uhr
Werbung
Beiträge: 0
Registriert: Feb 2016


skoda#2
Benutzerbild von skoda
Beiträge: 1189
Wohnort: Boston MA
Registriert: Feb 2006

02.09.2009, 21:08

sieht super interessant aus - danke fuers posten und die erklaerungen~!
no one died when clinton lied
eisen#3
Benutzerbild von eisen
Beiträge: 2
Registriert: Jul 2010

26.07.2010, 12:56

hallo ich weiß der post ist schon etwas älter aber ich habe mit dieser anleitung den java socketserver und den flashck´lient nachgebaut nur leider will der flashclient nicht so ganz und ich bin auch noch anfänger mit flash..
beim verbindungsaufbau bekomme ich den fehler:
securityErrorHandler: [SecurityErrorEvent type="securityError" bubbles=false cancelable=false eventPhase=2 text="Error #2048: Verletzung der Sicherheits-Sandbox: file:///C|/Users/Matthias/Documents/ChatClient.swf kann keine Daten von localhost:4000 laden."]

es scheint aber dennoch eine verbindung zu stande zu kommen den in der textarea steht dann:

Willkommen beim Chatserver
Serversoftware v1.0
------------------------------
Angemeldet als Matze

wenn ich nun aber eine nachricht abschicken möchte kommt der fehler:

Error: Error #2002: Vorgang an ungültigem Socket versucht.
   at flash.net::Socket/flush()
   at ChatClient_fla::MainTimeline/fClickSend()

kann mir jemand evtl sagen woran das liegen kann? brauche ich eine crossdomain.xml oder soetwas??

wäre für jede hilfe dankbar..

grüße eisen
eisen#4
Benutzerbild von eisen
Beiträge: 2
Registriert: Jul 2010

01.08.2010, 12:58

niemand eine Idee?
Werbung
Beiträge: 0
Registriert: Feb 2016


Sebastian#5
Benutzerbild von SebastianFlashhilfe.de Moderator
Beiträge: 6981
Wohnort: München
Registriert: Jan 2002

01.08.2010, 14:12

Ruf dein swf mal über einen localhost auf und am besten eingebettet in einem html. Dann sollte der Security Error nicht kommen.
JavaScript & JavaFX Freelancer || Flashhilfe @ Twitter || XING Profil
Daichi#6
Benutzerbild von Daichi
Beiträge: 1
Registriert: Feb 2012

04.02.2012, 00:47

Das Thema hier interessiert mich immernoch sehr ich hoffe es ist noch nicht zu spät um noch Hilfe zu bekommen :)

Ich habe den Flash Clienten Nachgebaut bekomme aber beim Start diese Fehler Meldung

ActionScript:
1
2
3
4
5
6
7
8
9
10
11
12
Szene 1, Ebene 'Ebene 1', Bild 1, Zeile 97   1119: Zugriff auf eine möglicherweise nicht definierte Eigenschaft scrollV über einen Verweis mit statischem Typ fl.controls:TextArea
Szene 1, Ebene 'Ebene 1', Bild 1, Zeile 201   1119: Zugriff auf eine möglicherweise nicht definierte Eigenschaft maxScrollV über einen Verweis mit statischem Typ fl.controls:TextArea
Szene 1, Ebene 'Ebene 1', Bild 1, Zeile 201   1119: Zugriff auf eine möglicherweise nicht definierte Eigenschaft scrollV über einen Verweis mit statischem Typ fl.controls:TextArea
Szene 1, Ebene 'Ebene 1', Bild 1, Zeile 200   1119: Zugriff auf eine möglicherweise nicht definierte Eigenschaft scrollV über einen Verweis mit statischem Typ fl.controls:TextArea
Szene 1, Ebene 'Ebene 1', Bild 1, Zeile 200   1119: Zugriff auf eine möglicherweise nicht definierte Eigenschaft scrollV über einen Verweis mit statischem Typ fl.controls:TextArea
Szene 1, Ebene 'Ebene 1', Bild 1, Zeile 195   1119: Zugriff auf eine möglicherweise nicht definierte Eigenschaft maxScrollV über einen Verweis mit statischem Typ fl.controls:TextArea
Szene 1, Ebene 'Ebene 1', Bild 1, Zeile 184   1119: Zugriff auf eine möglicherweise nicht definierte Eigenschaft maxScrollV über einen Verweis mit statischem Typ fl.controls:TextArea
Szene 1, Ebene 'Ebene 1', Bild 1, Zeile 183   1119: Zugriff auf eine möglicherweise nicht definierte Eigenschaft scrollV über einen Verweis mit statischem Typ fl.controls:TextArea
Szene 1, Ebene 'Ebene 1', Bild 1, Zeile 184   1119: Zugriff auf eine möglicherweise nicht definierte Eigenschaft scrollV über einen Verweis mit statischem Typ fl.controls:TextArea
Szene 1, Ebene 'Ebene 1', Bild 1, Zeile 183   1119: Zugriff auf eine möglicherweise nicht definierte Eigenschaft scrollV über einen Verweis mit statischem Typ fl.controls:TextArea
Szene 1, Ebene 'Ebene 1', Bild 1, Zeile 178   1119: Zugriff auf eine möglicherweise nicht definierte Eigenschaft maxScrollV über einen Verweis mit statischem Typ fl.controls:TextArea
Szene 1, Ebene 'Ebene 1', Bild 1, Zeile 97   1119: Zugriff auf eine möglicherweise nicht definierte Eigenschaft maxScrollV über einen Verweis mit statischem Typ fl.controls:TextArea

Kann mir einer sagen warum das so ist?

Ich glaube es hat bei mir mal Funktioniert hatte es aber damals noch nicht gespeichert und nochmal alles aufgebaut und den Code kopiert aber jetzt Funktioniert es irgendwie nicht mehr Hilfe wäre echt super :)

AntwortenRegistrieren Seite1  

Schnellantwort

Du musst registriert sein, um diese Funktion nutzen zu können.

 
Ähnliche Beiträge zum Thema
Partner Webseiten: art-and-law.de  Mediengestalter.info   php-resource.de   phpforum.de   phpwelt.de   Pixelio.de   Scubacube.de  
Haftungsausschluss   Datenschutzerklärung   Hier Werben   Impressum
© 1999-2019 Sebastian Wichmann - Flashhilfe.de