. .. : Swf.hu 1.0 archívum : Swf.hu főoldal >>> : .. .


 
 
SEGÉDLETEK Flash+Java

SimpleChat V1.0 - Flash-Chat alkalmazás készítése (JAVA)
  feltöltve: 2004.02.02 | szerző: tenegri | olvasva: 11798 alkalommal

       
 

Szerveroldali program

A szerveroldali program három osztályt foglal magába:

SimpleChatServer
Ez tartalmazza a főprogramot, rátelepszik a figyelendő TCP portra, fogadja az új kapcsolatokat, s minden új kapcsolathoz létrehoz egy új szálat, mely az adatforgalmat bonyolítja.
SimpleChatThread
Ez tartja az egyes felhasználókkal a kapcsolatot, fogadja és feldolgozza a klienstől érkező üzeneteket, s amit szükséges, azt a SimpleChatBroadcaster-en keresztül továbbítja a többi felhasználóhoz.
SimpleChatBroadcaster
Ez az osztály tartja nyilván az aktív kapcsolatokat, s osztja szét köztük az üzeneteket.

Mi az amit tudnunk kell még mielőtt megírjuk a szerverprogramot? Csak néhány apró dolog a kliensoldal elkészítéséhez választott Flash socket-kezeléséről, amit ActionScript-ben az XMLSocket osztály valósít meg:
• az üzeneteket 0-s kódú karakter zárja, azaz a Flash minden socketen keresztül küldött szöveg végére illeszt egy 0-s kódú karaktert, s a kapott üzeneteket is e szerint értelmezi, vagyis akkor aktiválja az "üzenet érkezett" eseményt, ha a találkozik egy 0-s kódú karakterrel
• a socket-en átküldött szövegeket a Flash UTF-8 kódolásúnak feltételezi
• csak a 1023 feletti portszámok használhatók

Ha ezt a pár dolgot figyelembe vesszük, akkor már kezdhetjük is. Megjegyzem, hogy mivel az swf.hu nem egy Java-val foglalkozó portál, nem is folyok bele különösebben a Java program részleteibe.

SimpleChatServer.java

import java.net.*;
import java.io.*;
import java.util.*;

public class SimpleChatServer {

  public static void main(String[] args) throws IOException {
  ServerSocket serverSocket = null;
  Socket socket = null;
  SimpleChatThread thread = null;
  SimpleChatBroadcaster broadcaster = null;
  int port = 0;
  boolean listen = true;

    /* A konzolra irányuló kiírásokban a biztonság kedvéért nem használunk ékezeteket
    (és angolul lesznek a szövegek), kevés konzol ismeri ugyanis a Unicode-ot, amit
    a Java is használ, a kódlapokkal pedig most inkább ne molyoljunk :) */

    System.out.println("Simple Chat Server v1.0, by Rakos Attila, 2004");

    /* Nézzük meg a parancssorban kapott paramétereket */
    if (args.length != 1) {
      System.out.println("Wrong parameter count.");
      System.out.println("Usage: java SimpleChatServer port_number");
      System.exit(-1);
    }

    try {
      /* Alakítsuk számmá a parancssori paramétert */
      port = Integer.parseInt(args[0]);
    }
    catch (NumberFormatException e) {
      /* Hát ez nem szám volt... */
      System.out.println("Wrong parameter, it seems to be not a number.");
      System.out.println("Usage: java SimpleChatServer port_number");
      System.exit(-1);
    }

    /* Próbáljunk ráülni a megadott portra, ezen fogjuk fogadni a klienseket */
    try {
      serverSocket = new ServerSocket(port);
    }
    catch (IOException e) {
      /* Ez nem jött össze... */
      System.out.println("Couldn't initialize port " + args[0] + ".");
      System.exit(-1);
    }

    System.out.println("Chat server is running on port " + args[0] + "...");

    broadcaster = new SimpleChatBroadcaster();

    /* Egy végtelen ciklusban dolgozzuk fel a bejövő kapcsolatokat */
    while (listen) {
      /* Várakozás a következő kapcsolatra */
      socket = serverSocket.accept();
      /* Az új kapcsolatnak létrehozunk egy új szálat, ami kiszolgálja */
      thread = new SimpleChatThread(socket, broadcaster);
      /* Iduljon az a szál */
      thread.start();
    }

    /* Na, ez az ami sose fog lefutni :) Ha valakinek ez nem szimpatikus megoldás, akkor
    rakjon a fenti ciklusba valamit, ami megfelelő felhasználói beavatkozásra átbillenti
    a kilépési feltételt (ehhez állítani kell még a socket time-out-ján). Ctrl+C-vel így
    is lelőhető a program :)*/

    serverSocket.close();
  }
}

