27、深入剖析聊天系统的类设计

深入剖析聊天系统的类设计

在开发聊天系统时,合理的类设计至关重要。它不仅能提高系统的可维护性和扩展性,还能确保系统的性能和稳定性。下面将详细介绍聊天系统中各个关键类的设计和功能。

1. 打开存储并管理上下文

首先,需要打开存储并将其作为上下文的值进行管理。以下是相关代码:

// Öffne den Store
try {
    Configuration theStoreConfig =
        ConfigManager.getConfigManager().getConfiguration(
            aContextName, theStorename, true);
    theStore = StoreFactory.getInstance(theStoreConfig);
}
catch (PersistenceException pe) {
    throw new SMIException(pe);
}
// Hinterlege den Store als Wert dieses Kontextes
// unter dem Schlüssel C_Store
putValue(C_Store, theStore);

这段代码的操作步骤如下:
1. 通过 ConfigManager 获取存储配置。
2. 使用 StoreFactory 根据配置实例化存储对象。
3. 将存储对象作为上下文的值,以 C_Store 为键进行存储。

2. 管理聊天参与者

为了管理聊天参与者,引入了 ChatSwitch 类。它继承自 SMIEventSwitch ,并在 init 方法中添加了用户列表。以下是 ChatSwitch 类的代码:

package de.webapp.Chat ;
import de.webapp.Framework.SMI.* ;
import java.util.Hashtable;
public class ChatSwitch extends SMIEventSwitch implements C_Chat {
    /** Fügt dem Switch eine Nutzerliste hinzu. */
    public void init(String aSwitchName, I_SMIContext aSMIContext)
        throws SMIException {
        super.init(aSwitchName, aSMIContext);
        putValue(C_Userlist, new Hashtable());
    }
}

操作步骤如下:
1. 继承 SMIEventSwitch 类。
2. 重写 init 方法。
3. 在 init 方法中,使用 putValue 方法将一个新的 Hashtable 作为用户列表,以 C_Userlist 为键存储在 SMIEventSwitch 中。

3. 方便访问用户列表和存储的基类

为了方便后续的 I_SMICommandListener 类访问用户列表和存储,创建了 ChatBean 基类。它封装了用户对象的管理,并对 executeSMIEvent 方法进行了重写。以下是 ChatBean 类的代码:

package de.webapp.Chat;
import java.util.Hashtable;
import de.webapp.Framework.SMI.*;
import de.webapp.Framework.Persistence.*;
/** Basisklasse für weitere ChatBeans */
public class ChatBean extends SMICommandListener implements C_Chat {
    /** Setzt die Nutzerliste und führt Folgekommando aus */
    public void executeSMIEvent(SMIEvent aSMIEvent) {
        SMICommand aSMICommand = (SMICommand)aSMIEvent;
        // Nutzerliste setzen
        aSMICommand.putValue(C_Userlist, getUserlist());
        // Kommando ausführen
        super.executeSMIEvent(aSMICommand);
        // Gegebenenfalls Folgekommando ausführen
        String theFollowUpCommand =
            (String)aSMICommand.getValue(C_FollowUpCommand);
        if (theFollowUpCommand != null) {
            aSMICommand.setCommand(theFollowUpCommand);
            aSMICommand.removeValue(C_FollowUpCommand);
            getSMIEventSwitch().executeSMIEvent(aSMICommand);
        }
    }
    /** Leere Methode, falls executeSMIEvent() ausreicht */
    public void noop(SMICommand aSMICommand) {}
    /** Gibt das Nutzer-Objekt aus der Session zurück */
    protected User getUser(SMICommand aSMICommand) {
        return (User)aSMICommand.getSessionValue(C_User);
    }
    /** Hinterlegt ein Nutzer-Objekt in der Session */
    protected void setUser(SMICommand aSMICommand, User aUser) {
        aSMICommand.putSessionValue(C_User, aUser);
    }
    /** Entfernt ein Nutzer-Objekt aus der Session */
    protected void removeUser(SMICommand aSMICommand) {
        aSMICommand.removeSessionValue(C_User);
    }
    /** Gibt die Nutzerliste des Switches zurück */
    protected Hashtable getUserlist() {
        return (Hashtable)getSMIEventSwitch().getValue(C_Userlist);
    }
    /** Gibt den Store des I_SMIContextes zurück */
    protected I_Store getStore() {
        return (I_Store)getSMIEventSwitch().getSMIContext().getValue(C_Store);
    }
}

操作步骤如下:
1. 继承 SMICommandListener 类。
2. 重写 executeSMIEvent 方法:
- 将用户列表设置到 SMICommand 中。
- 执行当前命令。
- 若存在后续命令,设置命令并执行。
3. 提供 getUser 、 setUser 和 removeUser 方法,用于管理用户对象在会话中的存储和获取。
4. 提供 getUserlist 和 getStore 方法,用于获取用户列表和存储对象。

4. 消息类的设计

在聊天系统中,消息是核心元素。通过 Message 类来封装消息,包含发送者和消息文本。以下是 Message 类的代码:

package de.webapp.Chat ;
/** Kapselt eine Nachricht */
public class Message {
    protected User myFrom;
    protected String myText;
    /** Konstruktor */
    public Message(User aFrom, String aText) {
        myFrom = aFrom;
        myText = aText;
    }
    /** Gibt den Absender zurück */
    public User getFrom() {
        return myFrom;
    }
    /** Gibt den Nachrichtentext zurück */
    public String getText() {
        return myText;
    }
}

操作步骤如下:
1. 定义 Message 类,包含 User 类型的发送者和 String 类型的消息文本。
2. 提供构造函数,用于初始化发送者和消息文本。
3. 提供 getFrom 和 getText 方法,用于获取发送者和消息文本。

