Tutorial: Flash-Chat via Java Socketserver [Flash 10]
| 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 ü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 ü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<ChatServerHandler> handlers = 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 übergeben wird
private String ucolor; //Die gewählte Farbe, die der Client gewählt hat
private String room; //Der Chatraum, der übergeben wurde
private Socket socket;
private BufferedReader in;
private PrintWriter out;
private String login;
// Ende Attribute1
public ChatServerHandler(Socket socket) throws 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 ü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 = 0; i < handlers.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 = 0; i < handlers.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 = 0; i < handlers.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 ü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 = 0; i < handlers.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();
}
} ?>
|
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 = 0; i < handlers.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
luzzeEs gibt Leute, die können Ihren Stammbaum bis zu denen zurückverfolgen, die noch darauf saßen. ^^ | Geändert von Luzze am 02.09.09 um 23:16 Uhr | |
| Werbung | Beiträge: 0 Registriert: Feb 2016
| | | 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 | | | 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 | | | Beiträge: 2 Registriert: Jul 2010
| 01.08.2010, 12:58
niemand eine Idee? | | Werbung | Beiträge: 0 Registriert: Feb 2016
| | | 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 | | | 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 :) | |
| Ähnliche Beiträge zum Thema | 28.03.2018 - WladSpiel 06.07.2016 - leonardo225 05.03.2017 - cokebaby 04.04.2016 - DiOmega |
|
Flashhilfe.de Flash Platform Tipps & Tutorials Flash Platform Andere Programmiersprachen Jobangebote Diskussionen
Flashhilfe News 
Regeln & Bedingungen
|