SimpleChatBroadcaster.java

import java.net.*;
import java.io.*;
import java.util.*;
import java.util.logging.*;

public class SimpleChatBroadcaster {

  /* A connections-ben tartjuk nyilván az élő kapcsolatokat. A Vector elemei
  SimpleChatThread objektumok */

  private Vector connections = null;

  /* A konstruktor */
  public SimpleChatBroadcaster() {
    connections = new Vector(10);
  }

  /* Új elemet ad hozzá a kapcsolatok listájához */
  public synchronized void addConnection(SimpleChatThread aConnection) {
  int i;

    /* Hozzáadjuk a listához az új elemet */
    connections.add(aConnection);

    /* és a nyilvántartott kapcsolatokat értesítjük az új belépőről (kivéve persze
    ez utóbbit, ő úgyis tudja magáról, hogy belépett :) */

    for (i = 0; i < connections.size(); i++)
      if (connections.get(i) != aConnection)
        ((SimpleChatThread)(connections.get(i))).sendUserConnected(aConnection.getNick());
  }

  /* Töröl egy elemet a kapcsolatok listájából */
  public synchronized void removeConnection(SimpleChatThread aConnection) {
  int i;

    /* Töröljük a listából a megadott kapcsolatot */
    connections.remove(aConnection);

    /* és értesítjük a megmaradtakat is erről a szomorú eseményről :) */
    for (i = 0; i < connections.size(); i++)
      ((SimpleChatThread)(connections.get(i))).sendUserDisconnected(aConnection.getNick());
  }

  /* A nyilvántartott kapcsolatok számát adja vissza */
  public synchronized int numberOfConnections() {
    return connections.size();
  }

  /* A nyilvántartott kapcsolatoknak elküld egy üzenetet (aMessage), melynek feladója aSender.
  Az aIncludeSender paraméter mondja meg, hogy a feladó is megkapja-e az üzentete */

  public synchronized void broadcastMessage(SimpleChatThread aSender, String aMessage, boolean aIncludeSender) {
  int i;

    for (i = 0; i < connections.size(); i++)
      if (connections.get(i) != aSender || aIncludeSender)
        ((SimpleChatThread)(connections.get(i))).sendMessage(aSender, aMessage);
  }

  /* Egy tájékoztató üzentet küld szét a nyilvántartott kapcsolatoknak.
  Az aIncludeSender paraméter mondja meg, hogy a feladó is megkapja-e az üzentete */

  public synchronized void broadcastNotification(SimpleChatThread aSender, String aNotification, boolean aIncludeSender) {
  int i;

    for (i = 0; i < connections.size(); i++)
      if (connections.get(i) != aSender || aIncludeSender)
        ((SimpleChatThread)(connections.get(i))).sendNotification(aNotification);
  }

  /* A nyilvántartott kapcsolatokhoz tartozó felhasználóneveket adja vissza egy String tömbben */
  public synchronized String[] getUserList() {
  int i;
    String[] users = new String[connections.size()];

    for (i = 0; i < connections.size(); i++)
      users[i] = ((SimpleChatThread)(connections.get(i))).getNick();

    return users;
  }

  /* A paraméterként megadott felhasználónév alapján visszaadja az ahhoz tartozó
  kapcsolat-objektum azonosítóját (ha nincs, akkor null-t) */

  public synchronized SimpleChatThread getConnectionByNick(String aNick) {
  int i;
  SimpleChatThread result = null;

    for (i = 0; i < connections.size(); i++)
      if (aNick.toUpperCase().equals(((SimpleChatThread)(connections.get(i))).getNick().toUpperCase())) {
        result = (SimpleChatThread)connections.get(i);
        break;
      }

    return result;
  }
}

SimpleChatThread.java

import java.net.*;
import java.io.*;
import javax.xml.parsers.*;
import org.w3c.dom.*;
import org.xml.sax.*;

public class SimpleChatThread extends Thread {
  private Socket socket;
  private SimpleChatBroadcaster broadcaster;
  /* Ezen keresztül küldjük a socket-re a kimenő adatokat */
  private PrintStream output;
  /* Ezen keresztül olvassuk a socket-en át érkező adatokat */
  private BufferedReader input;
  /* Ezek az XML kezeléshez kellenek */
  private DocumentBuilderFactory dbf;
  private DocumentBuilder db;
  /* Ez tárolja a felhasználónevet */
  private String nick;