5. 用户对象的设计

用户对象是聊天系统中的重要组成部分。 User 类继承自 Persistence ,实现了 I_Persistence 接口,并具备用户管理和消息处理的功能。以下是 User 类的部分代码:

package de.webapp.Chat;
import java.util.*;
import java.io.*;
import java.sql.*;
import javax.servlet.http.*;
import de.webapp.Framework.Persistence.*;
import de.webapp.Framework.Log.*;
/** Nutzer des Chats */
public class User extends Persistence
    implements C_Chat, HttpSessionBindingListener {
    /** Name des Nutzers */
    protected String myName;
    /** Paßwort des Nutzers */
    protected String myPassword;
    /** Sendefarbe des Nutzers */
    protected Integer myColor;
    /** Chatraum, in dem sich der Nutzer befindet */
    protected transient ChatRoom myChat;
    /** Nachrichtenschlange */
    protected transient Vector myMessageQueue;
    /** Zeitpunkt, zu dem die letzte Nachricht gesendet wurde */
    protected transient long myLastMessage;
    /** Zeigt an, ob das asynchrone Schreiben der Nachrichten gestoppt wurde */
    protected transient boolean stopped;
    /** Aktuelle benutzter I_MessageWriter */
    protected transient I_MessageWriter myWriter;
    /** Geschützter -Konstruktor */
    protected User() {}
    /** Konstruktor mit Peer */
    public User(I_PersistencePeer aPeer) throws PersistenceException {
        super(aPeer);
        init();
    }
    /** Konstruktor mit Peer und ResultSet */
    public User(I_PersistencePeer aPeer, ResultSet rs)
        throws PersistenceException {
        super(aPeer, rs);
        init();
    }
    /** Konstruktor mit Peer, ResultSet und ObjectIdentifier (für MS-Access) */
    public User(I_PersistencePeer aPeer, ResultSet rs,
        String aObjectIdentifier)
        throws PersistenceException  {
        this(aPeer);
        this.bind(rs, aObjectIdentifier);
    }
    /** Initialisierung */
    protected void init() {
        myMessageQueue = new Vector();
        myChat = null;
        myWriter = null;
        stopped = false;
    }
    // 其他方法...
}

操作步骤如下:
1. 继承 Persistence 类,实现 C_Chat 和 HttpSessionBindingListener 接口。
2. 定义用户的属性,如姓名、密码、发送颜色等。
3. 提供多个构造函数,用于初始化用户对象。
4. 提供 init 方法,初始化消息队列、聊天室、消息写入器等。

6. 消息发送器

ChatDispatcher 类负责消息的发送。它继承自 ChatBean ,实现了 sendMessage 命令。以下是 ChatDispatcher 类的代码:

package de.webapp.Chat;
import java.util.*;
import java.io.*;
import de.webapp.Framework.SMI.*;
import de.webapp.Framework.Persistence.*;
import de.webapp.Framework.Log.*;
/** Übernimmt das Versenden von Nachrichten */
public class ChatDispatcher extends ChatBean implements C_Chat {
    /** Sendet eine Nachricht */
    public void send(SMICommand aSMICommand) throws PersistenceException {
        // Nutzer überhaupt noch eingeloggt?
        User theUser = getUser(aSMICommand);
        if (theUser == null) {
            aSMICommand.putValue(C_Template,
                aSMICommand.getValue("userNotLoggedIn"));
            return;
        }
        // Zeitstempel setzen
        theUser.touch();
        // Parameter aus der Anfrage lesen
        // theTo enthält die ObjectIdentifier der Adressaten
        String[] theTo = aSMICommand.getRequest().getParameterValues(C_To);
        // theText enthält die Nachricht
        String theText = (String)aSMICommand.getValue(C_Message);
        // wenn theToAll != null ist, ist die Nachricht an alle
        String theToAll = (String)aSMICommand.getValue(C_All);
        // theColor enthält die Farbe als Integer
        Integer theColor = new Integer((String)aSMICommand.getValue(C_Color));
        if (!theColor.equals(theUser.getColor())) {
            // gegebenenfalls Farbe persistent speichern
            updateColor(aSMICommand);
        }
        // Nachricht nur versenden, wenn es auch Adressaten gibt
        if (theTo != null || theToAll != null) {
            Message theMessage = new Message(theUser, theText);
            if (theToAll==null) {
                // Private Messages einzeln zustellen
                // eine Kopie an den Absender soll in jedem Fall dabei sein
                theMessage.getFrom().handleMessage(theMessage);
                for (int i=0; i < theTo.length; i++) {
                    User aUser = (User)getUserlist().get(theTo[i]);
                    if (aUser != null && aUser != theMessage.getFrom())
                        aUser.handleMessage(theMessage);
                }
            }
            else {
                // allgemeine Methode für alle
                for (Enumeration e = getUserlist().elements();
                    e.hasMoreElements();) {
                    User aUser = (User)e.nextElement();
                    if (aUser != null) aUser.handleMessage(theMessage);
                }
            }
        }
    }
    /** Speichert das NutzerObjekt mit neuer Farbe */
    protected void updateColor(SMICommand aSMICommand)
        throws PersistenceException {
        // Store besorgen
        I_Store theStore = getStore();
        User theUser = getUser(aSMICommand);
        synchronized(theStore) {
            theUser.setColor(
                new Integer((String)aSMICommand.getValue(C_Color)));
            try {
                theStore.begin();
                theUser.update();
                theStore.commit();
            }
            catch (Throwable t) {
                theStore.rollback();
                if (t instanceof PersistenceException) {
                    throw (PersistenceException)t;
                }
                throw new SMIEventListenerException(t);
            }
        }
    }
}

