Da ich das Design von Netzwerk -Computernetzwerkkurs -Kursdesign erstellen muss und keine Netzwerkkabel oder LAN -Router -Konfiguration usw. durchführen möchte, habe ich mich schließlich entschlossen, Socket zu verwenden, um einen LAN -Chatraum basierend auf Java zu schreiben:
Weitere Informationen zur Socket- und Netzwerkprogrammierung finden Sie in meinem anderen Artikel: Java Socket-basierte Programmierung
Das Programm basiert auf der C/S -Struktur, dh im Client -Servermodus.
Server:
Die Standard -IP ist native IP
Beide Parteien müssen eine Portnummer bestimmen, um die maximale Anzahl der verbundenen Personen festzulegen. Die Schnittstelle kann gestartet und geschlossen werden, um den Online -Benutzer und den Namen anzuzeigen (dieses Gerät wird hier nicht angezeigt)
Kunde:
Müssen die Server -IP -Adresse (LAN) manuell festlegen
Stellen Sie die Portnummer manuell ein und geben Sie den Namen manuell ein, um das Programm mit dem Programm wie folgt zu verbinden und zu trennen:
Server:
Kunde:
Ich werde den spezifischen Code am Ende hochladen.
Es gibt viele Mängel in der Software, wie z. B.:
Die Datenbank gibt es keine Kreuzungsoptimierung: Alle Benutzer können in der Datenbank gespeichert werden, und Chat -Datensätze können auch in der Datenbank platziert werden. Eins-zu-eins-Chat-Optimierung wird nicht implementiert: Die Methode zur Neudefinition von Eins-zu-Eins-Chat gibt immer noch viele Mängel. Wenn Sie daran interessiert sind, kommen Sie zurück und studieren Sie es langsam.
Im Folgenden finden Sie drei Codes für das Programm:
User.java
Public Class User { / *** Benutzerinformationsklasse* Wird verwendet, um Benutzerpersoneninformationen aufzuzeichnen: Name und IP* / privater Zeichenfolge Name; private Zeichenfolge IP; public user (string name, string ip) {this.name = name; this.ip = ip; } public String getName () {return name; } public void setName (String -Name) {this.name = name; } public String getiP () {return ip; } public void setIp (String ip) {this.ip = ip; }}Server_more.java
Import Java.awt.BorderLayout; Import Java.awt.Color; Import Java.awt.GridLayout; Import Java.awt.toolkit; Import Java.awt.event.ActionEvent; Import Java.awt.event.ActionListener; java.awt.event.windowevent; import Java.io.bufufferedReader; Import Java.io.ioException; Import Java.io.inputStreamReader; Java.io.printwriter import. java.util.stringTokenizer; import Javax.swing.DefaultListModel; Import Javax.swing.jbutton; Import Javax.swing.jframe; javax.swing.jscrollpane; import javax.swing.jsplitpane; import javax.swing.jtextarea; import javax.swing.jtextfield; javax.swing.border.titledborder; öffentliche Klasse Server_more {private jframe; private jtextArea contentArea; // Textfeld privat JTextField txt_message; // Wird verwendet, um Textinformationen private JTextfield TXT_MAX anzuzeigen; // Setzen Sie die maximale Anzahl von Personen, die private JTextfield txt_port angeschlossen sind. // Portnummer privat einstellen jbutton btn_start; // Button Button Private Jbutton Btn_Stop; // Button private jbutton btn_send abnehmen; // Button Private JPanel Northpanel senden; // North Panel Private Jpanel Southpanel; // South Panel Private Jscrollpane RightPanel; // linke Scroll Bar Private Jscrollpane links; // Right Scroll Bar Private JSplitPane CentersPlit; // Split Line Private JList UserList; // Einzelkomponente private StandardlistModel ListModel; Private ServerSocket ServerSocket; private Serverthread Serverthread; private ArrayList <Codethread> Clients; privat boolean isSstart = false; // Hauptmethode, Programmausführungseintrag public static void main (String [] args) {new Server_more (); } // Meldung Senden Sie public void send () {if (! IsStart) {joptionpane.showMessagedialog (Frame, "Der Server hat noch nicht gestartet, und die Nachricht kann nicht gesendet werden!", "Fehler", joptionpane.error_message); zurückkehren; } if (Clients.size () == 0) {joptionpane.showMessagedialog (Frame, "Kein Benutzer ist online, Nachrichten können nicht gesendet werden!", "Fehler", joptionpane.error_message); zurückkehren; } String message = txt_message.getText (). Trim (); if (message == null || message.equals ("") {joptionpane.showmessagedialog (Frame, "Nachricht kann nicht leer sein!", "Fehler", joptionpane.error_message); zurückkehren; } sendServerMessage (message); // Mass Server -Nachrichten contentArea.append ("Server:" + txt_message.getText () + "/r/n"); txt_message.setText (null); } // Konstruktieren Sie die Drop -Methode public server_more () {Fram = new JFrame ("Server"); // Ändern Sie das Symbol der jframe: //frame.seticonimage(toolkit.getDefaulttoolkit().Createimage(client.class.getResource("qq.png "))); //frame.seticonimage(toolkit.getDefaulttoolkit().Createimage(server.class.getResource("qqq.png "))); contentArea = new JTextArea (); contentArea.Seeteditable (false); contentArea.setforenground (color.Blue); txt_message = new JTextField (); txt_max = new JTextField ("30"); txt_port = new JTextField ("6666"); btn_start = new Jbutton ("start"); btn_stop = new Jbutton ("stop"); btn_send = new Jbutton ("send"); btn_stop.setenabled (false); listModel = new DefaultListModel (); userList = new JList (listModel); Southpanel = New JPanel (New BorderLayout ()); Southpanel.SetBorder (neuer Titel ("Schreibmeldung")); Southpanel.add (txt_message, "center"); Southpanel.Add (Btn_Send, "East"); linkSpanel = new JScrollPane (UserList); linkSpanel.setBorder (neuer Titel ("Online -Benutzer")); rightPanel = new JScrollPane (contentArea); RightPanel.SetBorder (neuer Titel ("Nachrichtenanzeigebereich")); CenterSplit = new JSplitPane (jSplitpane.horizontal_split, links, rechterPanel); CentersPlit.SetDividerLocation (100); Northpanel = new jpanel (); Northpanel.SetLayout (New GridLayout (1, 6)); Northpanel.add (New Jlabel ("Obergrenze")); Northpanel.add (txt_max); Northpanel.add (New Jlabel ("Port")); northpanel.add (txt_port); northpanel.add (btn_start); Northpanel.Add (btn_stop); Northpanel.SetBorder (neuer Titel ("Konfigurationsinformationen")); Frame.SetLayout (New BorderLayout ()); Frame.Add (Northpanel, "North"); Frame.Add (CentersPlit, "Center"); Frame.Add (Southpanel, "South"); Frame.Setsize (600, 400); //frame.setsize(toolkit.getDefaulttoolkit (). GetCreenSize ()); // Setzen Sie den Vollbildschirm int screen_width = Toolkit.getDefaulttoolkit (). GetCreenSize (). Breite; int screen_height = Toolkit.getDefaulttoolkit (). GetCreenSize (). Höhe; Frame.SetLocation ((screen_width - Frame.getWidth ()) / 2, (screen_height - Frame.Getheight ()) / 2); Frame.SetVisible (True); // Ereignis beim Schließen des Fensterrahmens. // Ereignis txt_message.addactactionListener (new ActionListener () {public void actionPerformed (actionEvent e) {send ();}}); // Ereignis btn_send.addactactionListener (new ActionListener () {public void actionPerformed (actionEvent arg0) {send ();}}); // Ereignis btn_start.addactactionListener (new actionListener () {public void actionPerformed (actionEvent e) {if (isStart) {joptionpane.showMessagedialog (Frame "," Der Server ist bereits im Start -up -Zustand, starten nicht wiederholt! " {max = integer.parseInt (txt_max.getText ()); Integer.ParseInt (TXT_PORT.GETTEXT (); Port + "/r/n"); txt_max.setenabled (false); txt_port.setenabled (false); btn_stop.setenabled (true); } catch (Exception exc) {joptionpane.showMessagedialog (Frame, exc.getMessage (), "Fehler", joptionpane.ERROR_MESSAGE); }}}); // Ereignis btn_stop.addactactionListener (new actionListener () {public void actionPerformed (actionEvent e) {if (! IsStart) {joptionpane.showMessagedialog (Frame, "Der Server hat noch nicht gestartet, nötig zu stoppen!" btn_start.setenable (true); JOPTIONPANE.SHOWMESSAGEIGOGOG (Frame, "Ausnahme ist aufgetreten, wenn der Server gestoppt wird!", "Fehler", joptionpane.Error_message); } // Starten Sie den Server public void serverstart (int max, int port) löscht java.net.bindException {try {Clients = new ArrayList <Clientthread> (); ServerSocket = New ServerSocket (Port); serverthread = new Serverthread (ServerSocket, max); serverthread.start (); isStart = true; } catch (bindException e) {isStart = false; Neue BindException werfen ("Portnummer wurde besetzt, bitte ändern Sie es!"); } catch (Ausnahme e1) {e1.printstacktrace (); isStart = false; Neue BindException ("Start Server -Ausnahme!"); }} // Schließen Sie den Server @Suppresswarnings ("Abschaltung") public void celdserver () {try {if (serverthread! Clients.get (i) .getWriter (). Flush (); // Ressourcenclients veröffentlichen. Clients.get (i) .Writer.CLOSE (); Clients.get (i) .socket.close (); Clients.Remove (i); } if (serverSocket! } catch (ioException e) {e.printstacktrace (); isStart = true; }} // Bulk Server Message public void sendServerMessage (String-Nachricht) {für (int i = clients.size ()-1; i> = 0; i--) {Clients.get (i) .getWriter (). Println ("Server:" + message + "(multiple send)"); Clients.get (i) .getWriter (). Flush (); }} // Server Thread Class Serverthread erweitert Thread {private ServerSocket ServerSocket; private int max; // obere Anzahl der Personen // Konstruktionsmethode des Server -Thread -Serverthreads (ServerSocket ServerSocket, int max) {this.ServerSocket = ServerSocket; this.max = max; } public void run () {while (true) {// Warten Sie weiter auf den Client -Link Try {Socket Socket = ServerSocket.accept (); if (Clients.size () == max) {// Wenn die maximale Anzahl von Personen BufferedReader R = neuer BufferedReader (neuer InputStreamReader (Socket.GetInputStream ()) erreicht hat; Pressewriter w = neuer Pressewriter (Socket .GetOutputStream ()); // grundlegende Benutzerinformationen aus der Client -Zeichenfolge inf = R.Readline () erhalten; StringTokenizer st = new StringTokenizer (inf, "@"); User user = neuer user (st.nextToken (), St.NextToken ()); // Feedback -Verbindungserfolgsinformationen W.println ("max@server: sorry" + user.getName () + user.getIp () + ", die Anzahl der online auf dem Server online erreichten Obergrenze, bitte versuchen Sie, sich später zu verbinden!"). W.Flush (); // die Ressource R.CLOSE () freigeben; W.CLOSE (); socket.close (); weitermachen; } ClientThread client = new ClientThread (Socket); Client.start (); // Starten Sie die Threads für diesen Client Service Clients.add (Client); listModel.addelement (Client.getUser (). getName ()); // Aktualisieren Sie die Online -Liste contentArea.append (client.getUser (). } catch (ioException e) {e.printstacktrace (); }}}}} // Thread serviert eine Client -Klasse -ClientThread erweitert Thread {privater Socket -Socket; privater BufferedReader -Leser; privater Prinkerschreiber; privater Benutzerbenutzer; public bufferedReader getReader () {return reader; } public printwriter getWriter () {return writer; } public user getUser () {zurückgebener Benutzer; } // Konstruktor des Client Thread public ClientThread (Socket Socket) {try {this.socket = socket; Reader = neuer BufferedReader (neuer InputStreamReader (Socket .GetInputStream ())); writer = neuer printwriter (socket.getOutputStream ()); // grundlegende Benutzerinformationen aus der Client -Zeichenfolge inf = reader.readline () erhalten; StringTokenizer st = new StringTokenizer (inf, "@"); user = neuer Benutzer (St.NextToken (), St.NextToken ()); // Feedback Connection Success Information Writer.println (user.getName () + user.getIp () + "erfolgreich mit dem Server verbinden!"); writer.flush (); // Feedback zu den aktuellen Online -Benutzerinformationen if (Clients.size ()> 0) {String temp = ""; für (int i = Clients.size ()-1; i> = 0; i--) {temp + = (Clients.get (i) .GetUser (). getName () + "/" + clients .get (i) .getUser (). getiP ()) + "@"; } writer.println ("userList@" + clients.size () + "@" + temp); writer.flush (); } // den Benutzer Online-Befehl an alle Online-Benutzer für (int i = clients.size ()-1; i> = 0; i--) {Clients.get (i) .getWriter (). Println ("add@" + user.getName () + user.getIp ()) senden; Clients.get (i) .getWriter (). Flush (); }} catch (ioException e) {e.printstacktrace (); }} @SuppressWarnings ("Abschaltung") public void run () {// Empfangen Sie kontinuierlich Nachrichten vom Client zur Verarbeitung. String message = null; while (true) {try {message = reader.readline (); // Client -Nachrichten empfangen if (message.equals ("close") // Offline -Befehl {contentArea.append (this.getUser (). getName () + this.getUser (). // Ressourcenleser trennen und veröffentlichen .CLOSE (); writer.close (); socket.close (); // Senden Sie den Offline-Befehl des Benutzers an alle Online-Benutzer für (int i = clients.size ()-1; i> = 0; i--) {Clients.get (i) .getWriter (). Println ("Delete@" + user.getName ()); Clients.get (i) .getWriter (). Flush (); } listModel.removeElement (user.getName ()); // Die Online-Liste aktualisieren // diesen Client-Service-Thread für (int i = clients.size ()-1; i> = 0; i--) {if (Clients.Get (i) .GetUser () == Benutzer) {ClientThread temp = clients.get (i); Clients.Remove (i); // Löschen Sie den Service -Thread temp.stop (); // Stoppen Sie diesen Service -Thread -Rückgabe; }}} else {DispatherMessage (message); // meldung}} catch (ioException e) {e.printstacktrace (); }}} // meldung public void iscorcherMessage (String -Nachricht) {StringTokenizer stringTokenizer = new StringOkenizer (meldung, "@"); String Source = StringOkenizer.NextToken (); String besitzer = stringOkenizer.nextToken (); String content = stringTokenizer.NextToken (); Message = Quelle + ":" + Inhalt; contentArea.Append (message + "/r/n"); if (besitzer.equals ("all") {// bulk for (int i = clients.size ()-1; i> = 0; i--) {Clients.get (i) .getWriter (). println (message + "(multiple send)"); Clients.get (i) .getWriter (). Flush (); }}}}}}}Client_more.java
Import Java.awt.BorderLayout; Import Java.awt.Color; Import Java.awt.GridLayout; Import Java.awt.toolkit; Import Java.awt.event.ActionEvent; Import Java.awt.event.ActionListener; java.awt.event.windowevent; import Java.io.buffenedReader; Import Java.io.ioException; Import Java.io.inputStreamReader; Import Java.io.printwriter import Java.net.socket; import. javax.swing.defaultListmodel; import Javax.swing.jbutton; import Javax.swing.jframe; import Javax.swing.jlabel; Javax.swing.jlist; Javax.Swing.JoptionPane; Import Javax.Swing.jpanel; javax.swing.jsplitpane; import javax.swing.jtextarea; import javax.swing.jtextfield; import javax.swing.border.titledBorder; öffentliche Klasse client_more {private JFrame -Frame; private jList userList; private jtextarea textarea; Private JTextfield Textfield; private jTextfield txt_port; private jTextfield txt_hostip; private jTextfield txt_name; Privat Jbutton btn_start; Privat Jbutton Btn_Stop; Privat Jbutton btn_send; Privat Jpanel Northpanel; Privat Jpanel Southpanel; Privat Jscrollpane Rightscroll; Privat Jscrollpane LeftScroll; Privat JSplitPane CentersPlit; private StandardListModel ListModel; privat boolean is connected = false; private Socket Socket; privater Prinkerschreiber; privater BufferedReader -Leser; private messagethread messagethread; // Der Thread für den Empfangen von Nachrichten private Karte <string, Benutzer> Onlineusers = New HashMap <String, Benutzer> (); // Alle Online -Benutzer // Hauptmethode, Programmeingabe public static void main (String [] args) {new client_more (); } // Senden von öffentlicher void send () {if (! Isconnected) {joptionpane.showMessagedialog (Frame, "Der Server wurde noch nicht damit verbunden, und die Nachricht kann nicht gesendet werden!", "Fehler", joptionpane.error_message); zurückkehren; } String message = textField.getText (). Trim (); if (message == null || message.equals ("") {joptionpane.showmessagedialog (Frame, "Nachricht kann nicht leer sein!", "Fehler", joptionpane.error_message); zurückkehren; } sendMessage (Frame.gettitle () + "@" + "All" + "@" + Message); textfield.setText (null); } // Constructor public Client_more () {textArea = new jTextArea (); textArea.Seeteditable (false); textArea.setforenground (color.Blue); textField = new JTextField (); txt_port = new JTextField ("6666"); txt_hostip = new JTextField ("127.0.0.1"); txt_name = new JTextField ("wu chengqian"); btn_start = new Jbutton ("Verbindung"); btn_stop = new Jbutton ("tricnect"); btn_send = new Jbutton ("send"); listModel = new DefaultListModel (); userList = new JList (listModel); Northpanel = new jpanel (); Northpanel.SetLayout (New GridLayout (1, 7)); Northpanel.add (New Jlabel ("Port")); northpanel.add (txt_port); Northpanel.add (New JLabel ("Server IP")); northpanel.add (txt_hostip); Northpanel.add (New Jlabel ("Name")); northpanel.add (txt_name); northpanel.add (btn_start); Northpanel.Add (btn_stop); Northpanel.SetBorder (neuer Titel ("Verbindungsinformationen")); RightsCroll = new JscrollPane (textarea); RightsCroll.SetBorder (neuer Titel ("Nachrichtenanzeigebereich")); LeftScroll = new JScrollPane (UserList); LeftScroll.SetBorder (neuer Titel ("Online -Benutzer")); Southpanel = New JPanel (New BorderLayout ()); Southpanel.Add (Textfield, "Center"); Southpanel.Add (Btn_Send, "East"); Southpanel.SetBorder (neuer Titel ("Schreibmeldung")); CenterSplit = new JSplitPane (jSplitpane.Horizontal_Split, LeftScroll, RightsCroll); CentersPlit.SetDividerLocation (100); Frame = new JFrame ("Client"); // Ändern Sie das Symbol der JFRAME: // Frame.Seticonimage (Toolkit.getDefaulttoolkit (). CreateImage (Client.class.getResource ("qq.png")); Frame.SetLayout (New BorderLayout ()); Frame.Add (Northpanel, "North"); Frame.Add (CentersPlit, "Center"); Frame.Add (Southpanel, "South"); Frame.Setsize (600, 400); int screen_width = Toolkit.getDefaulttoolkit (). GetCreenSize (). Breite; int screen_height = Toolkit.getDefaulttoolkit (). GetCreenSize (). Höhe; Frame.SetLocation ((screen_width - Frame.getWidth ()) / 2, (screen_height - Frame.Getheight ()) / 2); Frame.SetVisible (True); // Ereignis beim Drücken von Eingabetaste in das Textfeld zum Schreiben des Meldung TextField. // Ereignis btn_send.addactactionListener (new ActionListener () {public void actionPerformed (actionEvent e) {send ();}}); // Ereignis btn_start.addactactionListener (new ActionListener () {public void actionPerformed (actionEvent e) {int port; if (is connected) {joptionpane.showMessagedialog (Frame ", es ist bereits auf der Verbindung, wiederholen Sie die Verbindung nicht!" Integer.ParseInt (TXT_PORT.GETTEXT (). TRIM ()); Hostip.Equals (")) {Neue Ausnahme (Name und Server IP kann nicht leer sein!"); Joptionpane.showMessagedialog (Frame, exc.getMessage (), "Fehler", joptionpane.ERROR_MESSAGE); }}}); // Ereignis btn_stop.addactactionListener (new ActionListener () {public void actionPerformed (actionEvent e) {if (! Isconnected) {joptionpane.showMessagedialog (Frame ", es ist bereits in einem nicht trennen Zustand, trukieren Sie wiederholt. Flag = CloseConnection (); // Trennen Sie die neue Ausnahme ("Ausnahme aufgetreten, wenn Sie die Verbindung entsprechen!"); JOPTIONPANE.ERROR_MESSAGE); // Ereignis beim Schließen des Fensterrahmens. }/ ** * Verbinden Sie eine Verbindung zum Server * * @param port * @param hostip * @param name */ public boolean ConnectServer (int port, String hostip, String -Name) {// Verbindung zum Server try {socket = new socket (hostip, port); // eine Verbindung basierend auf der Portnummer und dem Server -IP -Writer = New Prinker (socket. Reader = neuer BufferedReader (neuer InputStreamReader (Socket .GetInputStream ())); // grundlegende Informationen des Client -Benutzers (Benutzername und IP -Adresse) senden SendMessage (Name + "@" + socket.getLocalAddress (). ToString ()); // Öffnen Sie den Thread, um Nachrichten Messagethread = New Messagethread (Leser, TextArea) zu empfangen. messagethread.start (); isconnected = true; // wurde angeschlossen, um True zurückzugeben; } catch (Ausnahme e) {textArea.Append ("Der Server mit Portnummer:" + port + "IP -Adresse:" + hostip + "fehlgeschlagen!" + "/r/n"); isconnected = false; // false auf nicht angeschlossene zurückgeben; }} / ** * Nachricht senden * * @param meldung * / public void sendMessage (String meldung) {writer.println (message); writer.flush (); }/ *** Client schließt aktiv die Verbindung*/ @Suppresswarnings ("Abschaltung") public synchronisierte boolean Closeconnection () {try {sendMessage ("schließen"); // Senden Sie den Befehl diskonnect an den server messagethread.stop (); // Reace -Akzeptieren von Messing // //) Ressource (Reader! } if (writer! = null) {writer.close (); } if (socket! = null) {socket.close (); } isconnected = false; zurückkehren; } catch (ioException e1) {e1.printstacktrace (); is connected = true; false zurückgeben; }} // Thread, der ständig Nachrichtenklasse Messagethread erweitert {private bufferedReader reader; private jtextarea textarea; // Konstruktion der Methode zum Empfangen von Message Thread Public Messagethread (BufferedReader Reader, jtextArea textarea) {this.Reader = reader; this.textarea = textArea; } // passiv die Verbindung verschließen public // passiv die Verbindung schließen und die Ressource veröffentlichen, wenn (reader! = Null) {reader.close (); } if (writer! = null) {writer.close (); } if (socket! = null) {socket.close (); } isconnected = false; // den Status so modifizieren, dass public void run () {String message = ""; while (true) {try {message = reader.readline (); StringTokenizer StringOkenizer = new StringTokenizer (Nachricht, "/@"); String command = stringTokenizer.NextToken (); // Befehl if (command.equals ("close") // server geschlossener Befehl {textArea.Append ("Server geschlossen!/R/n"); CloseScon (); // Schließen Sie die Verbindungsrückgabe passiv; // End -Thread} else if (command.equals ("add") {// Einige Benutzer aktualisieren die Online -Liste String username = ""; String userip = ""; if ((userername = stringTokenizer.NextToken ())! Onlineusers.put (Benutzername, Benutzer); ListModel.Addelement (Benutzername); }} else if (command.equals ("delete")) {// Einige Benutzer pendeln die Online -Liste String username = StringOkenizer.NextToken (); User user = (user) onlineusers.get (Benutzername); Onlineusers.Remove (Benutzer); ListModel.RemoveElement (Benutzername); } else if (command.equals ("userList") {// Online -Benutzerliste int Size = Integer .ParSInt (stringTokenizer.NextToken ()); String username = null; String userip = null; für (int i = 0; i <size; i ++) {username = stringTokenizer.nextToken (); userip = stringTokenizer.nextToken (); User user = neuer user (userername, userIp); Onlineusers.put (Benutzername, Benutzer); ListModel.Addelement (Benutzername); }} else if (command.equals ("max")) {// Die Anzahl der Personen hat die obere Grenze textarea.append (stringSeceizer.nextToken () + StringTokenizer.NextToken () + "/r/n") erreicht; CloseScon (); // Schließen Sie die Verbindung joptionPane.showMessAntialog (Frame, "Der Serverpuffer ist voll!", "Fehler", joptionpane.ERROR_MESSAGE); return; // End -Thread} else {// normale Nachricht textarea.append (message + "/r/n"); }} catch (ioException e) {e.printstacktrace (); } catch (Ausnahme e) {e.printstacktrace (); }}}}}Das obige ist der gesamte Inhalt dieses Artikels. Ich hoffe, es wird für das Lernen aller hilfreich sein und ich hoffe, jeder wird Wulin.com mehr unterstützen.