  /* A konstruktor */
  public SimpleChatThread(Socket aSocket, SimpleChatBroadcaster aBroadcaster) {
    super("SimpleChatThread");

    /* Eltároljuk a paraméterként kapott objektumokat */
    socket = aSocket;
    broadcaster = aBroadcaster;

    /* Létrehozzuk az XML értelmezéshez szükséges objektumpéldányokat */
    dbf = DocumentBuilderFactory.newInstance();
    try {
      db = dbf.newDocumentBuilder();
    } catch (ParserConfigurationException e) {
      e.printStackTrace();
    }
  }

  /* A szál lényegi része */
  public void run() {
  String inputText;
  Element data;

    try {
      /* Létrehozzuk a ki- és bemenetet kezelő objektumokat */
      output = new PrintStream(socket.getOutputStream(), false);
      input = new BufferedReader(new InputStreamReader(socket.getInputStream()));

      /* Ha sikerült valamit olvasni a socket-ről (ami így elsőre a LOGIN üzenet kellene,
      hogy legyen */

      if ((inputText = readData()) != null) {
        try {
          /* Megpróbáljuk XML-ként értelmezni a fogadott adatokat */
          data = db.parse(new InputSource(new StringReader(inputText))).getDocumentElement();
          /* Ha az elemnév megfelelő és az elem még tartalmaz is adatot... */
          if (data.getTagName() == "LOGIN" && data.hasChildNodes()) {
            /* Eltároljuk a felhasználónevet */
            nick = data.getFirstChild().getNodeValue().trim();

            /* Ha üres a felhasználónév vagy van már ilyen... */
            if (nick.length() == 0 || broadcaster.getConnectionByNick(nick) != null)
              /* Akkor értesítést küldünk erről, majd mást nem csinálunk, így a kapcsolat lezárásra kerül */
              sendStatus(102, "Van már ilyen néven belépett felhasználó vagy nincs megadva felhasználónév!");
            else {
              /* Nem volt még ilyen felhasználónév, küldünk erről egy nyugtázó üzenetet */
              sendStatus(100, "Kerülj beljebb " + nick + "! Köszöntünk a chat-en!");

              /* Mivel belépés utána kapcsolat már élőnek tekinthető, felvesszük a nyilvántartott
              kapcsolatok listájába */

              broadcaster.addConnection(this);

              /* Amíg jön adat a socket-en át, addig olvasgatunk róla */
              while ((inputText = readData()) != null) {
                try {
                  /* Ha megvan egy adag adat, akkor próbáljuk XML-ként értelmezni */
                  data = db.parse(new InputSource(new StringReader(inputText))).getDocumentElement();

                  /* Ha üzenet érkezett a felhasználótól */
                  if (data.getTagName().equals("MESSAGE"))
                    processMessage(data);
                  else
                  /* Ha a felhasználó a belépett felhasználók listájára kiváncsi */
                  if (data.getTagName().equals("GET_USERLIST"))
                    processGetUserList(data);
                  else
                  /* Ha a felhasználó bontaná a kapcsolatot */
                  if (data.getTagName().equals("CLOSE"))
                    break;
                  else
                    /* Ha nem ismert az elemnév, akkor ezt tudatjuk a klienssel is */
                    sendStatus(104, "Ismeretlen típusú üzenet érkezett a klienstől.");
                } catch (SAXException e) {
                  /* Valami gond volt az XML értelmezésével, akkor erről küldünk egy értesítést a kliensnek */
                  sendStatus(103, "Nem értelmezhető XML üzenet érkezett a klienstől.");
                }
              }

              /* Töröljük a kapcsolatot a nyilvántartás listájából */
              broadcaster.removeConnection(this);
            }
          }
        } catch (SAXException e) {
          /* Valami gond volt a login XML értelmezésével, úgyhogy küldünk erről egy üzenetet (majd
          végül is bontjuk a kapcsolatot)*/

          sendStatus(101, "Nem értelmezhető a bejelentkező üzenet!");
        }
      }

      /* Lezárjuk a nyitott adatfolyamokat */
      output.close();
      input.close();
      socket.close();

    } catch (IOException e) {
      /* Valami gond történt a ki- és bemenet inicializálása során */
      e.printStackTrace();
    }
  }

  /* Visszaadja a felhasználónevet */
  public synchronized String getNick() {
    return nick;
  }