操作步骤如下:
1. 继承 ChatBean 类。
2. 实现 send 方法:
- 检查用户是否登录。
- 设置用户的时间戳。
- 读取请求参数,如收件人、消息内容、是否发送给所有人、颜色等。
- 若颜色有变化,调用 updateColor 方法更新颜色。
- 根据收件人情况,发送消息给指定用户或所有人。
3. 实现 updateColor 方法,用于更新用户的颜色并持久化存储。

7. 用户注册和更新

ChatRegistration 类负责用户的注册和更新操作。以下是 ChatRegistration 类的代码:

package de.webapp.Chat;
import java.util.*;
import java.io.*;
import de.webapp.Framework.SMI.*;
import de.webapp.Framework.Persistence.*;
import de.webapp.Framework.Log.*;
/** Manipulation eines Nutzer-Objekts */
public class ChatRegistration extends ChatBean implements C_Chat {
    /** Erzeugt ein Nutzer-Objekt und hinterlegt es in der HttpSession */
    public void create(SMICommand aSMICommand) throws PersistenceException {
        // Store besorgen.
        I_Store theStore = getStore();
        User theUser;
        // Store nach neuem Persistence des Typs "User" fragen
        theUser = (User)theStore.newPersistence("User");
        // Attribute setzen und eventuell Fehlermeldung ausgeben
        if (!setAttributes(aSMICommand, theUser)) {
            aSMICommand.putValue(C_ErrorMessage,
                "Ihre Eingaben waren fehlerhaft. "
                + "Bitte versuchen Sie es nocheinmal.");
            aSMICommand.putValue(C_Template, aSMICommand.getValue("failure"));
            return;
        }
        // Store sichern
        synchronized(theStore) {
            try {
                theStore.begin();
                theUser.create();
                theStore.commit();
                // User in der HttpSession hinterlegen -> ChatBean
                setUser(aSMICommand, theUser);
            }
            // wenn canCreate() false zurückgibt, wird eine
            // PersistenceRuntimeException ausgelöst.
            // => Fehlermeldung ausgeben.
            catch (PersistenceRuntimeException pre) {
                theStore.rollback();
                aSMICommand.putValue(C_ErrorMessage,
                    "Der Name, den Sie gewählt haben, ist belegt.");
                aSMICommand.putValue(C_Template,
                    aSMICommand.getValue("failure"));
            }
            catch (Throwable t) {
                theStore.rollback();
                if (t instanceof PersistenceException) {
                    throw (PersistenceException)t;
                }
                throw new SMIEventListenerException(t);
            }
        }
    }
    /** Speichert Änderungen eines Nutzer-Objekts in der Datenbank */
    public void update(SMICommand aSMICommand) throws PersistenceException {
        // Nutzer aus der HttpSession lesen
        User theUser = getUser(aSMICommand);
        // falls kein Nutzer vorhanden, Fehlermeldung ausgeben
        if (theUser == null) {
            aSMICommand.putValue(C_Template,
                aSMICommand.getValue("userNotLoggedIn"));
            return;
        }
        // Store besorgen.
        I_Store theStore = getStore();
        // Attribute setzen und eventuell Fehlermeldung ausgeben
        if (!setAttributes(aSMICommand, theUser)) {
            aSMICommand.putValue(C_ErrorMessage,
                "Ihre Eingaben waren fehlerhaft. "
                + "Bitte versuchen Sie es noch einmal.");
            aSMICommand.putValue(C_Template, aSMICommand.getValue("failure"));
            return;
        }
        // Store sichern
        synchronized(theStore) {
            try {
                theStore.begin();
                theUser.update();
                theStore.commit();
            }
            // wenn canUpdate() false zurückgibt, wird eine
            // PersistenceRuntimeException ausgelöst.
            // => Fehlermeldung ausgeben.
            catch (PersistenceRuntimeException pre) {
                theStore.rollback();
                aSMICommand.putValue(C_ErrorMessage,
                    "Der Name, den Sie gewählt haben, ist belegt.");
                aSMICommand.putValue(C_Template,
                    aSMICommand.getValue("failure"));
            }
            catch (Throwable t) {
                theStore.rollback();
                if (t instanceof PersistenceException) {
                    throw (PersistenceException)t;
                }
                throw new SMIEventListenerException(t);
            }
        }
    }
    /** Setzt die Attribute eines Persistence und gibt false zurück,
        falls die Werte nicht sinnvoll sind */
    protected boolean setAttributes(SMICommand aSMICommand, User aUser) {
        // Werte aus dem Kommando lesen.
        String thePassword1 = (String)aSMICommand.getValue(C_Password);
        String thePassword2 = (String)aSMICommand.getValue(C_Password2);
        String theUsername = (String)aSMICommand.getValue(C_Username);
        // 其他逻辑...
    }
}

操作步骤如下:
1. 继承 ChatBean 类。
2. 实现 create 方法:
- 从存储中获取新的用户对象。
- 设置用户属性,若属性设置失败,显示错误信息。
- 开启事务,创建用户对象并提交事务。
- 将用户对象存储到会话中。
- 若创建失败,回滚事务并显示错误信息。
3. 实现 update 方法:
- 从会话中获取用户对象,若用户不存在,显示错误信息。
- 设置用户属性,若属性设置失败,显示错误信息。
- 开启事务,更新用户对象并提交事务。
- 若更新失败,回滚事务并显示错误信息。
4. 实现 setAttributes 方法,用于设置用户属性。

