记录一个咸鱼大学生三个月的奋进生活025

  • 复习Java(仿QQ聊天系统03两种通信类、登录以及注册功能完善)
    • TcpSocket类(与服务器进行通信)
    • Server类(服务器类)
    • TcpMessage类(报文类)
    • ServerThread类(线程操作类的登录功能代码部分)
    • UdpSocket类(客户端之间进行通信)
    • LoginFrame类(登录界面代码完善)
    • LoginFrame登录页面 结果展示
    • ServerThread类(线程操作类的注册功能代码部分)
    • RegisterFrame类(注册界面代码完善)
    • RegisterFrame注册页面 结果展示
  • 学习Java面试题(并发编程相关知识)
  • 照片分享

复习Java(仿QQ聊天系统03两种通信类、登录以及注册功能完善)

  今天咱们完善 登录页面的功能 以及 注册页面的功能

  在完善之前我们首先需要一个Server类作为服务器,他的功能是:当有用户登录时就实例化一个线程(相当于每个用户的客户端),所以在登录页面代码就需要利用socket与服务器Server进行通信

在与Server服务器建立通信之前我们还要一个TcpSocket类用于与Server服务器进行通信:

TcpSocket类(与服务器进行通信)

package com.qq.pub;// 封装客户端与服务器进行交流的代码,隐藏代码思路并使代码变得简洁
// 其实TcpSocket相当于客户端登录了和服务器通信所用的工具
// 与服务器通信了才能进行之后的各种操作import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.net.Socket;
import java.net.UnknownHostException;public class TcpSocket {private Socket socket = null;private ObjectOutputStream out = null;private ObjectInputStream in = null;// TcpSocket构造方法,当调用这个构造方法并传入参数ip和port值时,就将这两个值实例化成socket进行服务器网络传输连接请求public TcpSocket() {// TODO Auto-generated constructor stub}public TcpSocket(String ip, int port) {try {this.socket = new Socket(ip, port);out = new ObjectOutputStream(this.socket.getOutputStream());in = new ObjectInputStream(this.socket.getInputStream());} catch (UnknownHostException e) {// TODO Auto-generated catch blocke.printStackTrace();} catch (IOException e) {// TODO Auto-generated catch blocke.printStackTrace();}}// submit方法参数为一个TcpMessage类的报文实例sMessage为需要发送的报文public TcpMessage submit(TcpMessage sMessage) {TcpMessage rMessage = null;          // rMessage为需要接受的报文,他同样也是一个TcpMessage报文类的实例try {// 将sMessage对象通过writeObject传输对象的方法发送this.out.writeObject(sMessage);this.out.flush();//等待服务器处理rMessage = (TcpMessage)this.in.readObject();} catch (ClassNotFoundException e) {// TODO Auto-generated catch blocke.printStackTrace();} catch (IOException e) {// TODO Auto-generated catch blocke.printStackTrace();}return rMessage;}// 获得当前通信的客户端的ip地址方法public String getIp() {return this.socket.getLocalAddress().getHostAddress();}
}

然后就是Server服务器类:

Server类(服务器类)

