There are some serious bug s in the code written before to realize simple network communication. Write in detail later.
According to the last code, mainly increased the user registration, login page, and realized the real-time display of the current login status of the number of people. And solve some bug s not found last time. (refer to previous notes for main function codes https://www.cnblogs.com/yuqingsong-cheng/p/12740307.html)
To achieve user registration and login, I need to use database, because I am mainly studying Sql Server. Sql Server also supports Linux system. Then install and configure under Ubuntu system of my computer.
Link: https://docs.microsoft.com/zh-cn/sql/linux/quickstart-install-connect-red-hat?view=sql-server-ver15
Sql Server official website has installation guidance documents for each system, so follow the normal installation steps and install everything normally.
There was a problem putting it on the server. Alicloud student server has 2G memory (for activities and student ID, it's really delicious). But memory is a little bit small. SqlServer requires at least 2G memory. So we can only give up SqlServer and turn to Mysql.
Also install according to MySql's official guidance document. However, remote connection requires some "messy" configuration, so we started to "connect to Baidu" and recommended a solution, https://blog.csdn.net/ethan__xu/article/details/89320614 It is applicable to mysql8.0 and above.
Database part of the solution, began to write about login, registration class. In the login registration part, a new port is opened for socket connection. Because the function is simple, only insert and query statements are used.
The client reads in the login and registration information entered by the user and sends it to the server. The server connects to the database for query / insertion and sends the result to the client.
Instance code
1 package logindata; 2 3 import java.io.DataInputStream; 4 import java.io.DataOutputStream; 5 import java.io.IOException; 6 import java.net.ServerSocket; 7 import java.net.Socket; 8 import java.sql.Connection; 9 import java.sql.DriverManager; 10 import java.sql.ResultSet; 11 import java.sql.SQLException; 12 import java.sql.Statement; 13 import java.util.ArrayList; 14 15 public class LoginData implements Runnable{ 16 17 static ArrayList<Socket> loginsocket = new ArrayList(); 18 19 public LoginData() { } 20 21 @Override 22 public void run() { 23 ServerSocket serverSocket=null; 24 try { 25 serverSocket = new ServerSocket(6567); 26 } catch (IOException e) { 27 e.printStackTrace(); 28 } 29 while(true) { 30 Socket socket=null; 31 try { 32 socket = serverSocket.accept(); 33 } catch (IOException e) { 34 // TODO Auto-generated catch block 35 e.printStackTrace(); 36 } 37 loginsocket.add(socket); 38 39 Runnable runnable; 40 try { 41 runnable = new LoginDataIO(socket); 42 Thread thread = new Thread(runnable); 43 thread.start(); 44 } catch (IOException e) { 45 // TODO Auto-generated catch block 46 e.printStackTrace(); 47 } 48 } 49 } 50 } 51 52 class LoginDataIO implements Runnable{ 53 54 String b="false"; 55 Socket socket; 56 DataInputStream inputStream; 57 DataOutputStream outputStream; 58 public LoginDataIO(Socket soc) throws IOException { 59 socket = soc; 60 inputStream = new DataInputStream(socket.getInputStream()); 61 outputStream = new DataOutputStream(socket.getOutputStream()); 62 } 63 64 @Override 65 public void run() { 66 String readUTF = null; 67 String readUTF2 = null; 68 String readUTF3 = null; 69 try { 70 readUTF = inputStream.readUTF(); 71 readUTF2 = inputStream.readUTF(); 72 readUTF3 = inputStream.readUTF(); 73 } catch (IOException e) { 74 e.printStackTrace(); 75 } 76 77 // System.out.println(readUTF+readUTF2+readUTF3); 78 79 SqlServerCon serverCon = new SqlServerCon(); 80 try { 81 //Determine whether the connection is logged in or registered. The return value is different. 82 if(readUTF3.equals("login")) { 83 b=serverCon.con(readUTF, readUTF2); 84 outputStream.writeUTF(b); 85 }else { 86 String re=serverCon.insert(readUTF, readUTF2); 87 outputStream.writeUTF(re); 88 } 89 } catch (SQLException e) { 90 // TODO Auto-generated catch block 91 e.printStackTrace(); 92 } catch (IOException e) { 93 // TODO Auto-generated catch block 94 e.printStackTrace(); 95 } catch (ClassNotFoundException e) { 96 // TODO Auto-generated catch block 97 e.printStackTrace(); 98 } 99 100 // System.out.println(b); 101 } 102 } 103 104 105 class SqlServerCon { 106 107 public SqlServerCon() { 108 // TODO Auto-generated constructor stub 109 } 110 111 String name; 112 String password; 113 // boolean duge = false; 114 String duge = "false"; 115 // String url = "jdbc:sqlserver://127.0.0.1:1433;" 116 // + "databaseName=TestData;user=sa;password=123456"; 117 /** 118 * com.mysql.jdbc.Driver Replace with com.mysql.cj.jdbc.Driver. 119 MySQL 8.0 If the above version does not need to establish an SSL connection, it needs to display close. 120 Finally, you need to set up CST. 121 */ 122 //Connect MySql data base url format 123 String url = "jdbc:mysql://127.0.0.1:3306/mytestdata?useSSL=false&serverTimezone=UTC"; 124 public String con(String n,String p) throws SQLException, ClassNotFoundException { 125 Class.forName("com.mysql.cj.jdbc.Driver"); 126 Connection connection = DriverManager.getConnection(url,"root","uu-7w3yfu?VX"); 127 // System.out.println(connection); 128 129 Statement statement = connection.createStatement(); 130 // statement.executeUpdate("insert into Data values('china','123456')"); 131 ResultSet executeQuery = statement.executeQuery("select * from persondata"); 132 133 //Login nickname password confirmation 134 while(executeQuery.next()) { 135 name=executeQuery.getString(1).trim(); 136 password = executeQuery.getString(2).trim(); //"It's important to use this method" String trim() The return value is the string of this string, where all leading and trailing spaces have been removed. 137 // System.out.println(n.equals(name)); 138 if(name.equals(n) && password.equals(p)) { 139 duge="true"; 140 break; 141 } 142 } 143 statement.close(); 144 connection.close(); 145 // System.out.println(duge); 146 return duge; 147 } 148 149 public String insert(String n,String p) throws SQLException, ClassNotFoundException { 150 boolean b = true; 151 String re = null; 152 Class.forName("com.mysql.cj.jdbc.Driver"); 153 Connection connection = DriverManager.getConnection(url,"root","uu-7w3yfu?VX"); 154 Statement statement = connection.createStatement(); 155 156 ResultSet executeQuery = statement.executeQuery("select * from persondata"); 157 while(executeQuery.next()) { 158 name=executeQuery.getString(1).trim(); 159 // password = executeQuery.getString(2).trim(); 160 if(name.equals(n)) { 161 b=false; 162 break; 163 } 164 } 165 166 //Return to login information 167 if(b && n.length()!=0 && p.length()!=0) { 168 String in = "insert into persondata "+"values("+"'"+n+"'"+","+"'"+p+"'"+")"; //This insert statement is very useful, but I didn't expect it to be better. 169 // System.out.println(in); 170 statement.executeUpdate(in); 171 statement.close(); 172 connection.close(); 173 re="login was successful,Please return to login"; 174 return re; 175 }else if(n.length()==0 || p.length()==0 ) { 176 re="Nickname or password cannot be empty, please re-enter"; 177 return re; 178 }else { 179 re="The nickname user already exists, please re-enter or log in"; 180 return re; 181 } 182 } 183 }
Because the server needs to be placed in the server, the user interface of the server is deleted.
1 import file.File; 2 import logindata.LoginData; 3 import server.Server; 4 5 public class ServerStart_View { 6 7 private static Server server = new Server(); 8 private static File file = new File(); 9 private static LoginData loginData = new LoginData(); 10 public static void main(String [] args) { 11 ServerStart_View frame = new ServerStart_View(); 12 server.get(frame); 13 Thread thread = new Thread(server); 14 thread.start(); 15 16 Thread thread2 = new Thread(file); 17 thread2.start(); 18 19 Thread thread3 = new Thread(loginData); 20 thread3.start(); 21 } 22 public void setText(String AllName,String string) { 23 System.out.println(AllName+" : "+string); 24 } 25 }
For the client, the login interface connects with the service belt through socket, sends the user information and reads the returned information.
Main code:
1 public class Login_View extends JFrame { 2 3 public static String AllName=null; 4 static Login_View frame; 5 private JPanel contentPane; 6 private JTextField textField; 7 private JTextField textField_1; 8 JOptionPane optionPane = new JOptionPane(); 9 private final Action action = new SwingAction(); 10 private JButton btnNewButton_1; 11 private final Action action_1 = new SwingAction_1(); 12 private JLabel lblNewLabel_2; 13 14 /** 15 * Launch the application. 16 */ 17 public static void main(String[] args) { 18 EventQueue.invokeLater(new Runnable() { 19 public void run() { 20 try { 21 frame = new Login_View(); 22 frame.setVisible(true); 23 frame.setDefaultCloseOperation(EXIT_ON_CLOSE); 24 } catch (Exception e) { 25 e.printStackTrace(); 26 } 27 } 28 }); 29 } 30 31 .................. 32 .................. 33 .................. 34 35 private class SwingAction extends AbstractAction { 36 public SwingAction() { 37 putValue(NAME, "Sign in"); 38 putValue(SHORT_DESCRIPTION, "Click to log in"); 39 } 40 public void actionPerformed(ActionEvent e) { 41 String text = textField.getText(); 42 String text2 = textField_1.getText(); 43 // System.out.println(text+text2); 44 // boolean boo=false; 45 String boo=null; 46 try { 47 boo = DataJudge.Judge(6567,text,text2,"login"); 48 } catch (IOException e1) { 49 e1.printStackTrace(); 50 } 51 if(boo.equals("true")) { 52 ClientStart_View.main1(); 53 AllName = text; //Save user name 54 frame.dispose(); //void dispose() Release this this Window,All native screen resources used by its subcomponents and all its owned children. 55 }else { 56 optionPane.showConfirmDialog 57 (contentPane, "Wrong user name or password,Please enter again", "Login failed",JOptionPane.OK_CANCEL_OPTION); 58 } 59 } 60 } 61 62 private class SwingAction_1 extends AbstractAction { 63 public SwingAction_1() { 64 putValue(NAME, "register"); 65 putValue(SHORT_DESCRIPTION, "Click to enter the registration page"); 66 } 67 public void actionPerformed(ActionEvent e) { 68 Registered_View registered = new Registered_View(Login_View.this); 69 registered.setLocationRelativeTo(rootPane); 70 registered.setVisible(true); 71 } 72 } 73 }
Connect to the server: the connection method is Boolean type when writing for the first time, but it is only applicable to the information judgment of login. When registering, it is necessary to judge whether the nickname is duplicate, whether the password nickname is empty and other different return information. (the server code has the corresponding judgment String return, parameter 1). So the connection method is changed to String type.
1 import java.io.DataInputStream; 2 import java.io.DataOutputStream; 3 import java.io.IOException; 4 import java.net.Socket; 5 import java.net.UnknownHostException; 6 7 public class DataJudge { 8 9 /*public static boolean Judge(int port,String name,String password,String judge) throws UnknownHostException, IOException { 10 11 Socket socket = new Socket("127.0.0.1", port); 12 DataInputStream inputStream = new DataInputStream(socket.getInputStream()); 13 DataOutputStream outputStream = new DataOutputStream(socket.getOutputStream()); 14 15 outputStream.writeUTF(name); 16 outputStream.writeUTF(password); 17 outputStream.writeUTF(judge); 18 19 boolean readBoolean = inputStream.readBoolean(); 20 21 outputStream.close(); 22 inputStream.close(); 23 socket.close(); 24 return readBoolean; 25 }*/ 26 27 public static String Judge(int port,String name,String password,String judge) throws UnknownHostException, IOException { 28 29 //Connect to the server database 30 Socket socket = new Socket("127.0.0.1", port); 31 DataInputStream inputStream = new DataInputStream(socket.getInputStream()); 32 DataOutputStream outputStream = new DataOutputStream(socket.getOutputStream()); 33 34 outputStream.writeUTF(name); 35 outputStream.writeUTF(password); 36 outputStream.writeUTF(judge); 37 38 String read = inputStream.readUTF(); 39 40 //Login is one-time, so close it in time socket 41 outputStream.close(); 42 inputStream.close(); 43 socket.close(); 44 return read; 45 } 46 }
User registration interface, main code:
1 public class Registered_View extends JDialog{ 2 // DataJudge dataJudge = new DataJudge(); 3 private JTextField textField_1; 4 private JTextField textField; 5 JLabel lblNewLabel_2; 6 private final Action action = new SwingAction(); 7 8 public Registered_View(JFrame frame) { 9 super(frame, "", true); //Make the registration dialog box appear on the main panel. 10 ......... 11 ......... 12 ......... 13 ......... 14 } 15 16 private class SwingAction extends AbstractAction { 17 public SwingAction() { 18 putValue(NAME, "register"); 19 putValue(SHORT_DESCRIPTION, "Click the button to register"); 20 } 21 public void actionPerformed(ActionEvent e) { 22 String b=null; //The registration information string used to receive the return from the server 23 String name = textField.getText(); 24 String password = textField_1.getText(); 25 try { 26 b = DataJudge.Judge(6567, name, password, "registered"); 27 } catch (IOException e1) { 28 // TODO Auto-generated catch block 29 e1.printStackTrace(); 30 } 31 32 lblNewLabel_2.setText(b); 33 } 34 }
The user logs in, and the registration part is completed.
Display the number of people in real time, mainly return the generic array size of the storage socket object to the client. This method is called after new client connections are available, and this method is called when a user disconnects.
1 public static void SendInfo(String rece, String AllName, String num) throws IOException { 2 DataOutputStream outputStream = null; 3 for (Socket Ssocket : Server.socketList) { 4 outputStream = new DataOutputStream(Ssocket.getOutputStream()); 5 outputStream.writeUTF(num); 6 outputStream.writeUTF(AllName); 7 outputStream.writeUTF(rece); 8 outputStream.flush(); 9 } 10 }
Talk about Bug
Before each disconnection, the user does not close the socket first, and the server does not remove the corresponding socket object, which results in that when the server sends it to each client one by one, the closed socket object cannot be found and a "write error" will be generated.
Therefore, it is necessary to remove the corresponding socket object when the client is disconnected, check the java API document, and find no way to judge whether the client socket is closed at the server.
Then I thought of the way I looked before. (although it's a bit of a hassle, I can't find a better way.). Then, when you click the exit button or close the panel, send a "by" character to the server. When the server reads this character, it knows that the client is about to disconnect, so as to exit the circular read operation and remove the corresponding socket object.
1 Panel off event listening 2 3 @Override 4 public void windowClosing(WindowEvent arg0) { 5 try { 6 chat_Client.send("bye"); 7 File_O.file_O.readbye("bye"); 8 } catch (IOException e) { 9 // TODO Auto-generated catch block 10 e.printStackTrace(); 11 } 12 }
1 Exit button event listening 2 3 private class SwingAction extends AbstractAction { 4 public SwingAction() { 5 putValue(NAME, "Sign out"); 6 putValue(SHORT_DESCRIPTION, "close program"); 7 } 8 public void actionPerformed(ActionEvent e) { 9 int result=optionPane.showConfirmDialog(contentPane, "Close exit or not", "Exit reminder", JOptionPane.YES_NO_OPTION); 10 if(result==JOptionPane.YES_OPTION) { 11 try { 12 chat_Client.send("bye"); 13 File_O.file_O.readbye("bye"); 14 System.exit(EXIT_ON_CLOSE); //static void exit(int status) Terminate currently running Java Virtual machine. That is, terminate the current program and close the window. 15 } catch (IOException e1) { 16 e1.printStackTrace(); 17 } 18 } 19 } 20 }
1 client send Method, sending finished bye After characters, close socket 2 3 //send()Method to send a message to the server. "Send" button Button click event to call this method 4 public void send(String send) throws IOException { 5 DataOutputStream stream = new DataOutputStream(socket.getOutputStream()); 6 stream.writeUTF(Login_View.AllName); 7 stream.writeUTF(send); 8 9 if(send.equals("bye")) { 10 stream.flush(); 11 socket.close(); 12 } 13 }
1. When the server reads the byte character, remove the corresponding socket object and exit the while loop 2 3 if (rece.equals("bye")) { 4 judg = false; 5 Server.socketList.remove(socket); 6 Server_IO.SendInfo("", "", "" + Server.socketList.size()); 7 /* 8 * for (Socket Ssocket:Server.socketList) { DataOutputStream outputStream = new 9 * DataOutputStream(socket.getOutputStream()); outputStream = new 10 * DataOutputStream(Ssocket.getOutputStream()); 11 * outputStream.writeUTF(""+Server.socketList.size()); 12 * outputStream.writeUTF(""); outputStream.writeUTF(""); 13 * System.out.println("8888888888888888"); outputStream.flush(); } 14 */ 15 break; 16 }
The same is true for the closing and removal of the file stream, which is not covered in detail.
There is another problem with the file stream. Normal login cannot perform the second file transfer. (the first time I wrote it, I probably only tested it once, but I didn't find any bug s. Hahahaha)
It took a long time to solve this problem (too cai, ha ha ha ha ha)
The original code, which is read and sent by the server (you can also take part in the previous essay)
1 while((len=input.read(read,0,read.length))>0) { 2 for(Socket soc:File.socketList_IO) { 3 if(soc != socket) 4 { 5 output = new DataOutputStream(soc.getOutputStream()); 6 output.writeUTF(name); 7 output.write(read,0,len); 8 output.flush(); 9 // System.out.println("Start forwarding to client"); 10 } 11 } 12 // System.out.println("implement"); 13 // System.out.println(len); 14 }
read() method: introduction to API documents
When reading to the end of the file, - 1 will be returned. You can see that the while loop ends when len is equal to - 1, but it doesn't work as expected. When debug ging (forgetting the screenshot), it is found that as long as the output stream of the client is not closed, the server will always block after reading the file
While ((len = input. Read (read, 0, read. Length)) > 0), unable to exit, thus unable to forward the next read. Nor can len=-1 be used for break;
Amend to read:
1 int len=0; 2 while(true) { 3 len=0; 4 if(input.available()!=0) 5 len=input.read(read,0,read.length); 6 if(len==0) break; 7 for(Socket soc:File.socketlist_file) { 8 if(soc != socket) 9 { 10 output = new DataOutputStream(soc.getOutputStream()); 11 output.writeUTF(name); 12 output.write(read,0,len); 13 // output.flush(); 14 // System.out.println("Start forwarding to client"); 15 } 16 // System.out.println("One time forwarding"+File.socketlist_file.size()); 17 } 18 }
That's it
I feel that there are still problems in the transfer and reading of files, and I will continue to improve next time.
Screenshot of some interfaces