通过以上这些类的设计和实现,我们可以构建一个功能完善的聊天系统。每个类都有其特定的职责,相互协作,确保系统的正常运行。在实际开发中,我们可以根据具体需求对这些类进行扩展和优化。

下面是一个简单的类关系图,展示了各个类之间的关系:

classDiagram
    class SMIEventSwitch
    class SMICommandListener
    class Persistence
    class I_Persistence
    class I_SMIContext
    class I_Store
    class I_MessageWriter
    class ChatContext
    class ChatSwitch
    class ChatBean
    class Message
    class User
    class ChatDispatcher
    class ChatRegistration
    class HTMLMessageWriter
    SMIEventSwitch <|-- ChatSwitch
    SMICommandListener <|-- ChatBean
    Persistence <|-- User
    User ..|> I_Persistence
    ChatContext *-- I_Store
    ChatSwitch *-- I_SMIContext
    ChatBean *-- ChatSwitch
    ChatBean *-- I_Store
    Message *-- User
    User *-- I_MessageWriter
    ChatBean <|-- ChatDispatcher
    ChatBean <|-- ChatRegistration
    I_MessageWriter <|.. HTMLMessageWriter

这个类关系图清晰地展示了各个类之间的继承、关联和依赖关系,有助于我们更好地理解整个聊天系统的架构。

综上所述,合理的类设计是构建高效、稳定聊天系统的基础。通过对各个类的功能和职责进行明确划分,我们可以提高代码的可维护性和扩展性,为后续的开发和优化提供便利。

深入剖析聊天系统的类设计

8. 消息写入器接口与实现

在聊天系统中,消息的写入需要一个统一的接口和具体的实现。 I_MessageWriter 接口定义了消息写入的基本方法,而 HTMLMessageWriter 类则是该接口的一个具体实现,用于将消息以 HTML 格式写入。

以下是 I_MessageWriter 接口的代码:

package de.webapp.Chat;
import java.io.IOException;
/** Definiert einen Nachrichten-Schreiber */
public interface I_MessageWriter {
    /** Schreibt eine Nachricht */
    public void write(Message aMessage) throws IOException;
    /** Zeigt an, ob dieser I_MessageWriter noch schreiben kann */
    public boolean canWrite();
}

操作步骤如下:
1. 定义 write 方法,用于写入消息,可能会抛出 IOException 异常。
2. 定义 canWrite 方法,用于检查是否还能继续写入消息。

以下是 HTMLMessageWriter 类的代码:

package de.webapp.Chat;
import java.io.*;
public class HTMLMessageWriter implements I_MessageWriter, C_Chat {
    public HTMLMessageWriter(Writer aWriter) {
        myWriter = aWriter;
    }
    protected int mySentBytes;
    protected Writer myWriter;
    /** Schreibt eine Nachricht mit dem aktuellen Writer. */
    public void write(Message aMessage) throws IOException {
        StringBuffer theOutput = new StringBuffer();
        theOutput.append(aMessage.getFrom().getName());
        theOutput.append(": <font color=\"" 
            + C_Colors[aMessage.getFrom().getColor().intValue()]+ "\">");
        theOutput.append(aMessage.getText());
        theOutput.append("</font><p>");
        myWriter.write(theOutput.toString());
        myWriter.flush();
        mySentBytes += theOutput.length();
    }
    /** Überprüft, ob noch geschrieben werden kann. */
    public boolean canWrite() {
        if (myWriter instanceof PrintWriter) {
            ((PrintWriter)myWriter).println();
            return (!((PrintWriter)myWriter).checkError() && mySentBytes < C_MaxBytes);
        }
        try {
            myWriter.flush();
        }
        catch (IOException ioe) {
            return false;
        }
        return true;
    }
}

操作步骤如下:
1. 实现 I_MessageWriter 接口和 C_Chat 接口。
2. 提供构造函数,接收一个 Writer 对象。
3. 实现 write 方法:
- 构建包含发送者姓名、消息内容和颜色的 HTML 字符串。
- 使用 Writer 对象写入该字符串并刷新。
- 记录已发送的字节数。
4. 实现 canWrite 方法:
- 若 Writer 是 PrintWriter 类型,检查是否有错误且已发送字节数是否小于最大字节数。
- 否则,尝试刷新 Writer ,若抛出异常则返回 false ,否则返回 true 。

9. 用户对象的持久化与属性管理

用户对象的持久化和属性管理是聊天系统的重要组成部分。 User 类继承自 Persistence 类,实现了 I_Persistence 接口,确保用户对象可以持久化存储。同时,它还管理用户的属性,如姓名、密码和发送颜色。

以下是 User 类的属性和方法相关代码:

package de.webapp.Chat;
import java.util.*;
import java.io.*;
import java.sql.*;
import javax.servlet.http.*;
import de.webapp.Framework.Persistence.*;
import de.webapp.Framework.Log.*;
/** Nutzer des Chats */
public class User extends Persistence
    implements C_Chat, HttpSessionBindingListener {
    /** Name des Nutzers */
    protected String myName;
    /** Paßwort des Nutzers */
    protected String myPassword;
    /** Sendefarbe des Nutzers */
    protected Integer myColor;
    // 其他属性...
    /** Gibt den Namen zurück */
    public String getName() {
        return myName;
    }
    /** Setzt den Namen */
    public void setName(String aName) {
        myName = aName;
        // Zustandsänderung!
        setModified();
    }
    /** Gibt das Paßwort zurück */
    public String getPassword() {
        return myPassword;
    }
    /** Setzt das Paßwort */
    public void setPassword(String aPassword) {
        myPassword = aPassword;
        // Zustandsänderung!
        setModified();
    }
    /** Gibt die Farbe zurück */
    public Integer getColor() {
        return myColor;
    }
    /** Setzt die Farbe */
    public void setColor(Integer aColor) {
        myColor = aColor;
        setModified();
    }
    // 其他方法...
}