package com.qq.server;// 服务器需要对应多个客户端client所以服务器需要实现多线程import java.io.IOException;
import java.net.ServerSocket;
import java.net.Socket;import com.qq.pub.CommonUse;public class Server {public static void main(String[] args) {System.out.println("服务器启动");ServerSocket serverSocket = null;Socket socket = null;try {// 实例化ServerSocket参数传入的是port值,只是之前在pub的CommonUse类中给的SERVER_PORT变量serverSocket = new ServerSocket(CommonUse.SERVER_PORT);       // 这个实例化ServerSocket千万不能放进循环因为这样会一直新给一个port值while(true) {         // 要进行很多次的Socket通讯所以要放到死循环里,相当于一直启动着服务器socket = serverSocket.accept();                           // 有人通讯就接收通讯ServerSocket的一个SocketServerThread thread = new ServerThread(socket);           // 每有一个Socket就实例一个新线程thread.start();}} catch (IOException e) {// TODO Auto-generated catch blocke.printStackTrace();}}
}

  大家仔细看了代码的时候会发现,Server服务器每接收(accept)到一个socket时就会将这个socket传入实例化一个ServerThread线程(这就是我们之前提到过的每个用户的客户端),这个类中就存放着所有需要执行的报文操作

  这时候就要引进一个概念就是报文,报文主要用于网络传输需要进行功能操作的场景,他其实就像处理事件一样,平常处理事件我们都是根据用户的操作进行不同的方法执行,而报文操作就是根据报文头然后对报文体进行操作,报文分为 报文头 和 报文体,报文头就像我刚才说的,是给服务器处理时的一个信号(类似于处理事件时的点击名为login的按钮),比如当报文头为login时,我们就对报文体进行login所要执行的操作,跟事件处理是一个意思

所以根据这些概念,我们来写一个报文类:

TcpMessage类(报文类)

package com.qq.pub;// 该类是报文类,像是一种传递工具但里面放的东西不一样,会根据报文头的不同情况去用不同的方法去操作体
// 头是要使用的方法,体是被使用的东西
// implements Serializable 这只是一个标识接口代表该类是报文类里面无方法需要实现import java.io.Serializable;
import java.util.HashMap;
import java.util.Map;// implements Serializable接口 就代表这是一个报文类了
public class TcpMessage implements Serializable {private String head = null;             // 报文头private Map<String, Object> map = new HashMap<String, Object>();            // 报文体,里面要有key标识还有需要被执行操作的对象public String getHead() {return head;}public void setHead(String head) {this.head = head;}public void setBody(String key, Object value) {this.map.put(key, value);}public Object getBody(String key) {return this.map.get(key);}
}

然后我们来写进行报文操作的ServerThread线程操作类中针对于登录功能的代码,要实现以下四个功能:① 要实现登录账户名密码的验证,② 然后将该用户标记为上线,③ 获取该用户所有信息,④ 然后上线了要与已经在线的好友建立udpsocket连接以后用来发送消息

ServerThread中用到的所有数据库操作方法都在我上一篇有关该项目的数据库类介绍文章中写到过:复习Java第二个项目仿QQ聊天系统 02(数据库部分以及数据实例类),里面 IQqUserDao接口 和 QqUserDaoImpl类 中都有介绍

ServerThread类(线程操作类的登录功能代码部分)

package com.qq.server;// 供Server服务器实例化多线程终端时的类,每有一个用户登录,Server就会实例化一个ServerThread线程类,然后实例化时参数传入通讯的socket实现网络传输
// 然后ServerThread里的socket会传递信息(报文)给每个页面,然后每个页面根据传输的报文进行判断然后执行操作import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.io.OutputStreamWriter;
import java.net.Socket;
import java.util.List;import com.qq.bean.Qquser;
import com.qq.dao.IQqUserDao;
import com.qq.dao.QqUserDaoImpl;
import com.qq.pub.CommonUse;
import com.qq.pub.TcpMessage;
import com.qq.pub.UdpSocket;public class ServerThread extends Thread {private Qquser fullUser = null;                      // 接收登录用户完整信息的类private Socket socket = null;private ObjectOutputStream out = null;private ObjectInputStream in = null;// checkUser方法传入用户登录时输入的账户密码去数据库查找是否有该用户并且判断密码是否正确public boolean checkUser(String id, String pass) {  IQqUserDao dao = new QqUserDaoImpl();Qquser qquser = dao.findById(id);               // dao中的findById是将id传入然后返回在数据库中查找到的对象给qquserif(qquser != null && qquser.getPassword().equals(pass)) {       // 如果返回了qquser也就是qquser != null并且返回的qquser的密码和用户输入的一样则登录成功返回truereturn true;}return false;}// findFullUser方法是通过用dao里的findById方法将登录用户的账号传进去返回该账号的所有信息对象private Qquser findFullUser(String account) {       return new QqUserDaoImpl().findById(account);}// midifyDB方法是将登录的用户传进去然后用dao中的update方法将数据库中的State状态信息改为1,ip和port值都做更改,实现用户的上线private void midifyDB(Qquser qquser) {              qquser.setState("1");new QqUserDaoImpl().update(qquser);}// onLine上线方法,查看所有该上线用户的在线的好友,并给所有在线的好友发送udpInfo报文以便之后的通讯private void onLine(Qquser qquser) {IQqUserDao dao = new QqUserDaoImpl();List<Qquser> list = dao.findBySql("select * from qquser where account"+ " in (select friendAccount from friend where userAccount = '" + qquser.getAccount() + "') and state = '1'");// udpInfo报文,报文头是在线,体是用户名,ip以及port值,都用复杂的断点CommonUse.UDP_PACKET_SYMBOL进行分割String udpInfo = CommonUse.ONLINE + CommonUse.UDP_PACKET_SYMBOL+ qquser.getAccount() + CommonUse.UDP_PACKET_SYMBOL+ qquser.getIp() + CommonUse.UDP_PACKET_SYMBOL+ qquser.getPort() + CommonUse.UDP_PACKET_SYMBOL;// 遍历查到的所有在线好友并用UdpSocket实例化,并传入ip值以及port值,这样就可以之后发送消息for (Qquser tempUser : list) {UdpSocket sendSocket = new UdpSocket(tempUser.getIp(), Integer.parseInt(tempUser.getPort()));         // Integer.parseInt() 将字符串转换为int型的方法sendSocket.send(udpInfo);                           // 调用UdpSocket的send方法将udpInfo报文发送过去}}// 重写构造方法public ServerThread() {// TODO Auto-generated constructor stub}public ServerThread(Socket socket) {System.out.println("有一个客户登录了");this.socket = socket;try {// 跟传入的socket对象进行对象流传输this.in = new ObjectInputStream(this.socket.getInputStream());this.out = new ObjectOutputStream(this.socket.getOutputStream());} catch (IOException e) {// TODO Auto-generated catch blocke.printStackTrace();}}// 重写线程的run方法@Overridepublic void run() {TcpMessage rMessage = null;try {while(true) {rMessage = (TcpMessage)this.in.readObject();String head = rMessage.getHead();                    // 获得读取的信息rMessage报文头// 如果获取的报文头是登录时else if(CommonUse.LOGIN.equals(head)) {TcpMessage sMessage = new TcpMessage();          // 实例化进行发送信息对象sMessageQquser loginUser = (Qquser)rMessage.getBody(CommonUse.QQ_USER);         // 获得报文体给loginUser对象if(this.checkUser(loginUser.getAccount(), loginUser.getPassword())) {   // 调用上面的checkUser方法将loginUser的账号以及密码传进去进行登录判断// 登录成功后调用上面的midifyDB方法将登录对象的数据库信息进行修改为在线状态this.midifyDB(loginUser);// 调用上面的findFullUser方法获得登录用户的全部信息this.fullUser = this.findFullUser(loginUser.getAccount());// 将发送信息对象sMessage报文头设为成功并将登录用户的所有信息对象设为报文体this.onLine(this.fullUser);                          // 调用online方法实例化所有在线的好友并用UdpSocket发送udpInfo报文以便之后的通讯sMessage.setHead(CommonUse.SUCCESSFUL);sMessage.setBody(CommonUse.QQ_USER, this.fullUser);} else {sMessage.setHead(CommonUse.FAILURE);}this.out.writeObject(sMessage);                  // 用对象流的书写方法将sMessage传输过去this.out.flush();} }} catch (ClassNotFoundException e) {// TODO Auto-generated catch blocke.printStackTrace();} catch (IOException e) {// 当有一个客户端关闭时System.out.println("有一个客户下线了");}}
}

然后我们写用于各个客户端之间进行操作的UdpSocket类:

UdpSocket类(客户端之间进行通信)

package com.qq.pub;// 与各终端之间进行通讯的UdpSocket
// 其实UdpSocket就负责各个客户端(用户)之间发送信息的功能import java.io.IOException;
import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.net.InetAddress;
import java.net.SocketException;
import java.net.UnknownHostException;public class UdpSocket {private DatagramSocket datagramSocket = null;                // DatagramSocket进行发送信息private DatagramPacket datagramPacket = null;                // DatagramPacket进行信息编写,根据发送还是接收实例化格式不同// 重写构造函数不传入参数ip和port值时就是使用接收信息的datagramPacketpublic UdpSocket() {byte[] pool = new byte[1024];try {this.datagramSocket = new DatagramSocket();this.datagramPacket = new DatagramPacket(pool, 0, pool.length);       // datagramPacket接收信息的格式} catch (SocketException e) {// TODO Auto-generated catch blocke.printStackTrace();}}// 重写构造函数传入参数ip和port值时就是使用发送信息的datagramPacketpublic UdpSocket(String ip, int port) {byte[] pool = new byte[1024];try {this.datagramSocket = new DatagramSocket();this.datagramPacket = new DatagramPacket(pool, 0, pool.length, InetAddress.getByName(ip), port);       // datagramPacket发送信息的格式} catch (SocketException e) {// TODO Auto-generated catch blocke.printStackTrace();} catch (UnknownHostException e) {// TODO Auto-generated catch blocke.printStackTrace();}}// send发送信息方法,将传入的参数作为数据进行传输public void send(String udpInfo) {this.datagramPacket.setData(udpInfo.getBytes());try {this.datagramSocket.send(this.datagramPacket);           // socket.send(packet);  .send()方法 发送packet对象} catch (IOException e) {// TODO Auto-generated catch blocke.printStackTrace();}}//  receive接收信息方法,该方法返回接收到的信息public String receive() {String data = null;try {this.datagramSocket.receive(this.datagramPacket);         // socket.receive(packet);  .receive()方法 接收packet对象data = new String(this.datagramPacket.getData());         // 将packet中的byte数据获取并转换为字符串} catch (IOException e) {// TODO Auto-generated catch blocke.printStackTrace();}return data;               // 返回接收到的信息}// getPort获得进行传输的终端的Port值的方法public int getPort() {return this.datagramSocket.getLocalPort();                    // .getLocalPort() 获得自己port值的方法}
}

最后就是完善我们之前写的LoginFrame类,之前只是完成了静态的页面代码,现在用上socket通信服务器,然后把登录的用户名密码打包成报文体给ServerThread进行操作,该报文头为login,注意要使用CommonUse中的变量LOGIN哦,以防打错字:

LoginFrame类(登录界面代码完善)

package com.qq.ui;// 用户进行登录的页面import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Font;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;import javax.swing.Box;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JOptionPane;
import javax.swing.JPanel;
import javax.swing.JPasswordField;
import javax.swing.JTextField;import com.qq.bean.Qquser;
import com.qq.component.ImgPanel;
import com.qq.pub.CommonUse;
import com.qq.pub.TcpMessage;
import com.qq.pub.TcpSocket;
import com.qq.pub.UdpSocket;public class LoginFrame extends JFrame implements ActionListener {private TcpSocket tcpSocket = null;           // 实例化TcpSocket与服务器进行操作的对象private UdpSocket receiveSocket = null;       // 实例化UdpSocket与其他终端进行操作的对象private JPanel bodyPanel;                     // 所有界面panel,一个大body和中间的登录框centerPanel以及底部的按钮bottomPanelprivate JPanel centerPanel;private JPanel bottomPanel;private JLabel accountLabel;                  // 用户名和密码的输入panelprivate JTextField accountTextField;private JLabel passowrdLabel;private JPasswordField passwordField;private JButton loginButton;                  // 登录和注册按钮private JButton registerButton;// 中间的操作panelprivate void initCenter() {this.centerPanel = new ImgPanel("./login.png");       // 实例化ImgPanel专门设背景的对象将登录页面的背景图片传入this.accountLabel = new JLabel("用 户 名:");this.accountTextField = new JTextField(16);this.accountLabel.setFont(new Font("微软雅黑", Font.BOLD, 17));        // setFont(new Font("字体", Font.样式, 字体大小))    设置字体样式的方法this.passowrdLabel = new JLabel("密     码:");this.passwordField = new JPasswordField(16);this.passowrdLabel.setFont(new Font("微软雅黑", Font.BOLD, 18));        // setFont(new Font("字体", Font.样式, 字体大小))    设置字体样式的方法// 设置box布局管理器Box box0 = Box.createVerticalBox();Box box1 = Box.createHorizontalBox();Box box2 = Box.createHorizontalBox();box1.add(accountLabel);box1.add(accountTextField);box2.add(passowrdLabel);box2.add(passwordField);box0.add(Box.createVerticalStrut(90));box0.add(box1);box0.add(Box.createVerticalStrut(20));box0.add(box2);this.centerPanel.add(box0);}// 底部的按钮panelprivate void initBottom() {this.bottomPanel = new JPanel();this.loginButton = new JButton("登 陆");this.loginButton.addActionListener(this);this.bottomPanel.add(this.loginButton);this.registerButton = new JButton("注 册");this.registerButton.addActionListener(this);this.bottomPanel.add(this.registerButton); }// 窗体代码private void init() {this.bodyPanel = (JPanel)this.getContentPane();this.bodyPanel.setLayout(new BorderLayout());this.initCenter();this.bodyPanel.add(this.centerPanel, BorderLayout.CENTER);this.initBottom();this.bodyPanel.add(this.bottomPanel, BorderLayout.SOUTH);this.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);this.setTitle("QQ登录页面");}// 重写构造方法public LoginFrame() {this.tcpSocket = new TcpSocket(CommonUse.SERVER_IP, CommonUse.SERVER_PORT);              // 通过TcpSocket类的构造方法来实例化请求服务器接收连接的socket,这里传入的参数是调用了CommonUse类中已经存好的变量,只会要用到这些属性都要调用CommonUse类中的变量,就能避免因为手抖写错的情况this.receiveSocket = new UdpSocket();                // 实例化receiveSocket与终端之间进行传输this.init();}// 主方法运行main方法public static void main(String[] args) {LoginFrame loginFrame1 = new LoginFrame();loginFrame1.setBounds(680, 340, 500, 320);loginFrame1.setVisible(true);}// 事件处理代码@Overridepublic void actionPerformed(ActionEvent e) {// 点击的是登录按钮时执行的操作if(e.getSource() == this.loginButton) {Qquser loginUser = new Qquser();            // 实例化登录的qquserString account = this.accountTextField.getText();          // 获得用户名以及密码的输入框内的信息String password = new String(this.passwordField.getPassword());// 将输入的信息设置给登录对象loginUserloginUser.setAccount(account);loginUser.setPassword(password);loginUser.setIp(this.tcpSocket.getIp());loginUser.setPort(String.valueOf(this.receiveSocket.getPort()));             // 调用receiveSocket中的getPort方法获得该终端的port值TcpMessage sMessage = new TcpMessage();                   // 实例化sMessage发送信息对象sMessage.setHead(CommonUse.LOGIN);                        // 将sMessage报文头设为登录sMessage.setBody(CommonUse.QQ_USER, loginUser);           // 将sMessage报文体传入登录的用户loginUserTcpMessage rMessage = this.tcpSocket.submit(sMessage);    // 调用tcpSocket里的submit方法就是将sMessage传过去服务器并等待处理完成然后接受返回的rMessage值String back = rMessage.getHead();                         // 获得传回的rMessage报文头赋值给back// 返回的报文头是成功时if(CommonUse.SUCCESSFUL.equals(back)) {JOptionPane.showMessageDialog(this, "登录成功");Qquser fullUser = (Qquser)rMessage.getBody(CommonUse.QQ_USER);      // 获得传回的rMessage里的报文体也就是用户的全部信息fullUser对象MainFrame mainFrame = new MainFrame(fullUser, this.tcpSocket, this.receiveSocket);      // 实例化MainFrame用户列表界面,并将用户全部信息fullUser对象以及fullUser传入// 如果不传入这个socket的话那该用户的下个界面将不能继续该用户的通讯mainFrame.setVisible(true); this.dispose();                 // 关闭登录页面} // 返回的报文头是失败时else if(CommonUse.FAILURE.equals(back)) {JOptionPane.showMessageDialog(this, "登录失败,用户名或者密码有误");}}// 点击的是注册按钮时执行的操作else if(e.getSource() == this.registerButton){RegisterFrame registerFrame = new RegisterFrame();registerFrame.setBounds(700, 320, 512, 390);registerFrame.setVisible(true);}}
}

让我们来看下完善后的loginframe类的结果,以 用户名为test、昵称为testman、密码为123456 的用户为例

LoginFrame登录页面 结果展示

数据库中 用户名为test、昵称为testman、密码为123456 的用户信息截图:

登录界面中输入 用户名为test、昵称为testman、密码为123456 的用户查看结果(记得要先运行Server类服务器哦,不然运行登录页面时TcpSocket就不知道该发给谁了):

登录成功:

数据库信息也已被修改为上线:

ServerThread类(线程操作类的注册功能代码部分)

接下来我们来完成注册页面功能代码的完善,还是跟登录一样,我们需要完善报文头为REGISTER(注册功能)相关的操作

注意!这里只展示ServerThread类中注册功能会用到的代码,所以下面代码里没有之前写的登录功能所用的代码,但是ServerThread类实际代码里还是会有登录功能所用的代码

package com.qq.server;// 供Server服务器实例化多线程终端时的类,每有一个用户登录,Server就会实例化一个ServerThread线程类,然后实例化时参数传入通讯的socket实现网络传输
// 然后ServerThread里的socket会传递信息(报文)给每个页面,然后每个页面根据传输的报文进行判断然后执行操作import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.io.OutputStreamWriter;
import java.net.Socket;
import java.util.List;import com.qq.bean.Qquser;
import com.qq.dao.IQqUserDao;
import com.qq.dao.QqUserDaoImpl;
import com.qq.pub.CommonUse;
import com.qq.pub.TcpMessage;
import com.qq.pub.UdpSocket;public class ServerThread extends Thread {private Qquser fullUser = null;                      // 接收登录用户完整信息的类private Socket socket = null;private ObjectOutputStream out = null;private ObjectInputStream in = null;// registe用户注册方法调用IQqUserDao的save注册功能public boolean registe(Qquser qquser) {IQqUserDao dao = new QqUserDaoImpl();int n = dao.save(qquser);if(n > 0) {return true;}return false;}// 重写构造方法public ServerThread() {// TODO Auto-generated constructor stub}public ServerThread(Socket socket) {System.out.println("有一个客户登录了");this.socket = socket;try {// 跟传入的socket对象进行对象流传输this.in = new ObjectInputStream(this.socket.getInputStream());this.out = new ObjectOutputStream(this.socket.getOutputStream());} catch (IOException e) {// TODO Auto-generated catch blocke.printStackTrace();}}// 重写线程的run方法@Overridepublic void run() {TcpMessage rMessage = null;try {while(true) {rMessage = (TcpMessage)this.in.readObject();String head = rMessage.getHead();                    // 获得读取的信息rMessage报文头// 如果获取的报文头是注册时if(CommonUse.REGISTER.equals(head)) {TcpMessage sMessage = new TcpMessage();          // 实例化进行发送信息对象sMessageQquser registeUser = (Qquser)rMessage.getBody(CommonUse.QQ_USER);        // 获得报文体给registeUser对象// 调用注册方法传入要注册的registeUser对象if(this.registe(registeUser)) {sMessage.setHead(CommonUse.SUCCESSFUL);      // 注册成功了就将成功传给发送信息对象sMessage} else {sMessage.setHead(CommonUse.FAILURE);         // 注册失败了就将失败传给发送信息对象sMessage}this.out.writeObject(sMessage);                  // 用对象流的书写方法将sMessage传输过去this.out.flush();}            }} catch (ClassNotFoundException e) {// TODO Auto-generated catch blocke.printStackTrace();} catch (IOException e) {// 当有一个客户端关闭时System.out.println("有一个客户下线了");}}
}

RegisterFrame类(注册界面代码完善)

最后就剩完善注册窗体RegisterFrame的代码实现点击按钮完成注册功能以及重置输入框功能

注意!其中已经涉及到了头像添加的问题,下一篇文章中我会专门说头像添加功能,上下线时头像变亮变灰是如何实现的,这里因为文章篇幅过长就不谈了,大家敬请期待我的下一篇文章,里面会有详细介绍

package com.qq.ui;// 用户进行注册的页面
// 3.0是通过封装服务器通讯代码到TcpSocket隐藏代码思路,在此类中只需要通过TcpSocket的构造方法实例化或者调用其中的方法import java.awt.BorderLayout;
import java.awt.Dimension;
import java.awt.HeadlessException;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.io.File;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.net.Socket;
import java.net.UnknownHostException;import javax.swing.Box;
import javax.swing.ImageIcon;
import javax.swing.JButton;
import javax.swing.JComboBox;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JOptionPane;
import javax.swing.JPanel;
import javax.swing.JPasswordField;
import javax.swing.JTextField;import com.qq.bean.Qquser;
import com.qq.component.ImgPanel;
import com.qq.pub.CommonUse;
import com.qq.pub.TcpMessage;
import com.qq.pub.TcpSocket;public class RegisterFrame extends JFrame implements ActionListener {private TcpSocket tcpSocket = null;private JPanel bodyPanel = null;private JPanel centerPanel = null;private JPanel bottomPanel = null;private JLabel accountLabel = null;private JLabel nameLabel = null;private JLabel passwordLabel = null;private JLabel repasswordLabel = null;private JLabel imgLabel = null;private JTextField accountTextField = null;private JTextField nameTextField = null;private JPasswordField passwordField = null;private JPasswordField repasswordField = null;private JComboBox imgComboBox = null;private JButton registerButton = null;private JButton cancelButton = null;// 自动添加头像方法private void addHead() {File[] files = new File("./selectimg").listFiles();       // 使用File的listFiles()方法获得该文件夹下的所有文件for (File file : files) {            // 遍历集合this.imgComboBox.addItem(new ImageIcon(file.getAbsolutePath()));        // 将每个图片文件的绝对定位通过getAbsolutePath()方法传入ImageIcon实例化并addItem给头像选择下拉列表}}// 中间输入信息界面的代码private void initCenter() {// 通过ImgPanel的构造方法实例化panel背景this.centerPanel = new ImgPanel("./register.png");this.accountLabel = new JLabel("账         号:", JLabel.RIGHT);this.accountLabel.setPreferredSize(new Dimension(70, 24));       // .setPreferredSize(new Dimension(宽, 高))    设置panel大小方法但是会随着界面大小变化而变化this.nameLabel = new JLabel("昵         称:", JLabel.RIGHT);this.nameLabel.setPreferredSize(new Dimension(70, 24));this.passwordLabel = new JLabel("密         码:", JLabel.RIGHT);this.passwordLabel.setPreferredSize(new Dimension(70, 24));this.repasswordLabel = new JLabel("确认密码:", JLabel.RIGHT);this.repasswordLabel.setPreferredSize(new Dimension(70, 24));this.imgLabel = new JLabel("选择头像:", JLabel.RIGHT);this.imgLabel.setPreferredSize(new Dimension(70, 24));this.accountTextField = new JTextField();this.accountTextField.setPreferredSize(new Dimension(160, 24));this.nameTextField = new JTextField();this.nameTextField.setPreferredSize(new Dimension(160, 24));this.passwordField = new JPasswordField();this.passwordField.setPreferredSize(new Dimension(160, 24));this.repasswordField = new JPasswordField();this.repasswordField.setPreferredSize(new Dimension(160, 24));this.imgComboBox = new JComboBox();this.addHead();                 // addHead()方法是上面写的自动添加头像方法// Box布局管理器,需要五个横着panel时需要声明一个大的竖着的box包裹住五个横着的boxBox box0 = Box.createVerticalBox();              // .createVerticalBox()创建竖着的box方法Box box1 = Box.createHorizontalBox();            // .createHorizontalBox()创建横着的box方法Box box2 = Box.createHorizontalBox();Box box3 = Box.createHorizontalBox();Box box4 = Box.createHorizontalBox();Box box5 = Box.createHorizontalBox();box1.add(this.accountLabel);box1.add(this.accountTextField);box2.add(this.nameLabel);box2.add(this.nameTextField);box3.add(this.passwordLabel);box3.add(this.passwordField);box4.add(this.repasswordLabel);box4.add(this.repasswordField);box5.add(this.imgLabel);box5.add(this.imgComboBox);box0.add(Box.createVerticalStrut(90));           // .createVerticalStrut(间距)设置和上一个box的间距box0.add(box1);box0.add(Box.createVerticalStrut(10));box0.add(box2);box0.add(Box.createVerticalStrut(10));box0.add(box3);box0.add(Box.createVerticalStrut(10));box0.add(box4);box0.add(Box.createVerticalStrut(10));box0.add(box5);this.centerPanel.add(box0);}// 下方按钮panel的代码private void initBottom() {this.bottomPanel = new JPanel();this.registerButton = new JButton("注 册");this.registerButton.addActionListener(this);this.cancelButton = new JButton("重 置");this.cancelButton.addActionListener(this);this.bottomPanel.add(this.registerButton);this.bottomPanel.add(this.cancelButton);}// 生成窗体的方法private void init() {this.bodyPanel = (JPanel)this.getContentPane();this.bodyPanel.setLayout(new BorderLayout());this.initCenter();this.bodyPanel.add(this.centerPanel, BorderLayout.CENTER);this.initBottom();this.bodyPanel.add(this.bottomPanel, BorderLayout.SOUTH);this.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);this.setTitle("QQ注册页面");}// 重写构造方法用TcpSocket构造方法实例化输入ip地址和port值完成与服务器连接public RegisterFrame() {this.tcpSocket = new TcpSocket("127.0.0.1", CommonUse.SERVER_PORT);this.init();}// 事件处理代码@Overridepublic void actionPerformed(ActionEvent e) {// 如果点的是注册按钮if(e.getSource() == this.registerButton) {Qquser qquser = new Qquser();String account = this.accountTextField.getText();String name = this.nameTextField.getText();String password = new String(this.passwordField.getPassword());String repassword = new String(this.repasswordField.getPassword());// 获得头像图片的名字方法:先用lastIndexOf方法获得\的位置再获得.的位置然后再用substring方法取他们之间的头像图片的名字String head = this.imgComboBox.getSelectedItem().toString();int start = head.lastIndexOf("\\");int end = head.lastIndexOf(".");String headString = head.substring(start + 1, end);if(password.equals(repassword)) {// 给对象赋值qquser.setAccount(account);qquser.setName(name);qquser.setPassword(password);qquser.setState("0");qquser.setIp("0");qquser.setPort("0");qquser.setPic(headString);// 实例化报文类设置头和体,头为REGISTER,体为刚获得输入的qquser对象TcpMessage sMessage = new TcpMessage();sMessage.setHead(CommonUse.REGISTER);sMessage.setBody(CommonUse.QQ_USER, qquser);// 调用tcpSocket的submit方法将sMessage发送并接收返回的rMessageTcpMessage rMessage = this.tcpSocket.submit(sMessage);// 并通过报文头的内容判断是否注册成功String back = rMessage.getHead();if(CommonUse.SUCCESSFUL.equals(back)) {JOptionPane.showMessageDialog(this, "注册成功");this.dispose();} else {JOptionPane.showMessageDialog(this, "注册失败");}} else {JOptionPane.showMessageDialog(this, "两次密码输入不一致");}}// 如果点的是重置按钮else if(e.getSource() == this.cancelButton){this.accountTextField.setText("");this.nameTextField.setText("");this.passwordField.setText("");this.repasswordField.setText("");}}
}

RegisterFrame注册页面 结果展示

我们在注册界面输入 账号为888888、昵称为wyh、密码为666666、头像随便选一个 来注册一个新用户(还是要记得先运行Server类服务器):

注册成功:

数据库中信息也保存成功:

学习Java面试题(并发编程相关知识)

指路陈哈哈大佬的并发编程相关面试题原帖

照片分享

作者:TL   作品名:Cbd  出自500px社区
2021.08.03  by wyh

复习Java第二个项目仿QQ聊天系统 03(两种通信类、登录以及注册功能完善) Java面试题并发编程相关知识生活【记录一个咸鱼大学生三个月的奋进生活】025相关推荐

  1. 复习Java第二个项目仿QQ聊天系统 01(界面部分) Java面试题Redis的过期策略和内存淘汰策略生活【记录一个咸鱼大学生三个月的奋进生活】023

    记录一个咸鱼大学生三个月的奋进生活023 复习Java(仿QQ聊天系统01界面部分) 设置背景(ImgPanel)类 登录界面(LoginFrame)类 注册界面(RegisterFrame)类 好友 ...

  2. 复习JavaWeb的小项目书籍信息的增删改查分页功能实现Java面试题Session和Cookie的基础概念生活【记录一个咸鱼大学生三个月的奋进生活】034

    记录一个咸鱼大学生三个月的奋进生活034 JavaWeb的增删改查分页功能实现 前期准备工作(数据库连接类和实体类) 数据库建立 数据库连接类(DBManager) 书籍信息的实体类(Book) 操作 ...

  3. 复习Java第一个项目学生信息管理系统 04(权限管理和动态挂菜单功能) python简单爬数据实例Java面试题三次握手和四次挥手生活【记录一个咸鱼大学生三个月的奋进生活】016

    记录一个咸鱼大学生三个月的奋进生活016 复习Java(学生信息管理系统04权限管理和动态挂菜单功能) 改写MainFrame的构造方法 新增LoginFrame的验证登录是否成功的代码 新增Logi ...

  4. 复习Java第一个项目学生信息管理系统 01(界面部分) Java面试题抽象类和接口生活【记录一个咸鱼大学生三个月的奋进生活】013

    记录一个咸鱼大学生三个月的奋进生活013 复习Java(学生信息管理系统01界面部分) 设置背景(WelcomePanel)类 登录界面(LoginFrame)类 主界面(MainFrame)类 学习 ...

  5. 复习Java.Lang包Java面试题Vector、ArrayList、LinkedList区别生活【记录一个咸鱼大学生三个月的奋进生活】007

    记录一个咸鱼大学生三个月的奋进生活007 复习Java(Lang包) 数据包装类 字符类Character的常用方法 字符串类(String) StringBuffer类的常用方法 Math类的常用方 ...

  6. 复习Java类与对象Java字符串面试题生活【记录一个咸鱼大学生三个月的奋进生活】003

    记录一个咸鱼大学生三个月的奋进生活003 复习Java(类与对象) 学习Java面试题 初步规划软件测试课设 健身 照片分享 复习Java(类与对象) 既然要说类与对象那就要明确 面向对象 这个概念: ...

  7. 复习Java小球游戏代码分享Java面试题MySQL中常用的锁生活【记录一个咸鱼大学生三个月的奋进生活】021

    记录一个咸鱼大学生三个月的奋进生活021 复习Java小球游戏 游戏界面的代码 小球运动线程的代码 运行游戏的代码 运行结果 代码分享 学习Java面试题(MySQL中常用的锁) 照片分享 复习Jav ...

  8. 复习Java入门与基础语法生活【记录一个咸鱼大学生三个月的奋进生活】002

    复习Java(入门) 既然是入门那就必须讲个故事了:   其实Java是James Gosling为了跨平台而创造的,当时(1991年)SUN公司想在智能家电中大展宏图,结果发现想完成智能家电用C是不 ...

  9. android 横向头像栏,GitHub - liushiqi0112/android-headimage-cliper: 头像上传图片裁剪,实现仿QQ、微信两种效果...

    Android头像上传之图片裁剪,实现仿QQ.微信两种效果 功能描述: 头像上传在APP中是很常见的功能,但是关于头像上传前的图片裁剪方式,如果使用系统提供的裁剪方式达不到预期效果,所以在这里提供一个 ...

最新文章

  1. C#实现字符串左旋转操作
  2. 魅影远程监控(企业版)
  3. 公众号管理模块-DouPHP模块化企业网站管理系统v1.6
  4. python实现爬虫下载美女图片
  5. Nand Flash数据存储单元的整体架构
  6. 虚拟机中Ubuntu IP地址漂移问题解决(为Ubuntu配置固定IP地址)
  7. Java 通过Executors创建线程池的种类
  8. 计算机网络基础(二)
  9. 急!Mac无法修复磁盘,应该怎么办? 看 这 里!
  10. 杂项-协议-HTTP:GET/POST/PUT/DELETE/INPUT/TRACE/OPTIONS/HEAD方法
  11. php添加pdo_mysql_php下添加pdo_mysql扩展
  12. label怎么换行 vb_C#与VB.NET换行符的对比及某些string在label能正常换行,在textbox不能换行的问题...
  13. SaaSpace:11种最佳免费会计软件工具
  14. sublime快捷键 1
  15. 阿里巴巴达摩院:自然语言处理技术有哪些进展和趋势?
  16. 旷世face++人脸识别实名鉴权商汤科技ocr二要素哪家强
  17. Linux Make(Makefile)由浅入深的学习与示例剖析
  18. emby,jellyfin,kodi系列
  19. 用最简单的方式来理解在单片机中的继电器的原理与接线
  20. 《魔兽世界》最大私服上线:玩家被国内工作室广告刷吐了

热门文章

  1. 邮箱,ip,叠词的正则处理方式
  2. XLRDError: Excel xlsx file; not supported 报错解决方法
  3. 【硬着头皮啃C++ Primer】第1章 开始
  4. tp6属性绑定到父模型相关知识点
  5. Elitebook735安装ubuntu18
  6. 探索Apache Camel Core – Seda组件
  7. CRM 开发 - 权限组件/stark组件/CRM业务
  8. 图解Linux命令之--insmod命令
  9. wkwebview 文件服务器,wkwebview开发常见问题(wkwebview加载本地沙盒文件)
  10. etcd 源码分析之unstable