  /* Beolvassa a következő üzenetet a socket-ről. Az üzenet végét a 0-s karakter jelzi */
  private String readData() throws IOException {
    StringBuffer s = new StringBuffer();
    int ch;

    while ((ch = input.read()) > 0)
      s.append((char)ch);

    return ch == 0 ? s.toString() : null;
  }

  /* Elküld egy üzenetet a kliensnek a paraméterként megadott fladóval és szöveggel */
  public synchronized void sendMessage(SimpleChatThread aSender, String aMessage) {
  Document doc;
  Element elem;

    doc = db.newDocument();
    elem = doc.createElement("MESSAGE");
    elem.setAttribute("SENDER", aSender.getNick());
    elem.appendChild(doc.createTextNode(aMessage));
    doc.appendChild(elem);

    sendText(doc.getFirstChild().toString());
  }

  /* Tájékoztató üzenetet küld a kliensnek */
  public synchronized void sendNotification(String aNotification) {
  Document doc;
  Element elem;

    doc = db.newDocument();
    elem = doc.createElement("NOTIFICATION");
    elem.appendChild(doc.createTextNode(aNotification));
    doc.appendChild(elem);

    sendText(doc.getFirstChild().toString());
  }

  /* Üzenetet küld a kliensnek, hogy új felhasználó csatlakozott a chat-hez */
  public synchronized void sendUserConnected(String aName) {
  Document doc;
  Element elem;

    doc = db.newDocument();
    elem = doc.createElement("USER_CONNECTED");
    elem.setAttribute("NAME", aName);
    doc.appendChild(elem);

    sendText(doc.getFirstChild().toString());
  }

  /* Üzenetet küld a kliensnek, hogy egy felhasználó kilpett a chat-ről */
  public synchronized void sendUserDisconnected(String aName) {
  Document doc;
  Element elem;

    doc = db.newDocument();
    elem = doc.createElement("USER_DISCONNECTED");
    elem.setAttribute("NAME", aName);
    doc.appendChild(elem);

    sendText(doc.getFirstChild().toString());
  }

  /* Elküldi a kliensnek a belépett felhasználók listáját */
  public synchronized void sendUserList(String[] aUsers) {
  int i;
  Document doc;
  Element elem;

    doc = db.newDocument();
    elem = doc.createElement("USERLIST");
    doc.appendChild(elem);

    for (i = 0; i < aUsers.length; i++) {
      elem = doc.createElement("USER");
      elem.setAttribute("NAME", aUsers[i]);
      doc.getFirstChild().appendChild(elem);
    }

    sendText(doc.getFirstChild().toString());
  }

  /* Hibaüzenetet küld a kliens számára */
  private void sendStatus(int aStatusCode, String aStatusMessage) {
  Document doc;
  Element elem;

    doc = db.newDocument();
    elem = doc.createElement("STATUS");
    elem.setAttribute("CODE", Integer.toString(aStatusCode));
    elem.appendChild(doc.createTextNode(aStatusMessage));
    doc.appendChild(elem);

    sendText(doc.getFirstChild().toString());
  }

  /* Feldolgozza a klienstől érkezett új üzenetet és a broadcaster-en keresztül
  továbbítja a többi felhasználóhoz */

  private void processMessage(Element aData) {
  String msg;

    if (aData.hasChildNodes()) {
      msg = aData.getFirstChild().getNodeValue().trim();
      if (msg.length() != 0)
        broadcaster.broadcastMessage(this, msg, true);
    }
  }

  /* Feldolgozza a felhasználók listájának elküdésére vonatkozó kérést és a listát
  elküldi a kliensnek */

  private void processGetUserList(Element aData) {
    sendUserList(broadcaster.getUserList());
  }

  /* Szöveget küld a socket-en keresztül, s a végére egy 0-s karaktert illeszt */
  private void sendText(String aText) {
    output.print(aText + "\0");
    output.flush();
  }
}

A forráskódot lefordítva, majd a SimpleChatServer.class osztályt elindítva (a kommunikációhoz használandó port számával mint paraméterrel) már fut is a chat-szerver és várja a kliensek jelentkezését.

Fordítás: javac SimpleChatServer.java SimpleChatBroadcaster.java SimpleChatThread.java
Futtatás: java SimpleChatServer 4321

 
       
 
 

© Devnet.hu. A segédletek semmilyen formában nem másolhatók, publikálhatók a Devnet.hu és a szerzők közös írásos engedélye nélkül.
 
. .. : Swf.hu 1.0 archívum : Swf.hu főoldal >>> : .. .