操作步骤如下:
1. 定义用户的属性,如 myName 、 myPassword 和 myColor 。
2. 提供 getName 、 getPassword 和 getColor 方法,用于获取用户的属性值。
3. 提供 setName 、 setPassword 和 setColor 方法,用于设置用户的属性值,并在属性修改时调用 setModified 方法标记状态更改。

10. 用户对象的验证与持久化操作

为了确保用户对象的唯一性和数据的一致性, User 类提供了 canCreate 和 canUpdate 方法进行验证,同时实现了持久化操作的相关方法。

以下是相关代码:

package de.webapp.Chat;
// 导入相关包...
/** Nutzer des Chats */
public class User extends Persistence
    implements C_Chat, HttpSessionBindingListener {
    // 属性和构造函数...
    /** Prüft, ob ein Nutzer-Objekt erzeugt werden darf */
    public boolean canCreate() throws PersistenceException {
        return isNameFree();
    }
    /** Prüft, ob ein Nutzer-Objekt persistent geändert werden darf */
    public boolean canUpdate() throws PersistenceException {
        return isNameFree();
    }
    /** Zeigt an, ob ein Name bereits von einem anderen Objekt belegt ist */
    protected boolean isNameFree() throws PersistenceException {
        I_Persistence sameName =
            getPeer().getRetriever().retrieve(getAttribute("Name"), myName);
        return (sameName == null || sameName == this);
    }
    /** Hilfsmethode zum Erlangen einen PersistenceAttribute-Objekts */
    protected PersistenceAttribute getAttribute(String aAttributeName) {
        return getPeer().getType().getAttributeWithName(aAttributeName);
    }
    // 其他方法...
}

操作步骤如下:
1. 实现 canCreate 方法,调用 isNameFree 方法检查是否可以创建用户对象。
2. 实现 canUpdate 方法,同样调用 isNameFree 方法检查是否可以更新用户对象。
3. 实现 isNameFree 方法:
- 通过 getPeer().getRetriever().retrieve 方法查找是否有同名的用户对象。
- 若没有同名对象或同名对象就是当前对象,则返回 true ,否则返回 false 。
4. 实现 getAttribute 方法,用于获取指定名称的 PersistenceAttribute 对象。

11. 用户对象的消息处理

用户对象需要处理接收到的消息, User 类提供了 handleMessage 和 writeMessages 方法来实现消息的接收和写入。

以下是相关代码:

package de.webapp.Chat;
// 导入相关包...
/** Nutzer des Chats */
public class User extends Persistence
    implements C_Chat, HttpSessionBindingListener {
    // 属性和构造函数...
    /** Schreibt eine Nachricht in die Nachrichtenschlange und
        benachrichtigt einen wartenden Thread */
    public void handleMessage(Message aMessage) {
        myMessageQueue.addElement(aMessage);
        synchronized (this) {
            notify();
        }
    }
    /** Schreibt Nachrichten mit dem übergebenen I_MessageWriter bis
        canWrite() false zurückgibt oder stopped true ist */
    public synchronized void writeMessages(I_MessageWriter aWriter) {
        // wartende Threads, die nicht mehr schreiben können
        // (canWrite() == false) die Chance zum verlassen dieser Methode
        // und myWriter = null zu setzen
        notify();
        // Falls ein anderer Writer noch funktionstüchtig
        // ist, Ausnahme auslösen
        if (myWriter != null) throw new IllegalStateException();
        // Writer übernehmen
        myWriter = aWriter;
        try {
            // Schreibschleife
            while(myWriter.canWrite() && !stopped) {
                // Nachrichtenschlange leerschreiben
                while (myMessageQueue.size() > 0 && myWriter.canWrite()
                    && !stopped) {
                    Message theMessage = (Message)myMessageQueue.elementAt(0);
                    myWriter.write(theMessage);
                    myMessageQueue.removeElementAt(0);
                }
                try {
                    // Mindestens alle 10 Sekunden aufwachen, um zu testen,
                    // ob der Writer noch funktioniert und das asynchrone
                    // Schreiben noch nicht gestoppt wurde.
                    wait(10000);
                }
                catch (InterruptedException ie) {}
            }
        }
        catch (IOException ioe) {
            if (Log.isLog()) {
                Log.log(ioe);
            }
        }
        // Writer auf null setzen
        myWriter = null;
    }
    // 其他方法...
}

操作步骤如下:
1. 实现 handleMessage 方法:
- 将接收到的消息添加到消息队列 myMessageQueue 中。
- 同步块中调用 notify 方法唤醒可能等待的线程。
2. 实现 writeMessages 方法:
- 调用 notify 方法唤醒可能等待的线程。
- 检查是否已有其他 I_MessageWriter 在使用,若有则抛出异常。
- 接收传入的 I_MessageWriter 对象。
- 进入循环,只要 I_MessageWriter 可以写入且 stopped 标志为 false ,就继续处理消息。
- 在内部循环中,从消息队列中取出消息并使用 I_MessageWriter 写入,同时移除已处理的消息。
- 每 10 秒唤醒一次线程,检查 I_MessageWriter 是否还能工作以及 stopped 标志。
- 若发生 IOException ,记录日志。
- 最后将 myWriter 置为 null 。

12. 用户对象与会话的绑定与解绑

