Escrito antes:
El fin de semana pasado me tomé un tiempo para grabar el diseño inicial y el diseño detallado del lado del servidor de un simple programa de chat de Socket que escribí. Finalmente esperé el certificado de examen suave que tomé antes de la graduación del martes, y luego pasé el día de tiempo extra. Hoy resultó ser el viernes, y planeé grabar el diseño detallado del cliente y el módulo común, porque estaré ocupado con otras cosas a partir de este fin de semana.
diseño:
El diseño del cliente se divide principalmente en dos partes, a saber, el diseño del módulo de comunicación del socket y el diseño relacionado con la interfaz de usuario.
Diseño de comunicación de socket de cliente:
El diseño aquí es realmente similar al diseño del servidor. La diferencia es que el servidor recibe paquetes de latidos, mientras que el cliente envía paquetes de latidos. Dado que el cliente solo se comunica con un servidor (la comunicación entre los clientes también está distribuida por el servidor), solo se utiliza un hilo de tamaño 2 para manejar estas dos cosas (NewFixedThreadPool (2)). Las clases de procesamiento correspondientes son ReceptiveListener y KeepAlivedog. Cuando se inicializa el recepcionero, se envía una devolución de llamada como una devolución de llamada al cliente recibe el mensaje del servidor. La implementación predeterminada de la devolución de llamada es defaultCallback. DefaultCallback se distribuye a diferentes manejadores a través de HF de acuerdo con diferentes eventos. El titular del cliente almacena la información actual del cliente. El diseño es el siguiente:
La implementación específica del módulo de comunicación de socket:
[Client.java]
El cliente es la entrada al cliente para conectarse al servidor. Para crear un cliente, debe especificar una devolución de llamada como la devolución de llamada cuando el cliente recibe el mensaje del servidor. Luego, el método Start () del cliente inicia escuchar el servidor (RecedInSistener). Cuando el RechiClectener recibe los datos enviados por el servidor, se llama al método de devolución de llamada (devolución de llamada) para procesarlo. Al mismo tiempo, el cliente también necesita enviar un paquete Heartbeat para notificar al servidor que todavía está conectado al servidor. El cliente mantiene el paquete del corazón. Alive () es iniciado e implementado por KeepAlivedog; Estos dos pasos son ejecutados por un grupo de hilo de NewfixedThreadPool (2) con un tamaño fijo de 2. Puede ser más razonable usar un NewfixedThreadPool (1) y NewsCheduledThreadPool (1) aquí para manejarlo, porque el paquete de latidos se envía regularmente, y así es como el servidor lo implica (este ajuste posterior). El código específico del cliente es el siguiente (los otros dos métodos están expuestos aquí para obtener el socket y el usuario al que pertenece el socket actual):
/** * Cliente * @author Yaolin * */public class Client {private final Socket Socket; cadena privada de; Grupo de servicio de ejecutores finales privados; devolución de llamada final privada de devolución de llamada; Public Client (devolución de llamada) lanza ioexception {this.socket = new Socket (constantValue.server_ip, constantValue.server_port); this.pool = ejecutors.newFixedThreadPool (2); this.callback = Callback; } public void start () {Pool.Execute (nuevo RecedInSistener (Socket, Callback)); } public void keepalive (string from) {this.from = desde; Pool.ExCute (nuevo KeepAlivedog (Socket, From)); } public Socket getSocket () {return Socket; } public String getFrom () {return de; }}[KeepAlivedog.java]
Después de que el cliente establece una conexión con el servidor (este programa se refiere al inicio de sesión exitoso, debido a que el Socket del Cliente será administrado por Socketholder del servidor después de que el inicio de sesión sea exitoso), es necesario enviar un paquete Heartbeat al paquete de corazón al servidor cada vez para decirle al servidor que todavía está en contacto con el servidor, de lo contrario, el servidor descartará el paso sin interacción después de un período de tiempo (ver el servidor para los detalles). El código de KeepAlivedog se implementa de la siguiente manera (se puede ajustar a NewsCheduledThreadPool (1) más adelante, por lo que el código aquí también se ajustará):
/*** KeepAlivedOg: indique el servidor que este cliente se está ejecutando; * * @author Yaolin */public class KeepAlivedog implementa Runnable {Socador de socket final privado; cadena final privada de; public KeepAlivedog (socket socket, string from) {this.socket = socket; this.From = de; } @Override public void run () {while (socket! = Null &&! Socket.isClosed ()) {try {printWriter out = new PrintWriter (Socket.getOutputStream ()); Message AlivEMessage = new AlivEMessage (); Message.setFrom (de); out.println (json.tojson (mensaje)); out.flush (); Thread.sleep (constantValue.keep_alive_period * 1000); } Catch (Exception e) {loggerUtil.error ("¡Falló en el mensaje de envío del cliente!" + E.getMessage (), e); }}}}[RecebiveListener.java]
El método START () del cliente inicia la escucha del servidor y es implementado por el receptor. Después de recibir el mensaje del servidor, el ReconedRenener volverá a llamar al método Dowork () de la devolución de llamada para permitir que la devolución de llamada maneje la lógica comercial específica. Por lo tanto, el ReceptiveListener solo es responsable de escuchar los mensajes en el servidor, y el procesamiento específico se maneja mediante devolución de llamada. Debe mencionarse aquí que cuando el tipo de mensaje es un tipo de archivo, dormirá para configurar el intervalo de ejecución, de modo que el Dowork en la devolución de llamada pueda leer el flujo de archivo al servidor en lugar de ingresar directamente el siguiente bucle. El diseño aquí es similar al servidor. El código de implementación específico de RechiVuelistener es el siguiente:
Public Class RecebiveListener implementa Runnable {SOCKECKE SOCKET FINAL PRIVADO; devolución de llamada final privada de devolución de llamada; Public RecebiveListener (socket de socket, devolución de llamada) {this.socket = Socket; this.callback = Callback; } @Override public void run () {if (socket! = Null) {while (! Socket.isClosed ()) {try {inputStream is = Socket.getInputStream (); Línea de cadena = nulo; Stringbuffer sb = null; if (is.available ()> 0) {bufferedReader bufr = new BufferedReader (new InputStreamReader (is)); sb = new StringBuffer (); while (is.available ()> 0 && (line = bufr.readline ())! = null) {sb.append (línea); } Loggerutil.trach ("recibir [" + sb.toString () + "] en" + nueva fecha ()); callback.dowork (socket, sb.ToString ()); Mensaje BaseMessage = JSON.PARSEOBJECT (SB.ToString (), BaseMessage.Class); if (message.gettype () == MessageType.file) {// Pausa para recibir el archivo loggerutil.trach ("Cliente: pausa para recibir el archivo"); Thread.sleep (constantValue.message_period); }} else {thread.sleep (constantValue.message_period); }} catch (Exception e) {loggerUtil.error ("¡Falló en el mensaje de envío del cliente!" + E.getMessage (), e); }}}}}}}[Callback.java, defaultcallback.java]
De lo anterior, podemos ver que el procesamiento de mensajes del cliente es una devolución de llamada, y su devolución de llamada es solo una interfaz. Todas las implementaciones de devolución de llamada implementan la interfaz para procesar mensajes de acuerdo con sus necesidades. Aquí, la implementación predeterminada de la devolución de llamada es el valor predeterminado. DefaultCallback solo procesa tres tipos de mensajes, a saber, mensajes de chat, mensajes de archivo y mensajes de devolución. Para los mensajes de chat, DefaultCallback obtendrá la interfaz correspondiente a través de la ruta del enrutador en la interfaz de usuario (consulte el diseño de la interfaz de usuario a continuación para más detalles) y luego mostrará el mensaje en el cuadro de chat correspondiente; Para los mensajes de archivo, DefaultCallback escribirá el archivo en la ruta especificada en la configuración (el archivo se recibe sin el permiso del usuario aquí. Este diseño no es muy amigable, por lo que por ahora); Para los mensajes de devolución, el defaultCallback se llamará a diferentes manejadores de acuerdo con la clave del mensaje de retorno. El código específico es el siguiente:
devolución de llamada de interfaz pública {public void dowork (servidor de socket, datos del objeto); } Public Class DefaultCallback implementa la devolución de llamada {@Override public void Dowork (servidor de socket, datos de objeto) {if (data! = null) {baseMessage Message = json.parseObject (data.ToString (), BaseMessage.class); switch (message.gettype ()) {case messageType.chat: handlechatMessage (data); romper; case MessageType.file: manefilemessage (servidor, datos); romper; case MessageType.return: HandlereturnMessage (datos); romper; }}} private void handLechatMessage (datos del objeto) {chatMessage M = json.parseObject (data.ToString (), chatMessage.class); String tabkey = m.getFrom (); // Desde jComponent comp = router.getView (chatroomView.class) .getComponent (chatroomview.chattabbed); if (comp inst installing de jtabbedpane) {jtabbedpane tab = (jtabbedpane) comp; int index = tab.Indexoftab (TabKey); if (index == -1) {tab.Addtab (tabkey, resulTholder.get (tabkey) .getScrollPane ()); } JTextArea textAREA = resulTholder.get (tabkey) .gettexTarea (); textAREA.settext (new StringBuffer () .Append (TextArea.Gettext ()). Append (System.LineSeParator ()). Append (System.LineSeParator ()) .Append ("[") .Append (M.GetOwner ()). Append ("]:"). .ToString ()); // Desplácese hasta la parte inferior TextAREA.SetCaretPosition (TextAREA.Gettext (). Longitud ()); }} private void manefilemessage (Socket Server, Object Data) {FilEmessage Message = Json.ParseObject (data.ToString (), fileMessage.class); if (message.getSize ()> 0) {outputStream OS = null; intente {if (server! = null) {inputStream is = server.getInputStream (); Archivo dir = nuevo archivo (constantValue.client_RECeive_dir); if (! dir.exists ()) {dir.mkdirs (); } OS = nuevo FileOutputStream (nuevo archivo (pathUtil.Combination (constantValue.client_RECeive_dir, new Date (). GetTime () + Message.getName ()))); int total = 0; while (! Server.isClosed ()) {if (is.available ()> 0) {byte [] buff = new byte [constantValue.buff_size]; int len = -1; while (is.available ()> 0 && (len = is.read (buff))! = -1) {os.write (buff, 0, len); total += len; Loggerutil.debug ("recibir buff [" + len + "]"); } os.flush (); if (total> = message.getSize ()) {loggerutil.info ("recibir buff [ok]"); romper; }}}}}} Catch (Exception e) {loggerUtil.error ("¡Recibir archivo fallido!" + E.getMessage (), e); } finalmente {if (os! = null) {try {os.close (); } catch (excepción ignore) {} os = null; }}}} private void handlereturnMessage (datos del objeto) {returnMessage m = json.parseObject (data.ToString (), returnMessage.class); if (StringUtil.IsNotEmpty (m.getKey ())) {switch (m.getKey ()) {case key.notify: // notifica al cliente para actualizar USR List hf.gethandler (key.notify) .Handle (data); romper; Case Key.login: hf.gethandler (key.login) .handle (datos); romper; Case Key.Register: Hf.Gethandler (Key.Register) .Handle (datos); romper; Case Key.listuser: Hf.gethandler (Key.Listuser) .Handle (datos); romper; Case Key.tip: hf.gethandler (key.tip) .handle (datos); romper; }}}}[Handler.java, hf.java, listuserhdl.java ...]
El componente del controlador es responsable de procesar mensajes del tipo de mensaje de retorno del servidor. DefaultCallback distribuye mensajes a diferentes manejadores de acuerdo con diferentes claves. Este es también un simple componente de fábrica. Es similar a los datos recibidos por el servidor. El diagrama de clase completo es el siguiente:
El código para esta sección se proporciona a continuación. Para reducir el espacio, se recopila todo el código implementado por el controlador.
Handler de interfaz pública {Mango de objeto público (Obj OBJ); } Public Class HF {Public Static Handler Gethandler (tecla de cadena) {Switch (Key) {Case Key.Notify: return New NotifyHDL (); Case key.login: return nuevo loginHDL (); Case Key.Register: return New RegisterHDL (); Case Key.ListUser: devuelve nuevo listUserHDL (); Case key.tip: devuelve nuevo tipHDL (); } return null; }} Handler de implementos de listuserHDL de clase pública {@Override public Object Handle (object obj) {if (obj! = null) {try {returnMessage rm = json.parseObject (obj.ToString (), returnMessage.class); if (rm.issuccess () && rm.getContent ()! = null) {clientListUserdto dto = json.parseObject (rm.getContent (). toString (), clientListUserdto.class); JComponent comp = router.getView (chatroomview.class) .getComponent (chatroomView.listusrlist); if (comp inst installyf jlist) {@suppleswarnings ("sin verificar") // jlist <string> listusrlist = (jList <String>) comp; List <String> listUser = new LinkedList <String> (); listuser.addall (dto.getListuser ()); Colección.sort (listuser); listuser.add (0, constantValue.to_all); listusrlist.setListData (listUser.toarray (nueva cadena [] {})); }}} Catch (Exception e) {loggerUtil.error ("¡Manejar listusr fallado!" + e.getMessage (), e); }} return null; }} La clase pública LoginHDL implementa el controlador {@Override Public Object Handle (object obj) {if (obj! = null) {try {returnMessage rm = json.parseObject (obj.ToString (), returnMessage.class); if (rM.issuccess ()) {Router.getView (Registro yLoginView.class) .Trash (); Router.getView (chatroomview.class) .create (). Display (); Clientholder.getClient (). KeepAlive (rm.getto ()); // Keep ...} else {Container Container = Router.getView (RegisterAndLogInview.class) .Container (); if (Container! = Null) {// Mostrar error JOptionPane.ShowMessageDialog (Container, rm.getMessage ()); }}} Catch (Exception e) {loggerutil.error ("¡Manejar inicio de sesión fallido!" + E.getMessage (), e); }} return null; }} La clase pública NotifyHDL implementa el controlador {@Override Public Object Handle (object obj) {if (obj! = null) {try {returnMessage rm = json.parseObject (obj.ToString (), returnMessage.class); if (rM.issuccess () && rm.getContent ()! = null) {clientNotifyDTO dto = json.parseObject (rm.getContent (). toString (), clientnotifydto.class); JComponent comp = router.getView (chatroomview.class) .getComponent (chatroomView.listusrlist); if (comp inst installyf jlist) {@suppleswarnings ("sin verificar") // jlist <string> listusrlist = (jList <String>) comp; List <String> listUser = modeltolist (listUsrlist.getModel ()); if (dto.isflag ()) {if (! listUser.contains (dto.getusername ())) {listUser.add (dto.getusername ()); listuser.remove (constantValue.to_all); Colección.sort (listuser); listuser.add (0, constantValue.to_all); }} else {listUser.remove (dto.getusername ()); } listusrlist.setListData (listUser.toarray (nueva cadena [] {})); }}} Catch (Exception e) {loggerutil.error ("¡manejar nofity fallido!" + e.getMessage (), e); }} return null; } Lista privada <String> Modeltolist (ListModel <String> ListModel) {list <String> list = new LinkedList <String> (); if (listModel! = null) {for (int i = 0; i <listModel.getSize (); i ++) {list.add (listModel.getElementat (i)); }} lista de retorno; }} Handler de registro de clase pública implementa el controlador {@Override Public Object Handle (object obj) {if (obj! = null) {try {returnMessage rm = json.parseObject (obj.ToString (), returnMessage.class); Contenedor contenedor = router.getView (registro yLoginView.class) .container (); if (contenedor! = null) {if (rm.issuccess ()) {joptionPane.showMessEdialog (contenedor, rm.getContent ()); } else {joptionPane.ShowMessEdialog (contenedor, rmmetMessage ()); }}} Catch (Exception e) {loggerUtil.error ("¡Manejar registro fallido!" + E.getMessage (), e); }} return null; }} Handler de la clase pública implementa el controlador {@Override public Object Handle (object obj) {if (obj! = null) {try {returnMessage m = json.parseObject (obj.ToString (), returnMessage.class); if (m.issuccess () && m.getContent ()! = null) {String tabKey = m.getFrom (); Sujeción de cadena = m.getContent (). ToString (); JComponent comp = router.getView (chatroomview.class) .getComponent (chatroomview.chattabbed); if (comp inst installing de jtabbedpane) {jtabbedpane tab = (jtabbedpane) comp; int index = tab.Indexoftab (TabKey); if (index == -1) {tab.Addtab (tabkey, resulTholder.get (tabkey) .getScrollPane ()); } JTextArea textAREA = resulTholder.get (tabkey) .gettexTarea (); textArea.settext (new StringBuffer () .Append (TextArea.GetText ()). Append (System.LineSeParator ()). Append (System.LineSeParator ()) .Append ("[") .Append (M.GetOwner ()). Append ("]:") .Append (System.LineSeParator (). // Desplácese hasta la parte inferior TextAREA.SetCaretPosition (TextAREA.Gettext (). Longitud ()); }}} Catch (Exception e) {loggerutil.error ("¡Manejar la punta fallida!" + E.getMessage (), e); }} return null; }} Hay otra clase para el módulo de comunicación de socket, es decir, el titular del cliente, que se utiliza para almacenar el cliente actual, que es similar al Socketholder en el servidor.
/** * @author Yaolin */public class Clientholder {public static Client Client; CLIENTE ESTÁTICO Public getClient () {Return Client; } public static void setClient (cliente cliente) {clientholder.client = client; }}Implementación específica del módulo de interfaz de usuario:
Lo anterior registra el diseño del módulo de comunicación de socket. A continuación, grabo el módulo de diseño de la interfaz de usuario. No planeo escribir la interfaz de usuario por mí mismo. Después de todo, la escritura es demasiado fea, por lo que puedo pedirle a compañeros de clase o amigos que me ayuden a golpearlo más tarde. Así que entrego el procesamiento de eventos de UI a la acción para manejarlo, y simplemente separar el diseño de la interfaz de usuario y la respuesta del evento. Todas las UI heredan jframe e implementan la interfaz de vista. La clase de implementación del controlador anterior se obtiene a través del enrutador (se devolverá directamente si existe, y se creará y almacenará si no existe). La vista proporciona la creación UI (), obtiene contenedor (), obtenga los componentes en la ui getComponent (), Display Display () y Recycle Trash (); Resultwrapper y Resultholder son solo para crear y almacenar pestañas de chat. El diseño es el siguiente:
[Router.java, View.java]
Todas las UI heredan jframe e implementan la interfaz de vista. La clase de implementación del controlador obtiene la interfaz de usuario especificada a través del enrutador (regresa directamente si existe, y crea y almacena si no existe). La vista proporciona la creación UI (), obtiene contenedor () y obtiene los componentes en la interfaz de usuario getComponent (), muestra la pantalla () y recicla basura (). La implementación específica es la siguiente:
/*** Ver ruta* @author Yaolin*/public class Router {private static map <String, View> listroute = new Hashmap <String, View> (); Public Static View GetView (class <?> Clazz) {ver v = listroute.get (clazz.getName ()); if (v == null) {try {v = (ver) class.forname (clazz.getName ()). NewInStance (); listroute.put (clazz.getName (), v); } Catch (Exception e) {loggerutil.error ("Crear vista fallida!" + E.getMessage (), e); }} return v; }} /** * Interfaces canónicas para todas las interfaces * @author Yaolin * */Public Interface View {/** * */public View Create (); / ** * */ contenedor público contenedor (); / ** * @param clave */ public jComponent getComponent (tecla de cadena); / ** * */ public void display (); / ** * */ public void Trash (); }[RegisterAndLogInview.java, chatroomview.java]
Como no quiero escribir la interfaz de usuario solo, simplemente escribí dos interfaces de UI aquí, a saber, la interfaz de registro e inicio de sesión y la interfaz de chat. Aquí hay dos interfaces feas:
Registre la interfaz de inicio de sesión
Interfaz de chat
Los siguientes son los códigos específicos para estas dos interfaces:
/*** Regístrese y inicie sesión* @author Yaolin*/public Class RegisterAndLoginView extiende Jframe Implementos Ver {Private estático final Long SerialVersionUid = 6322088074312546736l; Acción privada final de registro yLoginAction = new RegisterAndLoginAction (); Private static boolean create = false; @Override public View Create () {if (! Create) {init (); Create = true; } devolver esto; } contenedor público contenedor () {create (); return getContentPane (); } @Override public jComponent getComponent (clave de cadena) {return null; } @Override public void display () {setVisible (true); } @Override public void Trash () {desestimación (); } private void init () {// atributo setSize (500, 300); setResizable (falso); setLocationrelativeto (nulo); // contenedor jpanel panel = new JPanel (); panel.setLayout (nulo); // componente // nombre de usuario jlabel lbusername = new JLabel (i18n.text_username); lbusername.setBounds (100, 80, 200, 30); final JTextfield tfusername = new JTextField (); tfusername.setBounds (150, 80, 230, 30); panel.add (Lbusername); panel.add (tfusername); // contraseña jlabel lbpassword = new JLabel (i18n.text_password); lbpassword.setBounds (100, 120, 200, 30); final jpasswordfield pfpassword = new JPassWordField (); pfpassword.setBounds (150, 120, 230, 30); panel.add (lbpassword); panel.add (pfpassword); // btnregister jButton btnregister = new JButton (i18n.btn_register); btnregister.setBounds (100, 175, 80, 30); // btnlogin final jbutton btnlogin = new JButton (i18n.btn_login); btnlogin.setbounds (200, 175, 80, 30); // btncancel jbutton btnexit = new JButton (i18n.btn_exit); btnexit.setBounds (300, 175, 80, 30); panel.add (btnregister); panel.add (btnlogin); panel.add (btnexit); // evento pfpassword.AddKeyListener (new KeyAdapter () {public void keyPressed (final keyEvent e) {if (e.getKeyCode () == keyEvent.vk_enter) btnlogin.doClick ();}}); // end de addKeylistener btnregister.addactionListener (new ActionListener () {) ActionPerformed (final ActionEvent E) {if (StringUtil.IsEmpty (tfusername.gettext ()) || Action.handleregister (tfusername.gettext (), new String (pfpassword.getPassword ())); StringUtil.IsEmty (new String (pfpassword.getPassword ())) {joptionPane.showMessEdialog (getContentPane (), i18n.info_login_empty_data) de addactionListener btnexit.addactionListener (new ActionListener () {public void ActionPerformed (final ActionEvent E) {System.exit (0); }}); // final de addactionListener getContentPane (). add (panel); setDefaultCloseOperation (jframe.exit_on_close); }} /** * Ventana de chat de cliente * * @author Yaolin */Public Class ChatroomView extiende JFrame Implements Ver {Private Static final Long SerialVersionUid = -4515831172899054818l; Public static final String listUsrlist = "listusrlist"; Cadena final estática pública Chattabbed = "Chattabbed"; Private static boolean create = false; Acción de sala de chat privada = nuevo chatroomAction (); private jlist <String> listUsrlist = null; privado jtabbedpane chattabbed = null; @Override public View Create () {if (! Create) {init (); Create = true; } devolver esto; } contenedor público contenedor () {create (); return getContentPane (); } @Override public jComponent getComponent (clave de cadena) {create (); switch (clave) {case listUsrList: return listUsrList; Caso Chattabbed: return chattabbed; } return null; } @Override public void display () {setVisible (true); } @Override public void Trash () {desestimación (); } public void init () {settitle (i18n.text_app_name); setSize (800, 600); setResizable (falso); setLocationrelativeto (nulo); setLayout (new BorderLayout ()); agregar (createChatPanel (), borderLayout.center); add (createUsRListView (), borderLayout.east); setDefaultCloseOperation (jframe.exit_on_close); } private jComponent CreateChatPanel () {// File Selector final JFilechooser FILECHOOSER = new JFileChooser (); Panel jpanel = new JPanel (new BorderLayout ()); // Center Chattabbed = new JTabbedPane (); chattabbed.addtab (constantValue.to_all, resultholder.get (constantValue.to_all) .getScrollPane ()); panel.add (Chattabbed, BorderLayout.Center); // South JPanel South = new JPanel (new BorderLayout ()); // South - File JPanel Middle = new JPanel (new BorderLayout ()); Middle.Add (new JLabel (), BorderLayout.Center); // solo para rellenar jButton btnupload = new JButton (i18n.btn_send_file); Middle.Add (btnupload, borderLayout.east); South.Add (Middle, BorderLayout.north); // Sur - TextAREA final JTexTarea tasend = new JTextArea (); tasend.setCaretColor (color.blue); tasend.setMargin (nuevas inserciones (10, 10, 10, 10)); tasend.setrows (10); South.Add (Tasend, BorderLayout.Center); // Sur - Btn JPanel Bottom = new JPanel (new BorderLayout ()); Bottom.Add (new JLabel (), BorderLayout.Center); // solo para rellenar jbutton btnsend = new JButton (i18n.btn_send); Bottom.Add (BTNSEND, BorderLayout.East); South.Add (abajo, BorderLayout.South); btnupload.addactionListener (new ActionListener () {public void ActionPerformed (final ActionEvent e) {if (! ConstantValue.to_all.equals (chattabbed.gettitleat (chattabbed.getselectedIndex ())) {int durthval = filechOoser.showOpend (chatroomViewview; JfileCooser.approve_option) {archivo de archivo = fileCooser.getSelectedFile (); }}}}); btnsend.addactionListener (new ActionListener () {public void ActionPerformed (final ActionEvent e) {if (StringUtil.isNotEmpty (Tasend.Gettext ())) {Action.send (Chattabbed.Gettleat (Chattabbed.getSelectedIndex (), tasend.gettext (); }}); panel.Add (sur, borderLayout.South); panel de regreso; } private jComponent createUsRListView () {listUsRList = new JList <String> (); listusrlist.setBorder (nuevo LineBorder (color.blue)); listusrlist.setListData (nueva cadena [] {constantValue.to_all}); listuSrlist.SetFixedCellWidth (200); ListUsrlist.SetFixedCellHeight (30); listusrlist.AdDListSelectionListener (new ListSelectionListener () {@Override public void valueChanged (listelectionEvent e) {// chat a if (chattabbed.indexoftab (listusrlist.getSelectedValue ()) == -1 && listusrlist.getSelectedValue () ! ListUsrList.getSelectedValue (). Equals (ClientHolder.getClient (). GetFrom ())) {chattabbed.addtab (listUsrlist.getSelectedValue (), resulTholder.get (listUsrList.getSelectedValue ()). GetScrollPane ()); chattabbed.setselectedIndex (chattabbed.indexoftab (listusrlist.getSelectedValue ())); return listusrlist; }}[RegisterAndLoginAction.java, chatroomation.java]
Aquí, el procesamiento de eventos de la interfaz de usuario se maneja por acción, que simplemente separa el diseño de la interfaz de usuario y la respuesta del evento. Los eventos de RegistroDlogInview se manejan por Registro y LoginAction, y los eventos de ChatroomView se manejan por ChatroomAction. La implementación específica es la siguiente:
public class RegisterAndLoginAction {public void HandLeregister (String UserName, String Password) {if (StringUtil.IsEmpty (UserName) || } RegistroMessage Message = new RegisterMessage () .SetUsername (UserName) .setPassword (contraseña); Message.setFrom (nombre de usuario); SendHelper.send (Clientholder.getClient (). GetSocket (), mensaje); } public void handLelogin (String UserName, String Password) {if (StringUtil.IsEmpty (UserName) || StringUtil.isEmpty (Password)) {return; } Message de LoginMessage = new LoginMessage () .SetUsername (UserName) .SetPassword (Password); Message.setFrom (nombre de usuario); SendHelper.send (Clientholder.getClient (). GetSocket (), mensaje); }} Hay otras dos clases para el diseño de la interfaz de usuario, a saber, Resultholder y Resultwrapper. Resultwrapper y Resultholder son solo para crear y almacenar pestañas de chat. La implementación específica es la siguiente:
Public Class Resultwrapper {private jscrollpane scrollpane; JTEXTAREA PRIVADA textaraa; Public ResultWrapper (JSCrollPane ScrollPane, JTexTarea TextAREA) {this.scrollPane = scrollPane; this.texTarea = textAREA; } public JSCrollPane getScrollPane () {return scrollPane; } public void setScrollPane (jscrollPane scrollpane) {this.scrollPane = scrollPane; } public JTextArea getTextArea () {return textAREA; } public void setTextArea (JTexTarea TextAREA) {this.TexTarea = textAREA; }} public class ResulTholder {Mapa estático privado <String, Resultwrapper> listresultwrapper = new HashMap <String, resultwrapper> (); Public static void put (clave de cadena, envoltorio de resultado) {listresultwrapper.put (clave, contenedor); } public static resultwrapper get (clave de cadena) {Resultwrapper wrapper = listresultwraper.get (clave); if (wrapper == null) {wrapper = create (); poner (clave, envoltorio); } return wrapper; } private static dentewrapper create () {jTextArea dulteTextArea = new JTextArea (); ResultTextArea.setEditable (falso); ResultTextArea.setBorder (nuevo LineBorder (color.blue)); JSCrollPane scrollPane = new JSCrollPane (resultTextArea); scrollpane.sethorizontalscrollBarpolicy (scrollpaneconstants.horizontal_scrollbar_never); scrollPane.setVerTicalScrollBarPolicy (scrollPaneconstants.vertical_scrollbar_as_needed); ResultSwrapper wrapper = new ResultwraPper (scrollPane, resultexTarea); devolución de envoltura; }} Se da el último, la entrada a la que se ejecuta el cliente:
/** * * * @author Yaolin * */public class niloaychat {public static void main (string [] args) {ver v = router.getView (registre yginview.class) .create (); intente {v.display (); Client Client = New Client (New DefaultCallback ()); client.start (); ClientHolder.SetClient (Cliente); } Catch (ioException e) {joptionPane.ShowMessEdialog (V.Container (), E.GetMessage ()); }}} Dirección de descarga de demostración: demostración
Lo anterior es todo el contenido de este artículo. Espero que sea útil para el aprendizaje de todos y espero que todos apoyen más a Wulin.com.