为了实现用户对象与 HttpSession 的绑定和解绑, User 类实现了 HttpSessionBindingListener 接口,提供了 valueBound 和 valueUnbound 方法。

以下是相关代码:

package de.webapp.Chat;
// 导入相关包...
/** Nutzer des Chats */
public class User extends Persistence
    implements C_Chat, HttpSessionBindingListener {
    // 属性和构造函数...
    /** Beim Binden dieses Objekts an die HttpSession den Referenzzähler erhöhen */
    public void valueBound(HttpSessionBindingEvent e) {
        if (Log.isLog(C_Log.METHOD)) {
            Log.log("User " + getName()
                + " wurde an die HttpSession gebunden.", C_Log.METHOD);
        }
        retain();
    }
    /** Beim Entbinden dieses Objekts von der HttpSession den Referenzzähler erniedrigen, 
    beim Chat abmelden und die Chatassoziation löschen */
    public void valueUnbound(HttpSessionBindingEvent e) {
        if (Log.isLog(C_Log.METHOD)) {
            Log.log("User " + getName()
                + " wurde von der HttpSession entbunden.", C_Log.METHOD);
        }
        release();
        if (myChat != null) myChat.unregister(this);
        removeChat();
    }
    // 其他方法...
}

操作步骤如下:
1. 实现 valueBound 方法:
- 若日志开启,记录用户绑定到 HttpSession 的信息。
- 调用 retain 方法增加引用计数。
2. 实现 valueUnbound 方法:
- 若日志开启,记录用户从 HttpSession 解绑的信息。
- 调用 release 方法减少引用计数。
- 若用户在聊天室中,调用 myChat.unregister 方法从聊天室注销。
- 调用 removeChat 方法移除与聊天室的关联。

13. 总结与展望

通过上述对聊天系统各个类的详细分析,我们可以看到一个完整的聊天系统是如何通过合理的类设计构建起来的。每个类都有其明确的职责,相互协作,实现了用户管理、消息发送、消息接收和持久化存储等功能。

在实际应用中,我们可以根据具体需求对这些类进行扩展和优化。例如,可以添加更多的消息类型,实现更复杂的消息处理逻辑;可以优化持久化存储的性能,提高系统的响应速度;还可以增加更多的用户管理功能,如用户权限管理、用户信息修改等。

未来的发展方向可以包括引入实时通信技术,如 WebSocket,实现更实时的消息交互;结合人工智能技术,实现智能聊天机器人,提供更智能的服务;以及优化系统的安全性,防止信息泄露和恶意攻击。

以下是一个简单的消息处理流程图,展示了消息从发送到接收的过程:

graph LR
    A[发送者] --> B[ChatDispatcher]
    B --> C{消息类型}
    C -->|私聊| D[指定用户]
    C -->|群聊| E[所有用户]
    D --> F[User.handleMessage]
    E --> F
    F --> G[消息队列]
    G --> H[User.writeMessages]
    H --> I[I_MessageWriter]
    I --> J[接收者界面]

这个流程图清晰地展示了消息在聊天系统中的传递过程,有助于我们更好地理解系统的工作原理。

总之,合理的类设计是构建高效、稳定聊天系统的关键。通过不断地优化和扩展,我们可以为用户提供更好的聊天体验。

相关推荐

XSS跨站脚本攻击:从原理到实战防御的完整指南

跨站脚本攻击(XSS)是Web安全领域的核心威胁之一,其原理在于攻击者能够将恶意脚本注入到受信任的网页中,当用户浏览时脚本在浏览器端执行。这种攻击的技术价值在于它能够绕过同源策略,直接窃取用户Cookie、会话令牌等敏感信息,甚至篡改页面内容。在实际应用场景中,XSS常见于用户输入点如搜索框、评论区和表单提交处,攻击者通过构造恶意Payload实现攻击。本文基于Pikachu靶场实战,深入剖析反射型、存储型和DOM型XSS的攻击路径与区别,并系统讲解输入过滤、输出编码、内容安全策略(CSP)等防御方案,帮助

weixin_34186128的博客 398

LLM坐席增强实战:5个可落地的客户交互提效用法

大语言模型(LLM)在客户服务中的核心价值,不在于替代人工,而在于增强坐席的信息处理能力、决策支持效率与情感响应带宽。其技术原理依托RAG检索增强生成、轻量模型本地推理、多源上下文动态过滤与行为级情绪建模,具备低延迟、可解释、可干预、合规可控等工程化特征。这种‘坐席增强’范式显著提升复杂问题首解率、降低重复咨询率,并支撑金融、电商、SaaS等强监管行业的安全落地。本文聚焦5个经生产验证的高ROI用法:上下文响应生成、知识库集成、情绪识别、自动分类与自助回复生成,全部围绕‘3秒内提供可行动信息’这一实操标准展

weixin_34212762的博客 753

国央企创新负责人如何实现科技成果与企业创新需求的精准对接?.docx

国央企创新负责人如何实现科技成果与企业创新需求的精准对接?

高校技术转移办公室人员如何加快科技成果的市场对接速度?.docx

高校技术转移办公室人员如何加快科技成果的市场对接速度?

国央企创新负责人如何高效评估科技成果的市场化潜力?.docx

科易网基于AI模型+行业大数据,在技术转移、成果转化、技术经纪、知识产权、产业创新、科技招商等垂直领域的多样化应用场景,推出科技创新领域的AI+工具与数智化解决方案,推动科技创新与产业创新智能化发展。

静止无功发生器SVG仿真模型(附对应说明报告+搭建仿真过程的参考文献)

静止无功发生器SVG仿真模型(附对应说明报告+搭建仿真过程的参考文献)内容概要:本文围绕静止无功发生器(SVG)的仿真模型展开,重点介绍了基于Simulink平台搭建的三相SVG系统,适用于不平衡电网条件下的无功补偿场景。文中提及采用级联H桥拓扑结构与三层控制策略,实现对无功功率的精确动态补偿,并包含详细的仿真模型、说明报告及参考文献支持,有助于深入理解SVG的工作原理与控制方法。同时,资源还涵盖了TCR+FC型SVC等多种无功补偿装置的仿真模型,为电力系统无功优化提供了全面的技术参考。; 适合人群:电气工程及相关专业的高校学生、从事电力系统仿真与无功补偿研究的科研人员,以及具备一定MATLAB/Simulink基础的工程技术人员。; 使用场景及目标:①用于教学与科研中对SVG、SVC等无功补偿装置的工作原理和控制策略进行仿真验证;②支撑电力系统中无功优化、电能质量提升等相关课题的研究与设计;③为含分布式电源的配电网无功电压协调控制提供模型基础和技术路径。; 阅读建议:建议结合提供的说明报告与参考文献,逐步搭建并调试仿真模型,重点关注控制策略的实现逻辑与参数整定方法,同时可对比不同无功补偿方案的仿真结果以加深理解。

无刷直流电机(BLDCM)转速电流双闭环控制、三相桥式星型全控电机研究(Simulink仿真实现)

内容概要:本文系统研究了无刷直流电机(BLDCM)的转速电流双闭环控制策略,基于三相桥式星型全控电路结构,在Matlab/Simulink平台上构建了完整的仿真模型,深入对比了传统PID、模糊PID与模糊神经网络PID三种控制算法在电机调速系统中的动态响应特性、抗干扰能力及稳态控制精度。研究涵盖了无刷直流电机的数学建模、驱动电路设计、双闭环控制器结构设计、控制策略实现以及仿真结果的综合分析,重点剖析了不同智能控制算法在优化系统性能方面的优势与适用场景,为高性能电机控制系统的开发提供了详实的理论依据、仿真验证方法和技术参考。; 适合人群:具备自动控制原理、电力电子技术和Matlab/Simulink仿真基础的电气工程、自动化、机电一体化等专业的本科生、研究生,以及从事电机驱动与控制、电力电子系统研发的工程技术人员。; 使用场景及目标:① 深入学习和掌握无刷直流电机的基本工作原理、数学模型及矢量控制基础;② 理解并独立设计转速与电流双闭环控制系统的架构,掌握PI/PID控制器的参数整定方法;③ 对比分析传统控制与智能控制(模糊、神经网络)在电机调速系统中的应用效果,探究如何提升系统的快速性、鲁棒性和控制精度;④ 为电机控制相关的课程设计、毕业设计、科研项目申报及工业产品原型开发提供可复现的仿真案例和实践指导。; 阅读建议:建议读者在阅读时同步搭建或运行所提供的Simulink仿真模型,通过调整控制器参数、设置不同的负载扰动和转速指令,亲身体验并观察各控制策略的响应曲线差异,重点关注控制算法模块的设计逻辑与仿真结果的定量对比分析,以深化对理论知识的理解,有条件者可进一步结合DSP等硬件平台进行实验验证,实现从仿真到实物的完整闭环。

无刷直流电机BLDC双闭环调速系统Matlab仿真与建模

内容概要:本文围绕无刷直流电机(BLDC)双闭环调速系统的Matlab/Simulink仿真与建模展开,系统实现了转速电流双闭环控制策略,并深入对比了模糊神经网络PID、模糊PID与传统PID三种控制方法在BLDC调速系统中的动态响应、抗干扰能力和控制精度。文档提供了多个高保真的Simulink仿真模型,涵盖无位置传感器控制、滑模观测器、三相桥式星型全控电路等核心技术模块,旨在通过精细化建模仿真,帮助读者深入理解BLDC的控制机理、系统稳定性及动态性能。此外,资源还整合了静止无功发生器(SVG)、静止无功补偿器(SVC)等电力系统装置的仿真模型,展现了电机控制与电力电子、无功补偿技术深度融合的研究背景,为复合型课题研究提供技术支持。; 适合人群:具备扎实的电机控制理论基础和熟练的Matlab/Simulink仿真能力的电气工程、自动化、电力电子及相关专业的高年级本科生、研究生、科研人员,以及从事电机驱动系统、新能源发电或电力系统自动化开发的工程师。; 使用场景及目标:①掌握无刷直流电机双闭环调速系统的设计与仿真方法,深入理解电流环与转速环的协同控制机制;②对比分析传统PID、模糊PID与模糊神经网络PID等先进控制算法在动态响应速度、超调量、稳态精度及鲁棒性方面的性能差异;③为毕业设计、科研项目或工程实践提供可复现、可拓展的仿真平台与技术参考,支持从理论建模到算法优化的全流程研究。; 阅读建议:建议读者结合文中提供的详细Simulink模型、说明文档与参考文献,循序渐进地搭建与调试控制系统,重点关注控制器参数整定、反馈信号处理、观测器设计及仿真结果的定量分析。对于进阶应用,可进一步探索无感控制策略、滑模观测器优化、多电机协同控制等方向,并通过对比实验验证不同控制算法的适用边界与优化潜力。

【便携式电子设备的可持续电源】系统结合了太阳能和风能,并搭配电池存储,以便为低功耗设备供电(Simulink仿真实现)

内容概要:该文档介绍了一种结合太阳能和风能并配备电池存储的便携式电子设备可持续电源系统,并通过Simulink进行仿真验证。系统旨在为低功耗设备提供稳定、绿色的能源供给,充分利用两种可再生能源的互补特性——太阳能适用于光照充足的白天,风能则可在夜间或阴天发挥作用,从而提高整体供能的可靠性和连续性。电池储能单元用于储存多余电能并在发电不足时释放,确保负载持续供电。整个系统的仿真涵盖了能量采集、转换、存储与管理的关键环节,验证了其在不同环境条件下的运行有效性。; 适合人群:从事新能源、电力电子、嵌入式系统及相关领域的科研人员、工程技术人员及高校师生。; 使用场景及目标:①为野外作业、移动监测、应急救援等场景下的低功耗便携设备提供可持续供电方案;②作为教学与科研平台,用于开展多能源协同控制、能量管理策略及系统仿真方面的研究与实践;③推动可再生能源在小型化、分布式供电系统中的应用。; 阅读建议:读者应具备一定的电路与控制系统基础知识,建议结合Simulink模型深入理解各模块的工作原理,并尝试调整气象参数与负载条件以观察系统动态响应,进而优化控制策略。

周四 08点31分科技文献9.m4a

周四 08点31分科技文献9.m4a

ml307 二次开发

中移物联网旗下4g模组官方网站官方教程id=514官方客户不知道啥时候上班 早上10点也说不在服务时间内 找不到官方的sdk下载路径。

myWorld001的博客 2865

从桨叶设计到电机匹配:手把手教你调出无人机最佳动力组合

本文系统介绍了无人机动力系统的核心匹配原则,重点解析了如何通过测试与数据分析,优化螺旋桨与电机的组合,从而显著提升系统力效与续航。文章提供了从参数解读、实战测试到深度调优的完整指南,帮助开发者解决电机过热、推力不足等常见问题,实现无人机性能最大化。

weixin_29305313的博客 234

如何区分Info与CoWoS封装?

这篇文章主要从Info、CoWoS封装的定义、Interposer的种类以及Info、CoWoS封装的种类三方面进行了介绍,希望读者能够有所收获

wangyubo1的博客 7933

从零构建:如何用MATLAB打造你的第一个Stewart平台运动学仿真器

本文详细介绍了如何使用MATLAB从零构建一个六自由度Stewart平台运动学仿真器,涵盖基础理论、环境配置、运动学建模、动态轨迹规划和实时可视化实现。通过MATLAB的Robotics System Toolbox和Simulink等工具,读者可以掌握六自由度Stewart平台的逆运动学求解、正运动学求解及仿真系统集成,适用于飞行模拟器和精密定位等应用场景。

peach的博客 864

影刀RPA进阶:Excel多条件筛选与数据提取实战

本文详细介绍了如何利用影刀RPA实现Excel多条件筛选与数据提取的自动化。通过拆解文本、数值、日期及颜色筛选等核心指令,并结合实战案例,指导用户构建稳健的销售数据提取机器人,从而高效处理复杂数据报表,提升工作效率。

onion的博客 525

【视频讲解】Python量子计算聚类Q-means:量子k-means算法分析电路数据实现可视化...

全文链接:https://tecdat.cn/?p=37821分析师:Yifan Zhang量子计算在近期已然成为一个频繁出现的热门概念。尽管它在大众认知以及互联网社区中备受瞩目,热度极高,然而就其实际能力而言,当前仍然存在诸多局限(点击文末“阅读原文”获取完整代码数据)。量子计算作为一个全新的领域,带来了相较于传统经典计算模式的重大范式转变。在量子计算的体系中,传统的 0 或 1 的经典比特被一...

拓端研究室TRL 1452

高速电路的信号完整性分析

摘要:介绍了高速PCB设计中的信号完整性概念以及破坏信号完整性的原因,从理论和计算的层面上分析了高速电路设计中反射和串扰的形成原因,并介绍了IBIS仿真。 关键词:信号完整性 反射 串扰 IBIS仿真 随着半导体技术和深压微米工艺的不断发展,IC的开关速度目前已经从几十MHz增加到几百MHz,甚至达到几GHz。在高速PCB设计中,工程师经常会碰到误触发、阻尼振荡、过冲、欠冲、串...

bh_wang的专栏 1429

QGIS004:【05矢量属性表】-保留字段、删除字段、添加唯一值索引字段、添加字段到属性表、添加自增量字段、添加XY字段到图层、文本型转浮点型、重构字段、重命名字段

摘要:QGIS矢量属性表工具箱包括保留字段、删除字段、添加唯一值索引字段、添加字段到属性表、添加自增量字段、添加XY字段到图层、文本型转浮点型、重构字段、重命名字段等选项,本文介绍各选项的基本操作。

qq_31762031的博客 2310

SAP B1中文培訓教材_SDK

公司内部培训资料,对开发工具 ,DIAPI, UIAPI,Add-ON有详细解说

文档:亲测可用解决vnc灰屏,黑屏,鼠标大黑叉情况

主要解决vnc-viewer灰屏,黑屏,鼠标大黑叉等现象,详细见文档。

OV9734 DATASHEET,完整规格书

OV9734完整规格书,包括电气参数,尺寸规格,寄存器配置表等

老版QQ原生表情gif动态

这是老版的QQ表情,不过全是动态的的,Windows照片查看器是不会动的,可以右击用浏览器打开查看,再说一遍是老版的,只是分享给宁愿用老版也要动态的人。

上一篇: 2、Elasticsearch:强大的搜索与分析引擎
下一篇: 29、以活动为中心的参与式社区设计与材料导向的主动创造
zeta9
博客等级 码龄19年 65粉丝 1571原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符  | 博主筛选后可见
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

当前余额3.43元 前往充值 >
需支付:10.00元
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付元
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值