< JAVA

< JAVA - 大作业(2)仿qq即时通讯软件 >

背景

  • JAVA上机大作业:设计一个仿qq即时通讯软件

任务简要叙述:设计一款仿QQ的个人用户即时通讯软件,能够实现注册,登陆,与好友聊天等功能。要求使用GUI界面设计,网络通信,数据库连接,泛型容器等技术。

  • 注意:
    • 参考该代码时注意修改Config.java中的IP地址为自己(服务器工程)的IP地址

    • 参考该代码时注意修改DBManage.java中的数据库连接内容为自己的数据库连接

    • 客户端运行Login.java启动客户端;服务器运行Start.java启动服务器

    • 客户端/服务端都有相应需要导入的额外包,可以在如下网站搜索需要的jar包:MvnJar

    • 阿里大于发送短信验证码需要自己去注册账户填写自己的AK;163邮箱同理需要对应修改

需求分析

  • 客户端:

    1. 注册账户:使用手机或者email注册,要求使用验证码验证手机号或者email再注册。
    2. 找回密码:使用手机/email找回密码,要求使用验证码验证是否本人操作再找回
    3. 登陆账户:使用系统自动生成的qq号与自己设置的密码进行登陆,能够记住密码与自动登陆,设置登陆状态,保留多账号登陆,二维码登陆的接口。
    4. 主界面:好友列表/群组列表/常用联系人,可以被抢占下线
    5. 聊天框:与好友聊天,字体设置,抖动好友,发送文件
    6. 个人资料:查看修改个人资料
    7. 搜索框:搜索QQ号,添加好友
  • 服务器:

    • 多线程,能承载大容量用户体积。
      通信消息及时更新,账号只许唯一登陆。

概要设计

架构/业务流程图

数据库 - 数据表

用户表:
Uid			varchar(100)	key		用户编号
Qqnumber	varchar(100)	唯一索引		qq号
Password 	varchar(100)
Netname 	varchar(100)
Info 		varchar(200)
State		varchar(200)		//账户是否锁定
Createtime	datetime
Img			varchar(100)
onlinestate	varchar(100)		//在线状态 – 离线/隐身/在线….

个人资料表:
Uid				varchar(100)
Network			varchar(100)
Info				varchar(200)
Phonenumber		varchar(100)
Email				varchar(100)
Yy					int
Mm					int
Dd					int
Back(个人说明)	varchar(500)
Gend				varchar(10)
Realname			varchar(100)
Profession		varchar(100)
Hometown			varchar(100)
Relation			varchar(100)
Bloodtype		varchar(10)
Img				varchar(100)
Qqnumber			varchar(100)

好友表
Logid(登记编号)	varchar(100)	key
Uid		普通索引
Friendid
Createtime

文件目录

客户端工程文件目录

服务器端工程文件目录

前端界面一览

客户端实现

com.qq.view/Login.java - 登陆界面

package com.qq.view;

import java.awt.*;
import java.awt.event.*;
import java.io.BufferedWriter;
import java.io.File;
import java.io.FileWriter;
import java.io.IOException;
import java.net.URI;
import java.net.URISyntaxException;
import java.net.UnknownHostException;

import javax.swing.*;
import javax.swing.border.LineBorder;

import com.qq.view.server.NetService;
import com.qq.view.util.Config;

import net.sf.json.JSONObject;

public class Login extends JFrame implements ActionListener,ItemListener{

	//边框布局
	private BorderLayout bLayout;
	
	//南北中西面板容器
	private JPanel PanelNorth;
	private JPanel PanelWest;
	private JPanel PanelSouth;
	private JPanel PanelCenter;
	
	//南面变量
	private JButton jble,loginButton,jbri;		//多账号登陆、登陆、二维码登陆
	
	//北面变量
	private JButton jbnc,jbnm,jbnn;		//右上角三个按钮
	
	//中面变量
	private JButton jbu,jl1,jl2;		//小键盘、注册账号、找回密码
	private JComboBox username;		//账号栏
	private JPasswordField password;	//密码栏
	private JCheckBox jch1,jch2;		//记住密码、自动登陆
	
	//西面变量
	private JButton jcoc;	//登陆状态	//到时候要改成JComboBox形式
	private JPanel jpin;	//重绘需要
	private Image qqhead;

	
	public Login() {		//构造器
		
		initBG();
		initUI();
		addBtnListener();
		initListener();
		
		this.setTitle("qq登陆界面");
		this.setSize(380,294);
		this.setLocationRelativeTo(null);	//位置于正中间
		this.setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE);		//退出只退出当前窗口
		this.setResizable(false);	//设置不能调整窗口大小
		this.setUndecorated(true);	//消除窗体边框
		this.setVisible(true);	//设置窗体可见
		
		Graphics g=jpin.getGraphics(); 	//重绘,窗体可见之后取画布
	}
	 
	
	private void saveFile() {		// 登陆成功后保存下次登陆信息

	    try{
	 
	        String username_str = username.getEditor().getItem().toString().trim();	//获取下拉列表编辑的值(包括选择值)
			String password_str = password.getText().trim();
			String remamber = jch1.isSelected()+"";		// 保存设置
			String auto = jch2.isSelected()+"";
			JSONObject jsonObject = JSONObject.fromObject(Config.personality_json_data);	// 保存img
			String img_str = new String();
			if (jsonObject.getString("img").equals("无")) {
				img_str = "resources//online//0.png";
			}
			else {
				img_str = "resources//online//"+jsonObject.getString("img")+".png";
			}
			String content = username_str+","+password_str+","+remamber+","+auto+","+img_str;
	        File file =new File("resources//login.txt");
	 
	        if(!file.exists()){
	        	file.createNewFile();
	        }
	 
	        FileWriter fileWritter = new FileWriter(file.getName(),true);
	        BufferedWriter bufferWritter = new BufferedWriter(fileWritter);
	        bufferWritter.write(content);
	        bufferWritter.close();
	        fileWritter.close();
	 
	    }catch(IOException e){
	        e.printStackTrace();
	    }
	}

	private void initUI() {
		// TODO Auto-generated method stub
		
		bLayout = new BorderLayout();
		this.setLayout(bLayout);
		
		PanelNorth = new JPanel();
		PanelSouth = new JPanel();
		PanelWest = new JPanel();
		PanelCenter = new JPanel();
		
		//北面面板设计
		PanelNorth.setLayout(null);
		PanelNorth.setPreferredSize(new Dimension(0,140));	//设置面板大小
		PanelNorth.setOpaque(false);
			//退出按钮
		jbnc = new JButton(new ImageIcon("resources//login//btn_close_normal.png"));  
		jbnc.setBounds(342,-1,39,20);  //x,y,width,high
		jbnc.setRolloverIcon(new ImageIcon("resources//login//btn_close_highlight.png"));  //鼠标放上去
		jbnc.setPressedIcon(new ImageIcon("resources//login//btn_close_down.png"));  	//鼠标按压
		jbnc.setBorderPainted(false);  	//消除边框
		jbnc.setFocusPainted(false);  	//取消文本强调
		jbnc.setContentAreaFilled(false);	//想要按钮透明必须还得这样去除默认填充
		jbnc.setToolTipText("关闭");  	//鼠标放上去的提示语
		PanelNorth.add(jbnc); 
			//最小化按钮
		jbnm = new JButton(new ImageIcon("resources//login//btn_mini_normal.png"));
		jbnm.setBounds(315,-1,28,20);
		jbnm.setRolloverIcon(new ImageIcon("resources//login//btn_mini_highlight.png"));
		jbnm.setPressedIcon(new ImageIcon("resources//login//btn_mini_down.png"));
		jbnm.setBorderPainted(false);
		jbnm.setFocusPainted(false);
		jbnm.setContentAreaFilled(false);	
		jbnm.setToolTipText("最小化");
		PanelNorth.add(jbnm);
			//设置
		jbnn = new JButton(new ImageIcon("resources//login//btn_set_normal.png"));  
		jbnn.setBounds(288,-1,28,20);  
		jbnn.setRolloverIcon(new ImageIcon("resources//login//btn_set_hover.png"));  
		jbnn.setPressedIcon(new ImageIcon("resources//login//btn_set_press.png"));  
		jbnn.setBorderPainted(false);  
		jbnn.setFocusPainted(false);  
		jbnn.setContentAreaFilled(false);
		jbnn.setToolTipText("设置");  
		PanelNorth.add(jbnn);  
		
		//南面面板设计
		PanelSouth.setOpaque(false);		//将面板透明,里面内容不透明,这样可以把背景显现 
		PanelSouth.setPreferredSize(new Dimension(0,51));  
        PanelSouth.setBorder(null); 
        PanelSouth.setLayout(null); 
        	//设置多账号登陆
        jble = new JButton(new ImageIcon("resources//login//corner_left.png"));  
        jble.setPreferredSize(new Dimension(40,40));  
        jble.setFocusPainted(false);  
        jble.setRolloverIcon(new ImageIcon("resources//login//corner_left_hover.png"));  
        jble.setPressedIcon(new ImageIcon("resources//login//corner_left_press.png"));  
        jble.setBorderPainted(false);  
        jble.setContentAreaFilled(false);  
        jble.setBounds(0,11,40,40);  
//        JToolTip jtl = new JToolTip();  ???
//        jtl.setOpaque(false);  
//        jtl.setBackground(Color.WHITE);  
        jble.setToolTipText("多账号登录");  
        	//设置登陆按钮  
        ImageIcon logimg = new ImageIcon("resources//login//button_login_normal.png");  
        loginButton = new JButton("登 录",logimg);  
        loginButton.setFont(new Font("宋体",Font.BOLD,14));  	//粗体
        loginButton.setForeground(Color.white);
        loginButton.setHorizontalTextPosition(SwingConstants.CENTER);//将文字放在图片中间  
        loginButton.setFocusPainted(false);//设置点击不出现边框  
        loginButton.setContentAreaFilled(false); //设置透明
        loginButton.setRolloverIcon(new ImageIcon("resources//login//button_login_hover.png"));  
        loginButton.setPressedIcon(new ImageIcon("resources//login//button_login_down.png"));  //设置选择图标  
        loginButton.setBorderPainted(false);  //是否画边框  
        loginButton.setBounds(113,8,162,38);  	//设置位置很关键
//        	//设置右边按钮  
        jbri = new JButton(new ImageIcon("resources//login//corner_right_normal.png"));  
        jbri.setFocusPainted(false);  
        jbri.setRolloverIcon(new ImageIcon("resources//login//corner_right_hover.png"));  
        jbri.setPressedIcon(new ImageIcon("resources//login//corner_right_normal_down.png"));  
        jbri.setBorderPainted(false);  
        jbri.setContentAreaFilled(false);  
        jbri.setBounds(330,4,45,38);  
        jbri.setToolTipText("二维码登录");  
        loginButton.setBorder(BorderFactory.createLoweredBevelBorder());  	//设置下凹  
        PanelSouth.add(jble);  	//将按钮对象添加到面板上  
        PanelSouth.add(loginButton);  
        PanelSouth.add(jbri);         
        
		//西面面板设计
		PanelWest.setOpaque(false);
	    PanelWest.setPreferredSize(new Dimension(102,0));  	//设置西边面板容器的大小 
	    PanelWest.setLayout(new FlowLayout(FlowLayout.RIGHT)); 	//设置西边面板的布局方式为流式布局  
	    	//西面面板内容
	    ImageIcon imageWest = new ImageIcon("resources//login//qq.jpg");  	//
	    qqhead = imageWest.getImage(); 
	    jpin = new JPanel(){    		//qq头像 - 后续可以换成用户自己的头像
            public void paintComponent(Graphics g){    
               g.drawImage(qqhead, 0,0,this.getWidth(), this.getHeight(),null);  
            }    
        };
        jpin.setPreferredSize(new Dimension(82,82));
        jpin.setLayout(null);    
        jpin.setOpaque(false); 
        jcoc = new JButton(new ImageIcon("resources//login//Qme.png"));  
        jcoc.setBounds(64, 64, 18, 18);    
        jcoc.setFocusPainted(false);   
        jcoc.setOpaque(false);
        jcoc.setContentAreaFilled(false);
        jpin.add(jcoc); 
        PanelWest.add(jpin);  
		 
		//中面面板设计
		PanelCenter.setOpaque(false);
		PanelCenter.setLayout(null);  
        	//JcomboBox实现下拉框 
        String str []= {"624730725","251227228"};  
        username = new JComboBox(str);  		//用户名
        username.setEditable(true);  //设置下拉框可编辑  
        username.setBounds(7, 4, 185, 25);  
        username.setFont(new Font("Calibri ",0,13));  //设置默认字体
        PanelCenter.add(username);  
        	//实例化一个标签类的对象  
        jl1 = new JButton("注册账号");  		//到时候改button或者添加超链接 
        jl1.setFont(new Font("宋体",0,13));   
        jl1.setBounds(195,4,90,25);
        jl1.setForeground(Color.black);
        jl1.setFocusPainted(false); 
        PanelCenter.add(jl1);  
        	//实例化一个JLabel类的对象  				//到时候改button或者添加超链接
        jl2 = new JButton("找回密码");  
        jl2.setFont(new Font("宋体",0,12));  
        jl2.setForeground(Color.black);  
        jl2.setBounds(195, 38, 90, 25); 
        jl2.setFocusPainted(false); 
        PanelCenter.add(jl2);  
        	//实例化小键盘图标对象  
        ImageIcon keyboard = new ImageIcon("resources//login//keyboard.png");
        jbu = new JButton(keyboard);  
        jbu.setPreferredSize(new Dimension(22,20));  
        jbu.setBorderPainted(false);      
        	//实例化一个JPasswordField类的对象  
        password = new JPasswordField();  	//密码
        password.setLayout(new FlowLayout(FlowLayout.RIGHT,0,0));   
        LineBorder lin = new LineBorder(Color.WHITE,3,true);  
        password.setBorder(lin);  
        password.setBounds(7,38,185,23);  
        password.add(jbu);  
        password.setPreferredSize(new Dimension(185,25));  //设置大小  
        PanelCenter.add(password); 
        	//实例化两个JCheckBox类的对象  
        jch1 = new JCheckBox("记住密码");  
        jch1.setFocusPainted(false); //选中时没有边框  
        jch1.setFont(new Font("宋体",0,13));//字体  
        jch1.setBounds(2, 70, 78, 15);  
        PanelCenter.add(jch1);  
        jch2 = new JCheckBox("自动登录");  
        jch2.setFocusPainted(false);  
        jch2.setFont(new Font("宋体",0,12));  
        jch2.setBounds(80, 70, 78, 15);  
        PanelCenter.add(jch2);  
        	//设置复选框透明  
        jch1.setOpaque(false);  
        jch2.setOpaque(false);  
		
		this.add(PanelNorth,bLayout.NORTH);
		this.add(PanelSouth,bLayout.SOUTH);
		this.add(PanelWest,bLayout.WEST); 
		this.add(PanelCenter,bLayout.CENTER);
		
	}

	private void initBG() {	

		ImageIcon background = new ImageIcon("resources//login//qqlogin2.jpg");	//背景要放到JLabel里面才能显示 - //getClass().getResource("qqlogin2.jpg")放在该包下
		JLabel backgroundLabel = new JLabel(background);
		backgroundLabel.setBounds(0, 0, background.getIconWidth(),background.getIconHeight());	//设置标签显示的位置和大小
		this.getLayeredPane().add(backgroundLabel, new Integer(Integer.MIN_VALUE));		//放在jframe的layeredpane层
		JPanel contentPanel = (JPanel)this.getContentPane(); 			//将contentPane层设为透明,jframe直接add的部件都放在这一层
		contentPanel.setOpaque(false);		//将contentPane设置成透明 - 虽然没啥用,实际上因为有边界布局,所以得将布局上的JPanel设成透明才能看到背景
	}	
	
	public static void main(String[] args) {
		// TODO Auto-generated method stub
		
		JFrame.setDefaultLookAndFeelDecorated(true);		//swing框架皮肤
		JDialog.setDefaultLookAndFeelDecorated(true);
		try {
			UIManager.setLookAndFeel("javax.swing.plaf.nimbus.NimbusLookAndFeel");
		} catch (Exception e) {
			// TODO: handle exception
			e.printStackTrace();
		}
		
		Login login = new Login();
		
	}

	private void addBtnListener() {		//jble,jb,jbri;jbnc,jbnm,jbnn;jbu,jl1,jl2;
		jble.addActionListener(this);	//登陆
		loginButton.addActionListener(this);
		jbri.addActionListener(this);
		jbnc.addActionListener(this);	//右上三
		jbnm.addActionListener(this);
		jbnn.addActionListener(this);
		jbu.addActionListener(this);	//中间
		jl1.addActionListener(this);	//注册
		jl2.addActionListener(this);
	}
	
	private void initListener() {	//jcoCenter,jch1,jch2,jcoc
		
	}
	
	@Override
	public void itemStateChanged(ItemEvent e) {
		
	}
	
	@Override
	public void actionPerformed(ActionEvent e) {
		// TODO Auto-generated method stub
		if (e.getSource() == jbnc) {		//退出
			this.dispose();//销毁当前窗口
		}else if (e.getSource() == jbnm) { 
			this.setExtendedState(this.ICONIFIED);	//最小化
		}else if (e.getSource() == jbnn) {
			//设置界面
		}else if (e.getSource() == jbu) {
			//添加小键盘KeyListener
		}else if (e.getSource() == jl1) {		//弹出注册
			new register();
		}else if (e.getSource() == jl2) {
//			try {		//方法一
//			Runtime.getRuntime().exec( "cmd.exe /c start "+"www.baidu.com");	//执行cmd打开默认浏览器+跳转页面
//		} catch (IOException e1) {
//			// TODO Auto-generated catch block
//			e1.printStackTrace();
//		}
//			if (Desktop.isDesktopSupported()) {
//			      try {
//			        Desktop.getDesktop().browse(new URI("https://aq.qq.com/v2/uv_aq/html/reset_pwd/pc_reset_pwd_input_account.html?v=3.0&old_ver_account="));
//			      }
//			      catch (URISyntaxException | IOException ex) {	//首先检测是否有GUI桌面系统的存在,这样就不会在只有命令行的服务器系统下运行时也企图打开网页了
//			        ex.printStackTrace();
//			      }
//			    }
			new Recome();
		}else if (e.getSource() == loginButton) {		//登陆
			//用户名和密码
			String username_str = username.getEditor().getItem().toString().trim();	//获取下拉列表编辑的值(包括选择值)
			String password_str = password.getText().trim();
			if(username_str.trim().equals("")||password_str.trim().equals(""))
			{
				javax.swing.JOptionPane.showMessageDialog(Login.this, "用户名和密码必须填写!");
				return;
			}
			Config.username = username_str;
			Config.password = password_str;
			
			try {
				JSONObject json = NetService.getNetService().login();
				if(json.getInt("state") == 0) {
//					javax.swing.JOptionPane.showMessageDialog(Login.this, "登陆成功");
					new MainMenu();
					//记录登陆信息写入本地
					this.saveFile();
					
					this.dispose();
									
				}else {
					javax.swing.JOptionPane.showMessageDialog(Login.this, json.getString("msg"));
				} 
				
			} catch (Exception e1) {
				e1.printStackTrace();
				javax.swing.JOptionPane.showMessageDialog(Login.this, "网络连接失败!");
			}
		}
	
	}

}

com.qq.view/register.java - 注册界面

package com.qq.view;

import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.EventQueue;

import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.border.EmptyBorder;

import com.qq.view.server.NetService;
import com.qq.view.util.Config;

import net.sf.json.JSONObject;

import javax.swing.JLabel;
import java.awt.Font;
import javax.swing.JTextField;
import javax.swing.ImageIcon;
import javax.swing.JButton;
import javax.swing.JDialog;

import java.awt.event.ActionListener;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.Socket;
import java.awt.event.ActionEvent;
import javax.swing.SwingConstants;
import javax.swing.UIManager;

public class register extends JFrame implements ActionListener{

	private JPanel contentPane;
	private JTextField text_username;
	private JTextField text_password;
	private JTextField text_certain;
	private JTextField text_code;
	
	//需要做响应的组件最好都放在全局
	private JButton btn_send,btn_exit,btn_register;

	/**
	 * Launch the application.
	 */
	public static void main(String[] args) {
		JFrame.setDefaultLookAndFeelDecorated(true);		//swing框架皮肤
		JDialog.setDefaultLookAndFeelDecorated(true);
		try {
			UIManager.setLookAndFeel("javax.swing.plaf.nimbus.NimbusLookAndFeel");
		} catch (Exception e) {
			// TODO: handle exception
			e.printStackTrace();
		}
		
		EventQueue.invokeLater(new Runnable() {
			public void run() {
				try {
					register frame = new register();
					frame.setVisible(true);
				} catch (Exception e) {
					e.printStackTrace();
				}
			}
		});
	}

	/**
	 * Create the frame.
	 */
	public register() {
		setTitle("u6CE8u518Cu8D26u6237");
		setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE);
		setBounds(100, 100, 350, 425);
		setLocationRelativeTo(null);	//正中间位置
		setVisible(true);		//所有jframe这里没有加
		setResizable(false);	//不可改变大小
		contentPane = new JPanel();
		contentPane.setBorder(new EmptyBorder(5, 5, 5, 5));
		contentPane.setLayout(new BorderLayout(0, 0));
		setContentPane(contentPane);
		
		initBG();	//设置背景
		
		JPanel panelC = new JPanel();
		contentPane.add(panelC, BorderLayout.CENTER);
		panelC.setLayout(null);
		panelC.setOpaque(false);
		
		JLabel lab_username = new JLabel("u624Bu673A/Email");
		lab_username.setFont(new Font("宋体", Font.BOLD, 14));
		lab_username.setBounds(20, 30, 85, 35);
//		lab_username.setOpaque(true);  //此句是重点,设置背景颜色必须先将它设置为不透明的,因为默认是透明的。。。
//		lab_username.setBackground(Color.white);
		panelC.add(lab_username);
		
		text_username = new JTextField();
		text_username.setBounds(105, 30, 175, 35);
		panelC.add(text_username);
		text_username.setColumns(10);
		
		JLabel lab_password = new JLabel(" u8F93u5165u5BC6u7801");
		lab_password.setFont(new Font("宋体", Font.BOLD, 14));
		lab_password.setBounds(20, 100, 85, 35);
		panelC.add(lab_password);
		
		text_password = new JTextField();
		text_password.setColumns(10);
		text_password.setBounds(105, 100, 175, 35);
		panelC.add(text_password);
		
		JLabel lab_certain = new JLabel(" u786Eu5B9Au5BC6u7801");
		lab_certain.setFont(new Font("宋体", Font.BOLD, 14));
		lab_certain.setBounds(20, 170, 85, 35);
		panelC.add(lab_certain);
		
		text_certain = new JTextField();
		text_certain.setColumns(10);
		text_certain.setBounds(105, 170, 175, 35);
		panelC.add(text_certain);
		
		JLabel lab_code = new JLabel("u9A8C u8BC1 u7801");
		lab_code.setHorizontalAlignment(SwingConstants.CENTER);
		lab_code.setFont(new Font("宋体", Font.BOLD, 14));
		lab_code.setBounds(20, 239, 85, 35);
		panelC.add(lab_code);
		
		text_code = new JTextField();
		text_code.setColumns(10);
		text_code.setBounds(105, 239, 77, 35);
		panelC.add(text_code);
		
		btn_send = new JButton("u53D1u9001u9A8Cu8BC1");
		btn_send.setBounds(183, 296, 97, 23);
		panelC.add(btn_send);
		
		JPanel panelS = new JPanel();
		panelS.setPreferredSize(new Dimension(0, 50));
		contentPane.add(panelS, BorderLayout.SOUTH);
		panelS.setLayout(null);
		panelS.setOpaque(false);
		
		btn_exit = new JButton("u9000u51FA");
		btn_exit.setBounds(15, 15, 100, 25);
		panelS.add(btn_exit);
		
		btn_register = new JButton("u6CE8u518Cu8D26u6237");
		btn_register.setBounds(210, 15, 100, 25);
		panelS.add(btn_register);
		
		addBtnListener();
	}

	private void addBtnListener() {
		
		btn_exit.addActionListener(this);
		btn_register.addActionListener(this);
		btn_send.addActionListener(this);
		
	}
	
	@Override
	public void actionPerformed(ActionEvent e) {

		if (e.getSource() == btn_exit) {
			this.dispose();
		}else if (e.getSource() == btn_register) {
			try {
				
				String username_str = text_username.getText().trim();	//得到用户名
				String password = text_password.getText().trim();
				String certain = text_certain.getText().trim();
				String code = text_code.getText().trim();
				if(username_str.trim().equals("")) 
				{
					javax.swing.JOptionPane.showMessageDialog(register.this, "手机/email必须填写!");
					return;
				}
				if(password.trim().equals("")) 
				{
					javax.swing.JOptionPane.showMessageDialog(register.this, "密码必须填写!");
					return;
				}
				if(certain.trim().equals("")) 
				{
					javax.swing.JOptionPane.showMessageDialog(register.this, "密码确认必须填写!");
					return;
				}
				if(code.trim().equals("")) 
				{
					javax.swing.JOptionPane.showMessageDialog(register.this, "验证码必须填写!");
					return;
				}
				if(!password.trim().equals(certain)) 
				{
					javax.swing.JOptionPane.showMessageDialog(register.this, "密码确认必须与密码一致!");
					return;
				}
				
				Socket socket = new Socket(Config.IP,Config.REG_PORT);
				InputStream input = socket.getInputStream();
				OutputStream output = socket.getOutputStream();
				
				output.write(("{"type":"reg","username":""+username_str+"","password":""+password+"","code":""+code+""}").getBytes());
				output.flush();
				
				byte[] bytes =new byte[1024];
				int len = input.read(bytes);
				String string = new String(bytes,0,len);
				JSONObject json = JSONObject.fromObject(string);
				
				if(json.getInt("state") == 0 ) {	//注册成功
					javax.swing.JOptionPane.showMessageDialog(register.this, ("恭喜您!注册成功!您的qq号码是:"+json.getString("qqnumber")));
					text_certain.setText("");
					text_code.setText("");
					text_password.setText("");
					text_username.setText("");
				}else if(json.getInt("state") ==2 ){
					javax.swing.JOptionPane.showMessageDialog(register.this, "注册失败,用户名已存在!");
				}else if(json.getInt("state") ==1 ){
					javax.swing.JOptionPane.showMessageDialog(register.this, "发送失败,验证码错误!");
				}else if(json.getInt("state") ==3 ){
					javax.swing.JOptionPane.showMessageDialog(register.this, "发送失败,未知错误!");
				}
					
				input.close();
				output.close();
				socket.close();
				
			} catch (Exception e2) {
				e2.printStackTrace();
			}
			
		}else if (e.getSource() == btn_send) { 
			//发送验证码
			try {
				String username_str = text_username.getText().trim();	//得到用户名
				if(username_str.trim().equals("")) 
				{
					javax.swing.JOptionPane.showMessageDialog(register.this, "手机/email必须填写!");
					return;
				}
				
				Socket socket = new Socket(Config.IP,Config.REG_PORT);
				InputStream input = socket.getInputStream();
				OutputStream output = socket.getOutputStream();
				
				output.write(("{"type":"code","username":""+username_str+""}").getBytes());
				output.flush();
				
				byte[] bytes =new byte[1024];
				int len = input.read(bytes);
				String string = new String(bytes,0,len);
				JSONObject json = JSONObject.fromObject(string);
				if(json.getInt("state") ==0 ) {	//发送成功
					javax.swing.JOptionPane.showMessageDialog(register.this, "发送成功!");
				}else {
					javax.swing.JOptionPane.showMessageDialog(register.this, "发送失败,你的手机/email填写失败!");
				}
				
				input.close();
				output.close();
				socket.close();
				
			} catch (Exception e2) {
				e2.printStackTrace();
			}
			
		}
		
	}
	
	private void initBG() {	

		ImageIcon background = new ImageIcon("resources//register//regbg.png");	
		JLabel labelbg = new JLabel(background);
		labelbg.setBounds(0, 0, background.getIconWidth(),background.getIconHeight());	//设置标签显示的位置和大小
		this.getLayeredPane().add(labelbg, new Integer(Integer.MIN_VALUE));		//放在jframe的layeredpane层
		JPanel contentPanel = (JPanel)this.getContentPane(); 			//将contentPane层设为透明,jframe直接add的部件都放在这一层
		contentPanel.setOpaque(false);		//将contentPane设置成透明 - 虽然没啥用,实际上因为有边界布局,所以得将布局上的JPanel设成透明才能看到背景
	}
	
}

com.qq.view/Recome.java - 找回密码界面

package com.qq.view;

import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.EventQueue;

import javax.swing.JDialog;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.UIManager;
import javax.swing.border.EmptyBorder;

import com.qq.view.util.Config;

import net.sf.json.JSONObject;

import javax.swing.JLabel;
import java.awt.Font;
import javax.swing.SwingConstants;
import javax.swing.JTextField;
import javax.swing.ImageIcon;
import javax.swing.JButton;
import java.awt.event.ActionListener;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.Socket;
import java.awt.event.ActionEvent;

public class Recome extends JFrame {

	private JPanel contentPane;
	private JTextField textField;
	private JTextField textField_1;
	private JTextField textField_2;

	/**
	 * Launch the application.
	 */
	public static void main(String[] args) {
		JFrame.setDefaultLookAndFeelDecorated(true);		//swing框架皮肤
		JDialog.setDefaultLookAndFeelDecorated(true);
		try {
			UIManager.setLookAndFeel("javax.swing.plaf.nimbus.NimbusLookAndFeel");
		} catch (Exception e) {
			// TODO: handle exception
			e.printStackTrace();
		}
		
		EventQueue.invokeLater(new Runnable() {
			public void run() {
				try {
					Recome frame = new Recome();
					frame.setVisible(true);
				} catch (Exception e) {
					e.printStackTrace();
				}
			}
		});
	}

	/**
	 * Create the frame.
	 */
	public Recome() {
		setTitle("u627Eu56DEu5BC6u7801");
		setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE);
		setBounds(100, 100, 400, 421);
		setLocationRelativeTo(null);	//正中间位置
		setVisible(true);		//所有jframe这里没有加
		setResizable(false);	//不可改变大小
		contentPane = new JPanel();
		contentPane.setBorder(new EmptyBorder(5, 5, 5, 5));
		setContentPane(contentPane);
		contentPane.setLayout(null);
		initBG();
		
		JPanel panel = new JPanel();
		panel.setBounds(0, 0, 400, 421);
		contentPane.add(panel);
		panel.setLayout(null);
		panel.setOpaque(false);
		
		JLabel lblqq = new JLabel("u8F93u5165 Q Q");
		lblqq.setBounds(43, 65, 85, 35);
		panel.add(lblqq);
		lblqq.setHorizontalAlignment(SwingConstants.CENTER);
		lblqq.setFont(new Font("宋体", Font.BOLD, 14));
		lblqq.setForeground(Color.black);
		
		JLabel lblnumber = new JLabel("u624Bu673A/Email");
		lblnumber.setBounds(43, 116, 85, 35);
		panel.add(lblnumber);
		lblnumber.setHorizontalAlignment(SwingConstants.CENTER);
		lblnumber.setFont(new Font("宋体", Font.BOLD, 14));
		lblnumber.setForeground(Color.black);
		
		JLabel lblcode = new JLabel("u9A8C u8BC1 u7801");
		lblcode.setBounds(43, 178, 85, 35);
		panel.add(lblcode);
		lblcode.setHorizontalAlignment(SwingConstants.CENTER);
		lblcode.setFont(new Font("宋体", Font.BOLD, 14));
		lblcode.setForeground(Color.black);
		
		textField = new JTextField();
		textField.setBounds(140, 65, 180, 35);
		panel.add(textField);
		textField.setColumns(10);
		
		textField_1 = new JTextField();
		textField_1.setBounds(140, 116, 180, 35);
		panel.add(textField_1);
		textField_1.setColumns(10);
		
		textField_2 = new JTextField();
		textField_2.setBounds(140, 178, 87, 35);
		panel.add(textField_2);
		textField_2.setColumns(10);
		
		JButton btnNewButton = new JButton("u53D1u9001u9A8Cu8BC1");
		btnNewButton.addActionListener(new ActionListener() {
			public void actionPerformed(ActionEvent e) {
				try {
					String username_str = lblnumber.getText().trim();	//得到用户名
					if(username_str.trim().equals("")) 
					{
						javax.swing.JOptionPane.showMessageDialog(Recome.this, "手机/email必须填写!");
						return;
					}
					
					Socket socket = new Socket(Config.IP,Config.REG_PORT);
					InputStream input = socket.getInputStream();
					OutputStream output = socket.getOutputStream();
					
					output.write(("{"type":"code","username":""+username_str+""}").getBytes());
					output.flush();
					
					byte[] bytes =new byte[1024];
					int len = input.read(bytes);
					String string = new String(bytes,0,len);
					JSONObject json = JSONObject.fromObject(string);
					if(json.getInt("state") ==0 ) {	//发送成功
						javax.swing.JOptionPane.showMessageDialog(Recome.this, "发送成功!");
					}else {
						javax.swing.JOptionPane.showMessageDialog(Recome.this, "发送失败,你的手机/email填写失败!");
					}
					
					input.close();
					output.close();
					socket.close();
					
				} catch (Exception e2) {
					e2.printStackTrace();
				}
			}
		});
		btnNewButton.setBounds(261, 238, 87, 30);
		panel.add(btnNewButton);
		
		JButton button = new JButton("u9000  u51FA");
		button.addActionListener(new ActionListener() {
			public void actionPerformed(ActionEvent e) {
				dispose();
			}
		});
		button.setBounds(41, 310, 87, 30);
		panel.add(button);
		
		JButton button_1 = new JButton("u627Eu56DEu5BC6u7801");
		button_1.addActionListener(new ActionListener() {
			public void actionPerformed(ActionEvent e) {
				
				try {
					
					String username = lblnumber.getText().trim();	//得到用户名 
					String qqnumber = lblqq.getText().trim();
					String code = lblcode.getText().trim();
					if(username.trim().equals("")) 
					{
						javax.swing.JOptionPane.showMessageDialog(Recome.this, "手机/email必须填写!");
						return;
					}
					if(qqnumber.trim().equals("")) 
					{
						javax.swing.JOptionPane.showMessageDialog(Recome.this, "QQ号码必须填写!");
						return;
					}
					if(code.trim().equals("")) 
					{
						javax.swing.JOptionPane.showMessageDialog(Recome.this, "验证码必须填写!");
						return;
					}
					
					Socket socket = new Socket(Config.IP,Config.REG_PORT);
					InputStream input = socket.getInputStream();
					OutputStream output = socket.getOutputStream();
					
					output.write(("{"type":"recome","username":""+username+"","qqnumber":""+qqnumber+"","code":""+code+""}").getBytes());
					output.flush();
					
					byte[] bytes =new byte[1024];
					int len = input.read(bytes);
					String string = new String(bytes,0,len);
					JSONObject json = JSONObject.fromObject(string);
					
					if(json.getInt("state") ==0 ) {	//找回成功
						javax.swing.JOptionPane.showMessageDialog(Recome.this, ("恭喜您!找回成功!您的账号密码是:"+json.getString("password")));
						lblcode.setText("");
						lblnumber.setText("");
						lblqq.setText("");
					}else if(json.getInt("state") ==2 ){
						javax.swing.JOptionPane.showMessageDialog(Recome.this, "注册失败,该QQ不存在或与绑定手机/Email不匹配!");
					}else if(json.getInt("state") ==1 ){
						javax.swing.JOptionPane.showMessageDialog(Recome.this, "发送失败,验证码错误!");
					}else if(json.getInt("state") ==3 ){
						javax.swing.JOptionPane.showMessageDialog(Recome.this, "发送失败,未知错误!");
					}
						
					input.close();
					output.close();
					socket.close();
					
				} catch (Exception e2) {
					e2.printStackTrace();
				}
				
			}
		});
		button_1.setBounds(261, 310, 87, 30);
		panel.add(button_1);
	}
	
	private void initBG() {	
		ImageIcon background = new ImageIcon("resources//register//regbg2.png");	
		JLabel labelbg = new JLabel(background);
		labelbg.setBounds(0, 0, background.getIconWidth(),background.getIconHeight());	//设置标签显示的位置和大小
		this.getLayeredPane().add(labelbg, new Integer(Integer.MIN_VALUE));		//放在jframe的layeredpane层
		JPanel contentPanel = (JPanel)this.getContentPane(); 			//将contentPane层设为透明,jframe直接add的部件都放在这一层
		contentPanel.setOpaque(false);		//将contentPane设置成透明 - 虽然没啥用,实际上因为有边界布局,所以得将布局上的JPanel设成透明才能看到背景
	}
	
}

com.qq.view/MainMenu.java - 主界面

package com.qq.view;

import java.awt.BorderLayout;
import java.awt.Dimension;
import java.awt.EventQueue;

import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.border.EmptyBorder;

import com.qq.view.util.Config;

import net.sf.json.JSONArray;
import net.sf.json.JSONObject;

import java.awt.FlowLayout;
import javax.swing.BoxLayout;
import javax.swing.ImageIcon;
import javax.swing.JButton;
import javax.swing.JDialog;

import java.awt.event.ActionListener;
import java.awt.event.ActionEvent;
import javax.swing.SwingConstants;
import javax.swing.UIManager;
import javax.swing.JLabel;
import java.awt.Font;
import javax.swing.JTabbedPane;
import javax.swing.JScrollBar;
import javax.swing.JScrollPane;

public class MainMenu extends JFrame{

	private JPanel contentPane;
	private JButton btn_head;
	private JLabel labelname;
	private JLabel labelinfo;
	boolean run = false;		//用来反馈顶号
	private Thread thread = null;
	
	public void mainUpdate() {		//更新自己主界面的上方显示
			//{"dd":"","mm":"","profession":"","yy":"","img":"无","gend":"男","phonenumber":"","back":"我有一丶丶想你",
			//"realname":"mm","relation":"单身","uid":"10002","bloodtype":"A","netname":"小龙人","email":"","info":"我是小龙人"}

		JSONObject jsonObject = JSONObject.fromObject(Config.personality_json_data);
		labelname.setText(jsonObject.getString("netname"));
		labelinfo.setText(jsonObject.getString("info"));
		if (jsonObject.getString("img").equals("无")) {
			btn_head.setIcon(new ImageIcon("resources//online//0.png"));
		}
		else {
			btn_head.setIcon(new ImageIcon("resources//online//"+jsonObject.getString("img")+".png"));
		}
	}
	
	/**
	 * Launch the application.
	 */
	public static void main(String[] args) {
		
		JFrame.setDefaultLookAndFeelDecorated(true);		//swing框架皮肤
		JDialog.setDefaultLookAndFeelDecorated(true);
		try {
			UIManager.setLookAndFeel("javax.swing.plaf.nimbus.NimbusLookAndFeel");
		} catch (Exception e) {
			// TODO: handle exception
			e.printStackTrace();
		}
		
		EventQueue.invokeLater(new Runnable() {
			public void run() {
				try {
					MainMenu frame = new MainMenu();
					frame.setVisible(true);
				} catch (Exception e) {
					e.printStackTrace();
				}
			}
		});
	}

	/**
	 * Create the frame.
	 */
	public MainMenu() {
		setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE); 
		setBounds(1000, 50, 303, 720);
		setVisible(true);
		contentPane = new JPanel();
		contentPane.setBorder(new EmptyBorder(5, 5, 5, 5));
		setContentPane(contentPane);
		contentPane.setLayout(new BorderLayout(0, 0));
		
		//北面面板
		JPanel panelNorth = new JPanel();
		panelNorth.setPreferredSize(new Dimension(300, 75));	//设置面板容器的大小 
		contentPane.add(panelNorth, BorderLayout.NORTH);
		ImageIcon head = new ImageIcon("resources//mainmenu//head_normal.jpg");
		btn_head = new JButton(head);	//设置头像
		btn_head.setBounds(10, 10, 48,48);  
		btn_head.setToolTipText("u5B8Cu5584u4E2Au4EBAu8D44u6599");
		btn_head.setFocusPainted(false);  
//		btn_head.setRolloverIcon(new ImageIcon("resources//mainmenu//head_highlight.jpg"));   鼠标放上去
//		btn_head.setPressedIcon(new ImageIcon("resources//mainmenu//head_down.jpg"));  	鼠标按压
		btn_head.setBorderPainted(false);  
		btn_head.setContentAreaFilled(false);  
		btn_head.addActionListener(new ActionListener() { 
			public void actionPerformed(ActionEvent e) {
				new Personality();
			}
		});
		panelNorth.setLayout(null); 
		panelNorth.add(btn_head);
		
		labelname = new JLabel("u9EC4u9F99u58EB");	//设置名称
		labelname.setFont(new Font("微软雅黑", Font.BOLD, 18));
		labelname.setBounds(72, 6, 180, 35);
		panelNorth.add(labelname);
		
		labelinfo = new JLabel("u9EC4u9F99u58EBu7684qqu7B7Eu540D");	//设置个性签名
		labelinfo.setToolTipText("u4E2Au6027u7B7Eu540Du66F4u65B0");
		labelinfo.setFont(new Font("微软雅黑", Font.PLAIN, 11));
		labelinfo.setBounds(72, 40, 180, 20);
		panelNorth.add(labelinfo); 
		
		//南面面板
		JPanel panelSouth = new JPanel();
		panelSouth.setPreferredSize(new Dimension(300, 60));	//设置面板容器的大小 
		contentPane.add(panelSouth, BorderLayout.SOUTH);
		panelSouth.setLayout(null);
		
		JButton btn_set = new JButton("设置");
		btn_set.setToolTipText("u8BBEu7F6E");
		btn_set.setBounds(10, 20, 60, 20);
		panelSouth.add(btn_set);
		
		JButton btn_exit = new JButton("退出");
		btn_exit.addActionListener(new ActionListener() {
			public void actionPerformed(ActionEvent e) {
				System.exit(0);
			}
		});
		btn_exit.setToolTipText("u9000u51FA");
		btn_exit.setBounds(80, 20, 60, 20);
		panelSouth.add(btn_exit);
		
		JButton btn_search = new JButton("u67E5u627E");
		btn_search.addActionListener(new ActionListener() {
			public void actionPerformed(ActionEvent e) {
				
				new Select();
				
			}
		});
		btn_search.setToolTipText("u67E5u627E");
		btn_search.setBounds(213, 20, 60, 20);
		panelSouth.add(btn_search);
		
		//中间面板
		JPanel panelCenter = new JPanel();
		panelCenter.setPreferredSize(new Dimension(0, 0));
		contentPane.add(panelCenter, BorderLayout.CENTER);
		panelCenter.setLayout(new BorderLayout(0, 0));
		
		JTabbedPane tabbedPane = new JTabbedPane(JTabbedPane.TOP);
		panelCenter.add(tabbedPane);
		
		JPanel panelmessage = new JPanel();
		tabbedPane.addTab(" 消 息 ", null, panelmessage, null);
		panelmessage.setLayout(new BorderLayout(0, 0));
		
		JScrollPane scrollPane = new JScrollPane();
		panelmessage.add(scrollPane, BorderLayout.CENTER);
		
		JPanel panelfriend = new JPanel();
		tabbedPane.addTab(" 联系人 ", null, panelfriend, null);
		panelfriend.setLayout(new BorderLayout(0, 0));
		
//		JLabel bg = new JLabel(new ImageIcon("resources//mainmenu//bg0.png"));	//这样设置背景有BUG
//		bg.setBounds(0, 0, 600, 600);	//背景覆盖全面版
//		panelfriend.add(bg,-1);
		
		JScrollPane scrollPane_1 = new JScrollPane();
		panelfriend.add(scrollPane_1, BorderLayout.CENTER);
		
		JPanel panelgroup = new JPanel();
		tabbedPane.addTab(" 群 组 ", null, panelgroup, null);
		panelgroup.setLayout(new BorderLayout(0, 0));
		
		JScrollPane scrollPane_2 = new JScrollPane();
		panelgroup.add(scrollPane_2, BorderLayout.CENTER);
		tabbedPane.setSelectedIndex(1); //设置联系人的选项卡被选中
		scrollPane_1.setViewportView(new FriendListJPanel());	// 插入好友列表
		
		this.mainUpdate();
	}
}

com.qq.view/FriendListJpanel.java - 好友列表面板

package com.qq.view;

import javax.swing.JPanel;

import com.qq.view.util.Config;

import net.sf.json.JSONArray;
import net.sf.json.JSONObject;

import javax.swing.ImageIcon;
import javax.swing.JLabel;

import java.awt.Dimension;
import java.awt.Font;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.Hashtable;
import java.util.Set;

import javax.swing.Icon;

public class FriendListJPanel extends JPanel {
	
	/**
	 * Create the panel.
	 */
	public FriendListJPanel() {
		super();
		setLayout(null);

		this.listUpdate();
	}
	
	public void onlineUpdate() {	//在线好友更新
		//在线列表
		String OnlineList = Config.friend_online;
		
		if (Config.friend_online.length() == 0) {		// 没有好友
			return;
		}
		
		String[] uids = OnlineList.split(",");	//在线好友
		Set<String> keys = Config.list.keySet();	//所有好友
		
		for(String key:keys) {		//先将所有好友置为离线
			Config.list.get(key).setOnline(false);
		}
		
		if(!OnlineList.equals("notFound") && !OnlineList.trim().equals("")) {		//如果没人在线则不用更新在线好友
			for(String uid:uids) {		//再将在线好友置为在线
				if (!uid.trim().equals("")) {
					FaceJPanel faceJPanel = (FaceJPanel)Config.list.get(uid);
					faceJPanel.setOnline(true);
				}
			}
		}
		
		Collection<FaceJPanel> faceJPanels = Config.list.values();
		ArrayList<FaceJPanel> tempList = new ArrayList(faceJPanels);
		Collections.sort(tempList);
		
		this.removeAll();
		int i=0;
		
		for (FaceJPanel faceJPanel : tempList) {
			faceJPanel.setBounds(0,i++*60,560,60);
			faceJPanel.flashImage();	//刷新头像
			this.add(faceJPanel);
		} 
		
		this.setPreferredSize(new Dimension(0,60*Config.list.size()));
		this.updateUI();
		
		Config.friendListJPanel = this;
	}
	
	
	
	public void listUpdate() {		//好友列表更新
		//好友列表
		String FriendList = Config.friend_json_data;	
		
		JSONArray jsonArray = JSONArray.fromObject(FriendList);	//解析json
		
		if(Config.list.size() == 0) {	//第一次加载列表
			//{"uid":"10002","img":"def","netname":"小龙人","info":"我是小龙人"}
			
			for (int i = 0; i < jsonArray.size(); i++) {	
				JSONObject jsonObject = (JSONObject)jsonArray.get(i);
				
				Config.list.put(jsonObject.getString("uid"), new FaceJPanel(jsonObject.getString("uid"), 
						jsonObject.getString("netname"), jsonObject.getString("info"), 
						jsonObject.getString("img"),jsonObject.getString("qqnumber")));
			}
		}else {		//已经加载过列表了
			
			for (int i = 0; i < jsonArray.size(); i++) {
				JSONObject jsonObject = (JSONObject)jsonArray.get(i);
				String uid = jsonObject.getString("uid");
				FaceJPanel faceJPanel = (FaceJPanel)Config.list.get(uid);
				if (faceJPanel != null) {
					faceJPanel.setName(jsonObject.getString("netname"));
					faceJPanel.setInfo(jsonObject.getString("info"));
					faceJPanel.setImage(jsonObject.getString("img"));
				}else {
					Config.list.put(uid, new FaceJPanel(uid, jsonObject.getString("netname"), 
							jsonObject.getString("info"), jsonObject.getString("img"),jsonObject.getString("qqnumber")));
				}
			}
			
		}

		this.onlineUpdate();
			
	}
	
}

com.qq.view/FaceJpanel.java - 好友面板

package com.qq.view;

import java.awt.Font;
import java.awt.Graphics;
import java.awt.event.MouseEvent;
import java.awt.event.MouseListener;
import java.util.Vector;

import javax.swing.ImageIcon;
import javax.swing.JLabel;
import javax.swing.JPanel;

import com.qq.view.util.Config;

public class FaceJPanel extends JPanel implements Comparable<FaceJPanel>,MouseListener,Runnable{

	private String uid;
	private String netName;
	private String info;
	private String image;
	private String qqnumber;
	private JLabel lblhead = new JLabel();
	private JLabel lblname = new JLabel();
	private JLabel lblinfo = new JLabel();
	private JLabel bg = new JLabel();
	private boolean isOnline = false;
	int x=0,y=0;
	
	public FaceJPanel(String uid,String netName,String info,String image,String qqnumber) {	//560*60
	
		this.uid = uid;
		this.netName = netName;
		this.info = info;
		this.image = image;
		this.qqnumber = qqnumber;
		this.setLayout(null);
		
		lblhead.setBounds(5, 5, 48, 48);
		add(lblhead);
		setImage(image);
		
		lblname.setFont(new Font("微软雅黑", Font.BOLD, 13));
		lblname.setBounds(70, 10, 369, 15);
		lblname.setText(netName);
		add(lblname);
		
		lblinfo.setFont(new Font("微软雅黑", Font.PLAIN, 11));
		lblinfo.setBounds(70, 35, 369, 15);
		lblinfo.setText(info);
		add(lblinfo);
		
		bg.setBounds(0, 0, 560, 60);	//背景覆盖全面版
		add(bg,-1);
		
		this.addMouseListener(this);
	}
	
	
	public void flashImage() {
		if (isOnline) {
			lblhead.setIcon(new ImageIcon("resources//online//"+this.image+".png"));
		} else {
			lblhead.setIcon(new ImageIcon("resources//offline//"+this.image+".png"));
		}
	}
	
	public void setImage(String image) {	//改头像
		if(image.equals("无")) {
			image = "0";		// 默认头像
		}
		this.image = image;
		
		if (isOnline) {
			lblhead.setIcon(new ImageIcon("resources//online//"+image+".png"));
		} else {
			lblhead.setIcon(new ImageIcon("resources//offline//"+image+".png"));
		}
	}
	
	public void setNetname(String netName) {
		lblname.setText(netName);
		this.netName = netName;
	}
	
	public void setInfo(String info) {
		lblinfo.setText(info);
		this.info = info;
	}
	
	public void setOnline(boolean isOnline) {		//在线离线切换
		this.isOnline = isOnline;
	}
	
	//存放所有未显示在chat的消息
	private Vector<Msg> msgs = new Vector<Msg>();
	boolean run = true;		//控制来消息是否产生效果
	private Thread thread = null;
	
	@Override
	public void run() {
		int x = lblhead.getX();
		int y = lblhead.getY();
				
		run = true;
		while(run) {					//头像抖动效果
			
			lblhead.setLocation(x-2, y-2);
			try {
				thread.sleep(300);
			} catch (Exception e) {
			}
			lblhead.setLocation(x+2, y+2);
			try {
				thread.sleep(300);
			} catch (Exception e) {
			}
		}
		
		lblhead.setLocation(x, y);
	}
	
	//寄存消息
	public void addMessage(Msg msg) {
		msgs.add(msg);
	
		if (thread == null) {
			thread = new Thread(this);
			thread.start();
		}else if(thread.getState() == Thread.State.TERMINATED){
			thread = new Thread(this);
			thread.start();
		}else if(run == false){
			thread = new Thread(this);
			thread.start();
		}
	}
	
	@Override
	public int compareTo(FaceJPanel o) {

		if (o.isOnline) {
			return 1;	//他在你上面
		}else if (this.isOnline) {
			return -1;	//你在它上面
		}else {
			return 0;	//你们两平等
		}

	}
	
	@Override
	public void mouseClicked(MouseEvent e) {	//就不做按压改变颜色了,因为这样还得监听其他的facejpanel
		bg.setIcon(new ImageIcon("resources//mainmenu//bg1.png"));
		if (e.getClickCount() == 2) {
			Config.showChat(uid, netName, info, image, isOnline,qqnumber,msgs);
			run = false;		//停止效果
		}
	}

	@Override
	public void mousePressed(MouseEvent e) {
		
	}

	@Override
	public void mouseReleased(MouseEvent e) {
		
	}

	@Override
	public void mouseEntered(MouseEvent e) {
		bg.setIcon(new ImageIcon("resources//mainmenu//bg2.png"));
	}

	@Override
	public void mouseExited(MouseEvent e) {
		bg.setIcon(new ImageIcon(""));
	}


	
}

com.qq.view/Chat.java - 聊天界面

package com.qq.view;

import java.awt.BorderLayout;
import java.awt.Dimension;
import java.awt.EventQueue;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.KeyEvent;
import java.awt.event.WindowEvent;
import java.awt.event.WindowListener;
import java.net.DatagramPacket;
import java.net.InetAddress;
import java.util.Date;
import java.util.Vector;

import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.border.EmptyBorder;

import com.qq.view.util.Config;

import net.sf.json.JSONObject;

import javax.swing.ImageIcon;
import javax.swing.JButton;
import javax.swing.JComponent;
import javax.swing.JDialog;
import javax.swing.JLabel;
import java.awt.Font;
import java.awt.TextArea;

import javax.swing.JSeparator;
import javax.swing.JSplitPane;
import javax.swing.KeyStroke;
import javax.swing.JScrollPane;
import javax.swing.SwingConstants;
import javax.swing.UIManager;

import java.awt.FlowLayout;
import javax.swing.JTextArea;
import javax.swing.JTextField;

public class Chat extends JFrame implements ActionListener,WindowListener {

	private JPanel contentPane;

	private JButton btn_head = new JButton();
	private JLabel labelname = new JLabel();
	private JLabel labelinfo = new JLabel();
	private String uid,netname,info,img,qqnumber;	//uid是对方的id
	private JTextArea sendview = new JTextArea();
	private JTextArea mainview = new JTextArea();
	private boolean isOnline =false;
	/**
	 * Launch the application.
	 */
	
	public void onlineUpdate(boolean isOnline) {
		this.isOnline = isOnline;
	}
	
	public void addMyMessage(Msg msg) {		//添加自己的消息
		String str = "
" + this.netname + "	"+ new Date().toLocaleString() + "
" +msg.getMsg() + "
";
		
		mainview.setText(mainview.getText()+str);
		mainview.setSelectionStart(mainview.getText().toString().length());
		mainview.setSelectionEnd(mainview.getText().toString().length());
		
		sendview.requestFocus();	//输入框光标闪动
	}
	
	public void addMessage(Msg msg) {		//添加别人的消息
		String str = "
" + JSONObject.fromObject(Config.personality_json_data).getString("netname")+"	"
				+ new Date().toLocaleString() + "
" +msg.getMsg() + "
";
		
		mainview.setText(mainview.getText()+str);
		mainview.setSelectionStart(mainview.getText().toString().length());
		mainview.setSelectionEnd(mainview.getText().toString().length());
		
		sendview.requestFocus();	//输入框光标闪动
	}
	
	/**
	 * Create the frame.
	 */
	public Chat(String uid,String netname,String info,String img,boolean isOnline,String qqnumber,Vector<Msg> msgs) {
		this.uid = uid;
		this.netname = netname;
		this.info = info;
		this.img = img;
		this.isOnline = isOnline;
		this.qqnumber = qqnumber;
		ImageIcon imageIcon = null;
		if (isOnline) {
			imageIcon = new ImageIcon("resources//online//"+img+".png");
		}else {
			imageIcon = new ImageIcon("resources//offline//"+img+".png");
		}
		this.setIconImage(imageIcon.getImage());	// 设置小图标
		btn_head.setIcon(imageIcon);
		labelname.setText(" "+netname+" ("+qqnumber+")");
		labelinfo.setText("  "+info);
		
		
		setTitle(netname);
		setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE);
		setBounds(0, 0, 875, 650);
		setLocationRelativeTo(null);	//正中间位置
		setVisible(true);
		contentPane = new JPanel();
		contentPane.setBorder(new EmptyBorder(5, 5, 5, 5));
		contentPane.setLayout(new BorderLayout(0, 0));
		setContentPane(contentPane);
		
		//北面面板
		JPanel panelNorth = new JPanel();
		panelNorth.setPreferredSize(new Dimension(0, 48));
		contentPane.add(panelNorth, BorderLayout.NORTH);
		panelNorth.setLayout(new BorderLayout(0, 0));
		
		btn_head.addActionListener(new ActionListener() {
			public void actionPerformed(ActionEvent e) {
			}
		});
		btn_head.setPreferredSize(new Dimension(48, 48));
		btn_head.setToolTipText("u5B8Cu5584u4E2Au4EBAu8D44u6599");
		btn_head.setFocusPainted(false);  
		btn_head.setRolloverIcon(new ImageIcon("resources//mainmenu//head_highlight.jpg"));   
		btn_head.setPressedIcon(new ImageIcon("resources//mainmenu//head_down.jpg"));  
		btn_head.setBorderPainted(false);  
		btn_head.setContentAreaFilled(false);  
		panelNorth.add(btn_head, BorderLayout.WEST); 
		
		JPanel panel = new JPanel();
		panelNorth.add(panel, BorderLayout.CENTER);
		panel.setLayout(new BorderLayout(0, 0));

		labelname.setFont(new Font("微软雅黑", Font.BOLD, 18));
		panel.add(labelname, BorderLayout.CENTER);	
		
		labelinfo.setFont(new Font("微软雅黑", Font.PLAIN, 10));
		labelinfo.setPreferredSize(new Dimension(0, 15));
		panel.add(labelinfo, BorderLayout.SOUTH);
		
		
		//中心面板
		JSplitPane splitPane = new JSplitPane();		//分割面板
		splitPane.setOrientation(JSplitPane.VERTICAL_SPLIT);
		splitPane.setDividerLocation(400);		//上面板的大小
		contentPane.add(splitPane, BorderLayout.CENTER);
		
		JPanel xia = new JPanel();
		splitPane.setRightComponent(xia);
		xia.setLayout(new BorderLayout(0, 0));
		
		JPanel panel_1 = new JPanel();
		xia.add(panel_1, BorderLayout.NORTH);
		panel_1.setLayout(null);
		panel_1.setPreferredSize(new Dimension(0, 30));
		
		JButton btn_font = new JButton("u5B57u4F53");
		btn_font.setToolTipText("u5B57u4F53");
		btn_font.setLocation(3, 4);
		btn_font.setSize(67, 23);
		panel_1.add(btn_font); 
		
		JButton btn_zhendong = new JButton("u6296u52A8");
		btn_zhendong.setToolTipText("u5411u597Du53CBu53D1u9001u7A97u53E3u6296u52A8");
		btn_zhendong.setBounds(80, 4, 67, 23);
		panel_1.add(btn_zhendong);
		
		JPanel panel_2 = new JPanel();
		xia.add(panel_2, BorderLayout.SOUTH);
		panel_2.setLayout(null);
		panel_2.setPreferredSize(new Dimension(0, 30));
		
		JButton btn_close = new JButton("u5173u95ED"); 
		btn_close.setToolTipText("u5173u95ED");
		btn_close.setBounds(671, 5, 67, 23);	//738
		panel_2.add(btn_close);
		
		JButton btn_send = new JButton("u53D1u9001");
		btn_send.addActionListener(new ActionListener() {
			public void actionPerformed(ActionEvent e) {
				
				if(!Chat.this.isOnline) {		//如果对方没上线,则不让发送
					javax.swing.JOptionPane.showMessageDialog(Chat.this, "对方未上线,发送消息失败!");
					sendview.setText(""); 			//发送之后清空区域
					return;
				}
				
				try {
					
					Msg msg = new Msg();
					msg.setCode(System.currentTimeMillis()+"");
					msg.setMsg(sendview.getText());
					msg.setMyUID(JSONObject.fromObject(Config.personality_json_data).getString("uid"));
					msg.setToUID(uid);
					msg.setType("msg");
					String json = JSONObject.fromObject(msg).toString();
					
					// json = {"msg":"123456","code":"1574426820174","toUID":"10002","myUID":"10001","type":"msg"}
					
					byte[] bytes = json.getBytes();
					
					DatagramPacket datagramPacket = new DatagramPacket(bytes, bytes.length,
							InetAddress.getByName(Config.IP),Config.Msg_PORT);
					Config.datagramSocket_client.send(datagramPacket);
					sendview.setText(""); 			//发送之后清空区域
					
					addMyMessage(msg);
				} catch (Exception e2) {
					e2.printStackTrace();
				}
			
			}
		});
		btn_send.registerKeyboardAction(this, KeyStroke.getKeyStroke(KeyEvent.VK_ENTER,0),JComponent.WHEN_IN_FOCUSED_WINDOW);
		btn_send.setToolTipText("Enteru952Eu53D1u9001u6D88u606F");
		btn_send.setBounds(748, 5, 97, 23);
		panel_2.add(btn_send);
		
		JScrollPane scrollPane = new JScrollPane();
		xia.add(scrollPane, BorderLayout.CENTER);
		
		scrollPane.setViewportView(sendview);
		
		JScrollPane mainwindow = new JScrollPane();
		splitPane.setLeftComponent(mainwindow);
		
		mainwindow.setViewportView(mainview);
//		mainwindow.setEnabled(false);	//不可编辑
		
		for (Msg msg : msgs) {
			this.addMessage(msg);
		}
		msgs.clear();
	}

	@Override
	public void actionPerformed(ActionEvent e) {
		// TODO Auto-generated method stub
		
	}
	@Override
	public void windowOpened(WindowEvent e) {
		// TODO Auto-generated method stub
		
	}
	@Override
	public void windowClosing(WindowEvent e) {
		
		Config.closeChat(uid);
		this.dispose();
	}
	@Override
	public void windowClosed(WindowEvent e) {
		// TODO Auto-generated method stub
		
	}
	@Override
	public void windowIconified(WindowEvent e) {
		// TODO Auto-generated method stub
		
	}
	@Override
	public void windowDeiconified(WindowEvent e) {
		// TODO Auto-generated method stub
		
	}
	@Override
	public void windowActivated(WindowEvent e) {
		// TODO Auto-generated method stub
		
	}
	@Override
	public void windowDeactivated(WindowEvent e) {
		// TODO Auto-generated method stub
		
	}
}

com.qq.view/Personality.java - 个人资料界面

package com.qq.view;

import java.awt.BorderLayout;
import java.awt.Dimension;
import java.awt.EventQueue;

import javax.swing.ImageIcon;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.border.EmptyBorder;

import com.qq.view.util.Config;

import net.sf.json.JSONObject;

import javax.swing.JLabel;
import javax.swing.JButton;
import java.awt.event.ActionListener;
import java.util.ArrayList;
import java.util.Calendar;
import java.util.List;
import java.awt.event.ActionEvent;
import javax.swing.JTextField;
import java.awt.Font;
import javax.swing.JRadioButton;
import javax.swing.JComboBox;
import javax.swing.JDialog;
import javax.swing.JScrollPane;
import javax.swing.JTextArea;
import javax.swing.SwingConstants;
import javax.swing.UIManager;

public class Personality extends JFrame {

	private JButton btn_head = new JButton();
	private JPanel contentPane;
	private JTextField text_netName = new JTextField();
	private JTextField text_info = new JTextField();
	private JTextField text_realname = new JTextField();
	private JTextField textemail = new JTextField();
	private JTextField textphone = new JTextField();
	private JTextArea text_back = new JTextArea();
	private JComboBox cbYear,cbMonth,cbDay,cbBlood,cbSex,cbPosition,cbHometown,cbEmotion;
	private String str0[] = new String[30];
	private String str1[] = {"1","2","3","4","5","6","7","8","9","10","11","12"};
	private String str2[] = new String[31];
	private String str3[] = {"男","女"};
	private String str4[] = {"A","B","AB","O"};

	public void personUpdate() {		//更新自己主界面的上方显示
		//{"dd":"","mm":"","profession":"","yy":"","img":"无","gend":"男","phonenumber":"","back":"我有一丶丶想你",
		//"realname":"mm","relation":"单身","uid":"10002","bloodtype":"A","netname":"小龙人","email":"","info":"我是小龙人"}

		JSONObject jsonObject = JSONObject.fromObject(Config.personality_json_data);
		text_netName.setText(jsonObject.getString("netname"));
		text_info.setText(jsonObject.getString("info"));
		if (jsonObject.getString("img").equals("无")) {
			btn_head.setIcon(new ImageIcon("resources//online//0.png"));
		}
		else {
			btn_head.setIcon(new ImageIcon("resources//online//"+jsonObject.getString("img")+".png"));
		}
		
		textemail.setText(jsonObject.getString("email"));
		textphone.setText(jsonObject.getString("phonenumber"));
		text_back.setText(jsonObject.getString("back"));
		cbSex.setSelectedItem(jsonObject.getString("gend"));
		cbBlood.setSelectedItem(jsonObject.getString("bloodtype"));
//		cbYear.setSelectedItem(jsonObject.getString("yy"));
		cbMonth.setSelectedItem(jsonObject.getString("mm"));

	}

	/**
	 * Create the frame.
	 */
	public Personality() {
		
		
		setTitle("u4E2Au4EBAu8D44u6599");
		setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE);
		setVisible(true);
		setBounds(100, 100, 750, 540);
		setLocationRelativeTo(null);	//正中间位置
		setResizable(false);	//不可改变大小
//		this.setUndecorated(true);	//消除窗体边框
		contentPane = new JPanel();
		contentPane.setBorder(new EmptyBorder(5, 5, 5, 5));
		setContentPane(contentPane);
		contentPane.setLayout(null);
		
		//全背景
		ImageIcon background = new ImageIcon("resources//personality//bg3.jpg");	//背景要放到JLabel里面才能显示 - //getClass().getResource("qqlogin2.jpg")放在该包下
		JLabel backgroundLabel = new JLabel(background);
		backgroundLabel.setBounds(0, 0, background.getIconWidth(),background.getIconHeight());	//设置标签显示的位置和大小
		this.getLayeredPane().add(backgroundLabel, new Integer(Integer.MIN_VALUE));		//放在jframe的layeredpane层
		JPanel contentPanel = (JPanel)this.getContentPane(); 			//将contentPane层设为透明,jframe直接add的部件都放在这一层
		contentPanel.setOpaque(false);		//将contentPane设置成透明 - 需要将子组件中的jpanel都设成透明才可见
		
		//左边面板
		JPanel panelLeft = new JPanel();
		panelLeft.setBounds(0, 0, 375, 500);
		contentPane.add(panelLeft);
		panelLeft.setLayout(new BorderLayout(0, 0));
		
		JLabel bg1 = new JLabel(new ImageIcon("resources//personality//personality.jpg"));
		panelLeft.add(bg1, BorderLayout.CENTER);
		
		JPanel panel = new JPanel();
		panel.setPreferredSize(new Dimension(0, 100));
		panelLeft.add(panel, BorderLayout.SOUTH); 
		panel.setLayout(null);
		
		btn_head.setToolTipText("u8BBEu7F6Eu5934u50CF");
		btn_head.setFocusPainted(false);  
//		btn_head.setRolloverIcon(new ImageIcon("resources//online//head_highlight.jpg"));   
//		btn_head.setPressedIcon(new ImageIcon("resources//online//head_down.jpg"));  
		btn_head.setBorderPainted(false);  
		btn_head.setContentAreaFilled(false);  
		btn_head.setBounds(21, 25, 48, 48);
		panel.add(btn_head);
		
		text_netName.setText("u9EC4u9F99u58EB");
		text_netName.setFont(new Font("微软雅黑", Font.PLAIN, 15));
		text_netName.setBounds(95, 25, 179, 26);
		panel.add(text_netName);
		text_netName.setColumns(10);
		
		text_info.setText("u9EC4u9F99u58EBu7684u4E2Au6027u7B7Eu540D");
		text_info.setFont(new Font("宋体", Font.PLAIN, 10));
		text_info.setBounds(95, 58, 179, 21);
		panel.add(text_info);
		text_info.setColumns(10);
		
		JLabel bg2 = new JLabel(new ImageIcon("resources//personality//bg2.jpg"));
		bg2.setBounds(0, 0, 375, 100);
		panel.add(bg2,-1);
		
		
		//右边面板
		JPanel panelRight = new JPanel(); 
		panelRight.setBounds(375, 0, 370, 500);
		contentPane.add(panelRight);
		panelRight.setLayout(new BorderLayout(0, 0));
		panelRight.setOpaque(false);
		
		JPanel panel_control = new JPanel();
		panel_control.setPreferredSize(new Dimension(0, 25));
		panelRight.add(panel_control, BorderLayout.NORTH);
		panel_control.setLayout(null);
		panel_control.setOpaque(false);
		
		JButton btn_close = new JButton(new ImageIcon("resources//login//btn_close_normal.png"));
		btn_close.setFocusPainted(false);  
		btn_close.setRolloverIcon(new ImageIcon("resources//login//btn_close_highlight.png"));   
		btn_close.setPressedIcon(new ImageIcon("resources//login//btn_close_down.png"));  
		btn_close.setBorderPainted(false);  
		btn_close.setContentAreaFilled(false);  
		btn_close.setBounds(330, -10, 36, 36);
		panel_control.add(btn_close);
		
		JPanel panel_1 = new JPanel();
		panelRight.add(panel_1, BorderLayout.CENTER);
		panel_1.setLayout(null);
		panel_1.setOpaque(false);
		
		JLabel lblNewLabel = new JLabel("u771Fu5B9Eu59D3u540D");
		lblNewLabel.setFont(new Font("宋体", Font.BOLD, 12));
		lblNewLabel.setBounds(20, 60, 56, 25);
		panel_1.add(lblNewLabel);
		
		text_realname = new JTextField();
		text_realname.setBounds(84, 60, 89, 25);
		panel_1.add(text_realname);
		text_realname.setColumns(10);
		
		JLabel label = new JLabel("u6027  u522B");
		label.setFont(new Font("宋体", Font.BOLD, 12));
		label.setBounds(185, 60, 56, 25);
		panel_1.add(label);
		
		JLabel label_1 = new JLabel("u51FAu751Fu5E74u6708");
		label_1.setFont(new Font("宋体", Font.BOLD, 12));
		label_1.setBounds(20, 115, 56, 25);
		panel_1.add(label_1);

		cbYear = new JComboBox();
		cbYear.setBounds(84, 115, 56, 25);
		panel_1.add(cbYear);
		
		cbMonth = new JComboBox(str1);
		cbMonth.setBounds(171, 115, 56, 25);
		panel_1.add(cbMonth);
		
		cbDay = new JComboBox();
		cbDay.setBounds(253, 115, 56, 25);
		panel_1.add(cbDay);
		
		JLabel label_2 = new JLabel("u8840   u578B");
		label_2.setFont(new Font("宋体", Font.BOLD, 12));
		label_2.setBounds(20, 170, 56, 25);
		panel_1.add(label_2);
		
		cbBlood = new JComboBox(str3);
		cbBlood.setBounds(84, 170, 56, 25);
		panel_1.add(cbBlood);
		
		cbSex = new JComboBox(str4);
		cbSex.setBounds(251, 60, 56, 23);
		panel_1.add(cbSex);
		
		JLabel lblNewLabel_1 = new JLabel("u804C   u4E1A");
		lblNewLabel_1.setFont(new Font("宋体", Font.BOLD, 12));
		lblNewLabel_1.setBounds(185, 170, 56, 25);
		panel_1.add(lblNewLabel_1);
		
		cbPosition = new JComboBox();
		cbPosition.setBounds(253, 170, 56, 25);
		panel_1.add(cbPosition);
		
		JLabel label_3 = new JLabel("u624B   u673A");
		label_3.setFont(new Font("宋体", Font.BOLD, 12));
		label_3.setBounds(20, 220, 56, 25);
		panel_1.add(label_3);
		
		JLabel label_4 = new JLabel("u6545   u4E61");
		label_4.setFont(new Font("宋体", Font.BOLD, 12));
		label_4.setBounds(185, 220, 56, 25);
		panel_1.add(label_4);
		
		JLabel label_5 = new JLabel("u90AE   u7BB1");
		label_5.setFont(new Font("宋体", Font.BOLD, 12));
		label_5.setBounds(20, 265, 56, 25);
		panel_1.add(label_5);
		
		JLabel label_6 = new JLabel("u60C5u611Fu5173u7CFB");
		label_6.setFont(new Font("宋体", Font.BOLD, 12));
		label_6.setBounds(20, 310, 56, 25);
		panel_1.add(label_6);
		
		JLabel label_7 = new JLabel("u4E2Au4EBAu8BF4u660E");
		label_7.setFont(new Font("宋体", Font.BOLD, 12));
		label_7.setBounds(20, 350, 56, 25);
		panel_1.add(label_7);
		
		cbHometown = new JComboBox();
		cbHometown.setBounds(253, 220, 56, 25);
		panel_1.add(cbHometown);
		
		textemail.setColumns(10);
		textemail.setBounds(84, 267, 222, 25);
		panel_1.add(textemail);
		
		cbEmotion = new JComboBox();
		cbEmotion.setBounds(84, 310, 56, 25);
		panel_1.add(cbEmotion);
		
		JScrollPane scrollPane = new JScrollPane();
		scrollPane.setBounds(84, 350, 258, 80);
		panel_1.add(scrollPane);
		
		scrollPane.setViewportView(text_back);
		
		JLabel lblQq = new JLabel("u4E2Au4EBAu8D44u6599");
		lblQq.setFont(new Font("宋体", Font.BOLD, 14));
		lblQq.setBounds(20, 20, 68, 25);
		panel_1.add(lblQq);
		
		textphone.setColumns(10);
		textphone.setBounds(84, 220, 89, 25);
		panel_1.add(textphone);
		
		// 呈现个人资料
		JSONObject json = JSONObject.fromObject(Config.personality_json_data);
		text_realname.setText(json.getString("realname"));
		
		JButton btn_save = new JButton("u4FDDu5B58");
		btn_save.addActionListener(new ActionListener() {
			public void actionPerformed(ActionEvent e) {
			}
		});
		btn_save.setBounds(245, 442, 97, 25);
		panel_1.add(btn_save);
		
		this.personUpdate();
	}
}

com.qq.view/Select.java - 搜索好友界面

package com.qq.view;

import java.awt.BorderLayout;
import java.awt.EventQueue;

import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.border.EmptyBorder;

import com.qq.view.server.NetService;

import javax.swing.JLabel;
import javax.swing.SwingConstants;
import javax.swing.UIManager;
import javax.swing.JTextField;
import javax.swing.JButton;
import javax.swing.JDialog;
import javax.swing.JScrollPane;
import java.awt.event.ActionListener;
import java.util.Vector;
import java.awt.event.ActionEvent;
import javax.swing.JTable;

public class Select extends JFrame {

	private JPanel contentPane;
	private JTextField textField;
	private JTable table;

	Vector cols = new Vector();		//结合tab表一起使用
	Vector rows = new Vector();
	

	/**
	 * Create the frame.
	 */
	public Select() {
		setTitle("u67E5u8BE2u597Du53CB");
		setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE);
		setBounds(100, 100, 489, 351);
		setLocationRelativeTo(null);	//正中间位置
		setResizable(false);	//不可改变大小
		setVisible(true);
		contentPane = new JPanel();
		contentPane.setBorder(new EmptyBorder(5, 5, 5, 5));
		setContentPane(contentPane);
		contentPane.setLayout(null);
		
		JLabel lblNewLabel = new JLabel("u6635u79F0");
		lblNewLabel.setHorizontalAlignment(SwingConstants.CENTER);
		lblNewLabel.setBounds(10, 22, 78, 22);
		contentPane.add(lblNewLabel);
		
		textField = new JTextField();
		textField.setBounds(89, 23, 248, 21);
		contentPane.add(textField);
		textField.setColumns(10);
		
		JButton btnNewButton = new JButton("u67E5u8BE2");
		btnNewButton.addActionListener(new ActionListener() {
			public void actionPerformed(ActionEvent e) {
				
				String username = textField.getText();		// 输入qq号
				
				NetService.getNetService().searchPerson(username);
				
			}
		});
		btnNewButton.setBounds(365, 22, 97, 23);
		contentPane.add(btnNewButton);
		
		JScrollPane scrollPane = new JScrollPane();
		scrollPane.setBounds(19, 54, 443, 243);
		contentPane.add(scrollPane);
		
		cols.add("昵称");
		cols.add("在线");
		
		table = new JTable(rows,cols);
		scrollPane.setViewportView(table);

		
	}
}

com.qq.view/Msg.java - UDP消息封装类

package com.qq.view;

public class Msg {

	private String MyUID;
	private String toUID;
	private String msg;
	private String type;
	private String code;
	public String getMyUID() {
		return MyUID;
	}
	public void setMyUID(String myUID) {
		MyUID = myUID;
	}
	public String getToUID() {
		return toUID;
	}
	public void setToUID(String toUID) {
		this.toUID = toUID;
	}
	public String getMsg() {
		return msg;
	}
	public void setMsg(String msg) {
		this.msg = msg;
	}
	public String getType() {
		return type;
	}
	public void setType(String type) {
		this.type = type;
	}
	public String getCode() {
		return code;
	}
	public void setCode(String code) {
		this.code = code;
	}
	
	
}

com.qq.view.server/NetService.java - 后端通讯服务

package com.qq.view.server;

import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.DatagramSocket;
import java.net.Socket;
import java.net.UnknownHostException;

import com.qq.view.Login;
import com.qq.view.util.Config;

import net.sf.json.JSONArray;
import net.sf.json.JSONObject;

/**
 *	 通讯服务 与服务器一直保持连接状态
 *	1.更新好友在线状态 - 5s更新一次
 *	2.登陆验证 
 * 	3.退出账户
 * 
 * @author:黄龙士
 */

public class NetService implements Runnable{

	
	private NetService() {
		// TODO Auto-generated constructor stub
	}
	
	//单例
	private static NetService netService = new NetService();
	
	public static NetService getNetService() {
		return netService;
	}
	
	private Socket socket =null;
	private InputStream input = null;
	private OutputStream output = null;
	private Thread thread = null;
	private boolean run = false;
	
	//这里准备与服务器保持长期通讯

	
	public JSONObject login() throws UnknownHostException,IOException {
		
		socket = new Socket(Config.IP,Config.LOGIN_PORT);
		
		input = socket.getInputStream();
		output = socket.getOutputStream();
		
		String json_str = "{"username":"" + Config.username + "","password":"" + Config.password + ""}";
		
		//开始与服务器传递消息
		output.write(json_str.getBytes());
		output.flush();
		
		//等待服务器回执消息	{state:,msg:}		
		byte[] bytes =new byte[1024];
		int len = input.read(bytes);
		json_str = new String(bytes,0,len);
		JSONObject json = JSONObject.fromObject(json_str);	//解析json文本
		
		//如果state==0,则登陆成功
		if(json.getInt("state") == 0) {
			//开启持续的网络服务
			
			if(thread != null) {
				//询问线程是否还活着
				if(thread.getState() == Thread.State.RUNNABLE) {
					run = false;	//终止线程
					try {
						thread.stop();
					} catch (Exception e) {
						// TODO: handle exception
					}
					
				}	
			}
			
			/////////////////////////////	
			output.write("U0001".getBytes());	//发送好友列表更新消息
			output.flush();

			bytes = new byte[1024*10];	//好友列表信息获得
			len = input.read(bytes);
			String jsonstr = new String(bytes,0,len);

			Config.jiexi_friend_json_data(jsonstr); 	//解析好友列表
			
			System.out.println("好友资料:"+Config.friend_json_data);		//debug
			
			output.write("U0003".getBytes());	//更新个人资料
			output.flush();
			len = input.read(bytes);
			
			String str = new String(bytes,0,len);		//此处是因为传过来的消息前后多了 [] 符号,需要去除,不然之后解析要多写代码
			Config.personality_json_data = str.substring(1,str.length()-1);	
			
			System.out.println("个人资料:"+Config.personality_json_data);		//debug
			/////////////////////////////
			
			//////////////////////////////启动UDP服务器
			Config.datagramSocket_client = new DatagramSocket();
			//启动心跳包
			new MessageRegService(Config.datagramSocket_client);
			//启动消息端
			new MessageService(Config.datagramSocket_client);
			
			////////////////////////////
			
			//重新开线程与服务器保持通讯
			thread = new Thread(this);
			run = true;
			thread.start();	
		}
		
		return json;
	}

	@Override
	public void run() {
		// TODO Auto-generated method stub
		
		
		try {
					 
			byte[] bytes = new byte[1024*10];
			int len = 0;
			
			while(run) {
				output.write("U0002".getBytes());	//实时更新好友在线 
				output.flush();
				input.read();		// 读个ack
				
				output.write(Config.friend_list_data.getBytes());	//发送好友列表更新消息
				output.flush();
				
				len = input.read(bytes);	
				String online = new String(bytes,0,len);
				System.out.println("在线账户:"+online);		// notFound来自此
				
				try {
					if (!online.equals(Config.friend_online)) {		//只有两者不相等时才做更新
						Config.friend_online = online;	
						Config.friendListJPanel.onlineUpdate();
					}
					
				} catch (Exception e) {
				}
						
				try {
					thread.sleep(2000);		//2s更新一次在线好友
				} catch (InterruptedException e) {
					// TODO: handle exception
				}
			}
		} catch (StringIndexOutOfBoundsException e) {
			run = false;
			javax.swing.JOptionPane.showMessageDialog(null, "您的账户在其他地方登陆!");
			System.exit(0);		//直接退出进程
		} catch (Exception e) {
			run = false;
			e.printStackTrace();
		}
		
	}
	
	public void searchPerson(String username) {
		try {
			 
			byte[] bytes = new byte[1024*10];
			int len = 0;
			
			output.write("U0004".getBytes());	//实时更新好友在线 
			output.flush();
			input.read();		// 读个ack
			
			output.write(username.getBytes());	//发送查找username
			output.flush();
			
			len = input.read(bytes);	
			String str_json = new String(bytes,0,len);
			System.out.println(str_json);		// 接受json
			
			

		} catch (Exception e) {
			e.printStackTrace();
		}
	}
}

com.qq.view.server/MessageService.java - 接收服务器中转过来的消息

package com.qq.view.server;

import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.net.InetAddress;
import com.qq.view.util.Config;
/**
 * 
 * 	接收服务器中转过来的消息
 * @author 黄龙士	
 *
 */
public class MessageService extends Thread{

	private DatagramSocket client = null;
	
	public void run() {
		
		while(true) {
			try {
				byte[] bytes = new byte[1024*32];
				DatagramPacket datagramPacket = new DatagramPacket(bytes, bytes.length,
						InetAddress.getByName(Config.IP),Config.Msg_PORT);
				client.receive(datagramPacket);
				
				// 接收的消息存储至消息栈里
				MessageStack.getMessageStack().addMessage(new String(datagramPacket.getData(),
						0,datagramPacket.getData().length));
				
			} catch (Exception e) {
				e.printStackTrace();
			}
		}
	}
	
	public MessageService(DatagramSocket client) {
		this.client = client;
		this.start();
	}
	
}

com.qq.view.server/MessageRegService.java - 发送心跳包

package com.qq.view.server;

import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.net.InetAddress;

import com.qq.view.util.Config;

import net.sf.json.JSONObject;

/**
 * 
 * 	向服务器发送心跳包
 * @author 黄龙士
 *
 */

public class MessageRegService extends Thread{

	private DatagramSocket client = null;
	
	//每10s向服务器发送心跳包
	public void run() {
		
		String uid = JSONObject.fromObject(Config.personality_json_data).getString("uid");
		String jsonstr = "{"type":"reg","myUID":""+uid+""}";
		byte[] bytes = jsonstr.getBytes();
		while(true) {

			try {
				DatagramPacket datagramPacket = new DatagramPacket(bytes, bytes.length,
						InetAddress.getByName(Config.IP),Config.Msg_PORT);
				
				//将心跳包发送给服务器
				client.send(datagramPacket);
				Thread.sleep(9999); 	
			} catch (Exception e) {
				e.printStackTrace();
			}
		}
	}
	
	public MessageRegService(DatagramSocket client) {
		this.client = client;
		this.start();
	}
	
}

com.qq.view.server/MessageStack.java - 消息栈

package com.qq.view.server;

import java.util.HashMap;
import java.util.LinkedList;

import com.qq.view.Chat;
import com.qq.view.FaceJPanel;
import com.qq.view.Msg;
import com.qq.view.util.Config;

import net.sf.json.JSONObject;

/**
 * 	消息栈 - 会存储所有的消息
 * @author 黄龙士
 *
 */

public class MessageStack {		//与在线队列一致 饿汉式单例实现
	
	private MessageStack() {}
	
	private static MessageStack messageStack = new MessageStack();
	
	public static MessageStack getMessageStack() {
		return messageStack;
	}
	
	public static HashMap<String, LinkedList<Msg>> hashMap = new HashMap();
	
	// 不管是给谁的消息都存储起来
	public void addMessage(String json) {
		// {"msg":"123456","code":"1574426820174","toUID":"10002","myUID":"10001","type":"msg"}
		
		JSONObject jsonObject = JSONObject.fromObject(json);
		String toUID = jsonObject.getString("toUID");
		String myUID = jsonObject.getString("myUID");
		String msg = jsonObject.getString("msg");
		String code = jsonObject.getString("code");
		String type = jsonObject.getString("type");		
		
		// 把消息储存在Msg中
		Msg msgObj = new Msg(); 
		msgObj.setCode(code);
		msgObj.setMsg(msg);
		msgObj.setMyUID(myUID);
		msgObj.setToUID(toUID);
		msgObj.setType(type);
		
		try {
			Chat chat = Config.chatTab.get(myUID);
			if (chat.isVisible()) {
				chat.addMessage(msgObj);
			}else {
				throw new Exception();
			}
		} catch (Exception e) {
			
			
			FaceJPanel faceJPanel = Config.list.get(myUID);		//当窗口没打开时这样
			faceJPanel.addMessage(msgObj);
			
			//Msg列表
//			LinkedList<Msg> list = hashMap.get(myUID);	//当我窗口打开时这样
//			if (list == null) {
//				list = new LinkedList<Msg>();
//			}
//			
//			list.add(msgObj);
//			hashMap.put(myUID, list);
			
		}
	}
	
}

com.qq.view.util/Config.java - 全局变量

package com.qq.view.util;

import java.net.DatagramSocket;
import java.util.HashMap;
import java.util.Hashtable;
import java.util.Vector;

import com.qq.view.Chat;
import com.qq.view.FaceJPanel;
import com.qq.view.FriendListJPanel;
import com.qq.view.MainMenu;
import com.qq.view.Msg;

import net.sf.json.JSONArray;
import net.sf.json.JSONObject;

public class Config {
	
	//服务器地址
	public static final String IP = "10.177.215.78";		//校园网 - 每次重新连接得重新配ip地址
//	public static final String IP = "192.168.31.38";		//宿舍wifi - 每次重新连接得重新配ip地址
	
	//端口号
	public static final int LOGIN_PORT = 4001;	//登陆端口
	public static final int REG_PORT = 4002; 	//注册端口
	public static final int Msg_PORT = 4003; 	//注册端口
	
	//用户名&密码等信息寄存
	public static String username;
	public static String password;
	public static String friend_json_data;		//好友列表json信息
	public static String personality_json_data;	//个人资料json信息
	public static String friend_list_data; 		//好友列表非json信息 - 仅uid
	public static String friend_online;			//在线好友json信息
	
	public static FriendListJPanel friendListJPanel;	//方便实时更新好友列表
	
	public static DatagramSocket datagramSocket_client = null;	//UDP发送接收心跳端
	
	public static Hashtable<String, FaceJPanel> list = new Hashtable<String, FaceJPanel>();	//自己的所有好友面板
	
	//解析好友json信息得到好友列表
	public static  void jiexi_friend_json_data(String friend_json_data) {
		Config.friend_json_data = friend_json_data;	
		JSONArray json = JSONArray.fromObject(friend_json_data);
		StringBuffer stringBuffer = new StringBuffer();	//存放在线好友id
		for(int i = 0;i<json.size();i++) {
				JSONObject jsonObject = (JSONObject)json.get(i);
				stringBuffer.append(jsonObject.getString("uid"));
				stringBuffer.append(",");		
		}
		friend_list_data = stringBuffer.toString();
	}
	
	//聊天窗登记
	public static Hashtable<String, Chat> chatTab = new Hashtable<String, Chat>(); 
	
	//显示聊天框
	public static void showChat(String uid,String netname,String info,String img,boolean isOnline,String qqnumber,Vector<Msg> msgs) {
		if (chatTab.get(uid) == null) {
			Chat chat = new Chat(uid, netname, info, img, isOnline,qqnumber,msgs);
			chatTab.put(uid, chat);
		}else {
			chatTab.get(uid).setAlwaysOnTop(true);
			chatTab.get(uid).setVisible(true);
		}
		chatTab.get(uid).onlineUpdate(isOnline);	//刷新在线状态
	}
	//关闭聊天框
	public static void closeChat(String uid) {
		chatTab.remove(uid);
	}
}

服务器实现

com.ym.db/DBManage.java - 数据库连接配置

package com.ym.db;

import java.sql.SQLException;

import javax.sql.DataSource;

import com.mchange.v2.c3p0.ComboPooledDataSource;
import java.sql.Connection;

/**
 * 
 * 用于连接数据库配置 - 驱动+JDBC数据库连接池 
 * @author 62473
 *
 */


public class DBmanage {

	public static final String driverName="com.mysql.jdbc.Driver";
	public static final String userName="root";
	public static final String userPwd="MySQL@123";
	public static final String dbName = "qq";
	public static final String url = "jdbc:mysql://localhost:3306/" + dbName;
	public static DataSource datasource = null;
	
	
	// 准备连接数据源C3P0
	static {
		try {
			
			ComboPooledDataSource pool = new ComboPooledDataSource();
			pool.setDriverClass(driverName);
			pool.setUser(userName);
			pool.setPassword(userPwd);
			pool.setJdbcUrl(url);
			pool.setMaxPoolSize(30);	//最大最小连接池数
			pool.setMinPoolSize(5);
			datasource = pool;
			
		} catch (Exception e) {
			// TODO: handle exception
			e.printStackTrace();
			System.out.print("数据库连接池加载失败!");
		}
	}
	
	// 通过这个方法获得Connection对象
	public static Connection getConnection() throws SQLException{
		
		return datasource.getConnection();
		
	}
		
	
}

com.ym.db/PasswordNotFoundException.java - 密码错误Exception

package com.ym.db;

public class PasswordNotFoundException extends Exception {

}

com.ym.db/StateException.java - 账户锁定Exception

package com.ym.db;


public class StateException extends Exception {

}

com.ym.db/UsernameException.java - 用户名已存在Excepiton

package com.ym.db;

public class UsernameException extends Exception {

}

com.ym.db/UsernameNotFoundException.java - 用户名不存在Exception

package com.ym.db;

public class UsernameNotFoundException extends Exception {

}

com.ym.db/UserInfo.java - 在线好友登陆信息

package com.ym.db;

// 在线好友资料信息

public class UserInfo {

	private String uid;
	private String netname;
	private String info;
	private String img;
	private String qqnumber;
	public String getQqnumber() {
		return qqnumber;
	}
	public void setQqnumber(String qqnumber) {
		this.qqnumber = qqnumber;
	}
	public String getUid() {
		return uid;
	}
	public void setUid(String uid) {
		this.uid = uid;
	}
	public String getNetname() {
		return netname;
	}
	public void setNetname(String netname) {
		this.netname = netname;
	}
	public String getInfo() {
		return info;
	}
	public void setInfo(String info) {
		this.info = info;
	}
	public String getImg() {
		return img;
	}
	public void setImg(String img) {
		this.img = img;
	}

}

com.ym.db/UserInfo2.java - 在线好友个人资料信息

package com.ym.db;

public class UserInfo2 extends UserInfo{

	private String phonenumber;
	private String email;
	private String yy;
	private String mm;
	private String dd;
	private String back;
	private String gend;
	private String realname;
	private String relation;
	private String profession;
	private String bloodtype;
	private String qqnumber;
	
	public String getQqnumber() {
		return qqnumber;
	}
	public void setQqnumber(String qqnumber) {
		this.qqnumber = qqnumber;
	}
	public String getPhonenumber() {
		return phonenumber;
	}
	public void setPhonenumber(String phonenumber) {
		this.phonenumber = phonenumber;
	}
	public String getEmail() {
		return email;
	}
	public void setEmail(String email) {
		this.email = email;
	}
	public String getYy() {
		return yy;
	}
	public void setYy(String yy) {
		this.yy = yy;
	}
	public String getMm() {
		return mm;
	}
	public void setMm(String mm) {
		this.mm = mm;
	}
	public String getDd() {
		return dd;
	}
	public void setDd(String dd) {
		this.dd = dd;
	}
	public String getBack() {
		return back;
	}
	public void setBack(String back) {
		this.back = back;
	}
	public String getGend() {
		return gend;
	}
	public void setGend(String gend) {
		this.gend = gend;
	}
	public String getRealname() {
		return realname;
	}
	public void setRealname(String realname) {
		this.realname = realname;
	}
	public String getRelation() {
		return relation;
	}
	public void setRelation(String relation) {
		this.relation = relation;
	}
	public String getProfession() {
		return profession;
	}
	public void setProfession(String profession) {
		this.profession = profession;
	}
	public String getBloodtype() {
		return bloodtype;
	}
	public void setBloodtype(String bloodtype) {
		this.bloodtype = bloodtype;
	}

}

com.ym.db/UserService.java - 数据库查询逻辑

package com.ym.db;

import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.Vector;



/**
 * 数据库查询逻辑
 * 
 * @author 黄龙士
 *
 *@throws UsernameNotFoundException:用户不存在
 *	PasswordNotFoundException 密码错误
 *	StateException 账户锁定
 *	SQLException 数据库连接失败
 */


public class UserService {

	// 登陆服务
	public String login(String qqnumber,String password) 	//可以修改参数变成多种方法形式登陆
			throws UsernameNotFoundException,PasswordNotFoundException,StateException,SQLException{
		
		Connection conn =null;
		try {
			
			conn = DBmanage.getConnection();
			PreparedStatement pst = conn.prepareStatement("SELECT * FROM users WHERE qqnumber=?");	//没有做防sql注入
			pst.setString(1, qqnumber);
			ResultSet rs = pst.executeQuery();	//查询得到的结果
			if(rs.next()) {
				if(rs.getInt("state") == 0) {	//0就是正常登陆
					
					if(rs.getString("password").equals(password)) {
						return rs.getString(1);		//登陆成功返回uid
					}else {
						throw new PasswordNotFoundException();
					}

				}else {
					throw new StateException();
				}
			}else {
				throw new UsernameNotFoundException();
			}
			
			
		} catch (SQLException e) {
			throw e;
		} finally {
			conn.close();
		}
		
	}
	
	// 获得好友列表
	public Vector<UserInfo> getFriendList(String uid) throws SQLException{
		
		Connection conn =null;
		try {
			
			conn = DBmanage.getConnection();
			PreparedStatement pst = conn.prepareStatement("select u.uid,u.img,u.netname,u.info,u.qqnumber from users u inner join friendship f on u.uid=f.friendid and f.uid=?");	//没有做防sql注入
			pst.setString(1, uid);
			ResultSet rs = pst.executeQuery();	//查询得到的结果
			Vector<UserInfo> vector = new Vector<>();
			while(rs.next()) {
				UserInfo userInfo = new UserInfo();
				userInfo.setUid(rs.getString(1));
				userInfo.setImg(rs.getString(2));
				userInfo.setNetname(rs.getString(3));
				userInfo.setInfo(rs.getString(4));
				userInfo.setQqnumber(rs.getString(5));
				vector.add(userInfo);
			}
			return vector;
		} catch (SQLException e) {
			// TODO: handle exception
			throw e;
		} finally {
			conn.close();
		}
		
	}
	
	// 个人资料查询 好友资料查询
	public UserInfo2 getPersonality(String uid) throws SQLException{

		Connection conn =null;
		try {
			
			conn = DBmanage.getConnection();
			PreparedStatement pst = conn.prepareStatement("SELECT * FROM personality where uid=?;");	//没有做防sql注入
			pst.setString(1, uid);
			ResultSet rs = pst.executeQuery();	//查询得到的结果
			UserInfo2 userInfo = new UserInfo2();
			if(rs.next()) {
				userInfo.setUid(rs.getString("uid"));
				userInfo.setNetname(rs.getString("netname"));
				userInfo.setInfo(rs.getString("info"));
				userInfo.setImg(rs.getString("img"));
				userInfo.setBack(rs.getString("back"));
				userInfo.setBloodtype(rs.getString("bloodtype"));
				userInfo.setEmail(rs.getString("email"));
				userInfo.setDd(rs.getString("dd"));
				userInfo.setMm(rs.getString("mm"));
				userInfo.setYy(rs.getString("yy"));
				userInfo.setGend(rs.getString("gend"));
				userInfo.setRelation(rs.getString("relation"));
				userInfo.setPhonenumber(rs.getString("phonenumber"));
				userInfo.setProfession(rs.getString("profession"));
				userInfo.setRealname(rs.getString("realname"));
				userInfo.setQqnumber(rs.getString("qqnumber"));
			}
			return userInfo;
		} catch (SQLException e) {
			// TODO: handle exception
			throw e;
		} finally {
			conn.close();
		}
		
	}
	
	// 注册账户
	public String regUser(String username,String password) throws UsernameException,SQLException{
		
		Connection conn =null;
		try {
			
			conn = DBmanage.getConnection();
			PreparedStatement pst = conn.prepareStatement("SELECT * FROM personality where phonenumber=? or email=?;");	//没有做防sql注入
			pst.setString(1, username);
			pst.setString(2, username);
			ResultSet rs = pst.executeQuery();	//查询得到的结果
			if(rs.next()) {
				throw new UsernameException();	// 若存在该手机/email 则报错
			}
			
			if(username.indexOf("@")>=0) {
				pst = conn.prepareStatement("insert into personality(uid,email,qqnumber) values(?,?,?)");	//更新个人资料 - 邮箱
			}else if(username.trim().length()==11){
				pst = conn.prepareStatement("insert into personality(uid,phonenumber,qqnumber) values(?,?,?)");	//更新个人资料 - 手机
			}
			String uid = System.currentTimeMillis()+"R"+(int)(Math.random()*10000);		//uid随机生成
			String qqnumber = (int)(Math.random()*100000)+""+System.currentTimeMillis()%10000;	//随机生成九位qq号
			pst.setString(1, uid);
			pst.setString(2, username);
			pst.setString(3,qqnumber);
			if (pst.executeUpdate()<=0) {
				throw new SQLException();
			}
			
			pst = conn.prepareStatement("insert into users(uid,password,createtime,qqnumber) values(?,?,sysdate(),?)");	//更新用户表
			pst.setString(1, uid);
			pst.setString(2, password);
			pst.setString(3, qqnumber);
			if (pst.executeUpdate()<=0) {
				throw new SQLException();
			}
			
			int logid = (int)(Math.random()*1000000);
			pst = conn.prepareStatement("insert into friendship(logid,uid,friendid,createtime) values(?,?,?,sysdate())");	//默认跟10001账号是好友
			pst.setInt(1, logid);
			pst.setString(2, uid);
			pst.setString(3, "10001");
			if (pst.executeUpdate()<=0) {
				throw new SQLException();
			}
			logid = (int)(Math.random()*1000000);
			pst = conn.prepareStatement("insert into friendship(logid,uid,friendid,createtime) values(?,?,?,sysdate())");	//默认跟10001账号是好友
			pst.setInt(1, logid);
			pst.setString(2, "10001");
			pst.setString(3, uid);
			if (pst.executeUpdate()<=0) {
				throw new SQLException();
			}
			
			return qqnumber;
		} catch (SQLException e) {
			throw e;
		} finally {
			conn.close();
		}
		
	}
	
	public String recomePassword(String qqnumber,String username) throws UsernameException,SQLException{		
		Connection conn =null;
		try {
			conn = DBmanage.getConnection();
			PreparedStatement pst = conn.prepareStatement("SELECT * FROM personality where (phonenumber=? or email=?) and qqnumber=?;");
			pst.setString(1, username);
			pst.setString(2, username);
			pst.setString(3, qqnumber);
			ResultSet rs = pst.executeQuery();	//查询得到的结果
			String password = null;
			if(rs.next()) {
				pst = conn.prepareStatement("SELECT * FROM users where qqnumber=?;");
				pst.setString(1, qqnumber);
				rs = pst.executeQuery();	//查询得到的结果
				rs.next();
				password = rs.getString("password");
			}else {
				throw new UsernameException();	// 若不存在该qqnumber or qq号与验证账户不匹配
			}
			return password;	
		} catch (SQLException e) {
			throw e;
		} finally {
			conn.close();
		}
	}
	
//	public String searchFriend(String qqnumber) {
//		
//		Connection conn =null;
//		try {
//			conn = DBmanage.getConnection();
//			PreparedStatement pst = conn.prepareStatement("SELECT * FROM personality where (phonenumber=? or email=?) and qqnumber=?;");
//			pst.setString(1, username);
//			pst.setString(2, username);
//			pst.setString(3, qqnumber);
//			ResultSet rs = pst.executeQuery();	//查询得到的结果
//			String username = null;
//			if(rs.next()) {
//				pst = conn.prepareStatement("SELECT * FROM users where qqnumber=?;");
//				pst.setString(1, qqnumber);
//				rs = pst.executeQuery();	//查询得到的结果
//				rs.next();
//				password = rs.getString("password");
//			}else {
//				throw new UsernameException();	// 若不存在该qqnumber or qq号与验证账户不匹配
//			}
//			return username;	
//		} catch (SQLException e) {
//			throw e;
//		} finally {
//			conn.close();
//		}
//		
//	}
	
	public static void main(String[] args) {
		try {
			String test = new UserService().recomePassword("624730725","17792593092");
			System.out.print(test);
		} catch (UsernameException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		} catch (SQLException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
	}
}

com.ym.server/Loginserver.java - 登陆服务器

package com.ym.server;

import java.io.InputStream;
import java.io.OutputStream;
import java.net.ServerSocket;
import java.net.Socket;
import java.sql.SQLException;
import java.util.Vector;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

import com.ym.db.PasswordNotFoundException;
import com.ym.db.StateException;
import com.ym.db.UserInfo2;
import com.ym.db.UserService;
import com.ym.db.UsernameNotFoundException;

import net.sf.json.JSONArray;
import net.sf.json.JSONObject;


/**
 * 登陆服务器 主要负责登陆
 * 
 * @author 黄龙士
 *
 */

public class Loginserver implements Runnable{

	private Socket socket = null;
	
	public Loginserver(Socket socket) {
		// TODO Auto-generated constructor stub
		
		this.socket = socket;
	}
	
	@Override
	public void run() {		//线程方法
		// TODO Auto-generated method stub
		
		//登陆操作
		String uid = null;	//数据库登陆成功后会返回uid
		InputStream in = null;
		OutputStream out = null;
		
		try {
			
			in = socket.getInputStream();
			out = socket.getOutputStream();
			
			//等待客户端信息 - 通用写法 其他等待消息也可以这样写	{username:,password:}
			byte[] bytes =new byte[1024];
			int len = in.read(bytes);
			String json_str = new String(bytes,0,len);
			
			//传入消息
			JSONObject json = JSONObject.fromObject(json_str);	//解析json文本
			String username = json.getString("username");	//qqnumber
			String password = json.getString("password");	//password
			
			try {
				Long.parseLong(username);	//qqnumber全由数字组成
			} catch (NumberFormatException e) {
				// TODO: handle exception
				out.write("{"state":4,"msg":"未知错误!"}".getBytes());
				out.flush();
				return;
			}
			
			try {		
				
				uid = new UserService().login(username, password);	//此处username就是qqnumber
												//password passward 单词拼写有些地方写错了
			
//				System.out.print(uid);	//debug用
				
				//登陆成功后加入在线用户队列
				UserOnlineList.getUserOnlineList().regOnline(uid, password, socket, username);
				
				out.write("{"state":0,"msg":"登陆成功!"}".getBytes());		
				out.flush();
				
				while(true) {	//登陆之后陆陆续续接受客户端发送的请求
					
					bytes =new byte[2048];
					len = in.read(bytes);		
					String command = new String(bytes,0,len);
					
					
					if (command.equals("U0001")) {	//更新好友列表
						
						Vector<com.ym.db.UserInfo> userInfos = new UserService().getFriendList(uid);
						out.write(JSONArray.fromObject(userInfos).toString().getBytes("utf-8"));		
						out.flush();
						
					} else if (command.equals("U0002")) {	//更新好友在线状态
						
						out.write(1);	//发送好友列表更新消息
						out.flush();
						
						len = in.read(bytes);	// 获得好友id列表
						String str = new String(bytes,0,len);	//10001,10002,...
						String[] ids = str.split(",");
						StringBuffer stringBuffer = new StringBuffer();	//存放在线好友id
						for(String string:ids) {
							if (UserOnlineList.getUserOnlineList().isUserOnline(string)) {
								stringBuffer.append(string);
								stringBuffer.append(",");
							}
						}
						
						if(stringBuffer.length()==0) {	//0表示只有自己在线 (在线好友id如果显示自己的话此处就改成0)
							//没有好友在线
							out.write("notFound".getBytes("utf-8"));
							out.flush();
						}else {
							//回执好友在线列表
							out.write(stringBuffer.toString().getBytes("utf-8"));
							out.flush();
						}
						
					} else if (command.equals("U0003")) {	//更新个人资料
						
						UserInfo2 userInfo2 = new UserService().getPersonality(uid);
						out.write(JSONArray.fromObject(userInfo2).toString().getBytes("utf-8"));
						out.flush();
						
					} else if (command.equals("E0001")){	//修改个人资料

					} else if (command.equals("U0004")) {	// 查找qq号
						
						out.write(1);	//握手
						out.flush();
						
						System.out.println("查找qq号中");				/// debug
						
						len = in.read(bytes);	
						String str_find = new String(bytes,0,len);	//"username";
						
						username = new UserService().login(username, password);		//
						
						
						
						
					} else if (command.equals("EXIT")) {	//退出登陆
						UserOnlineList.getUserOnlineList().logout(uid);
						return;
					}
					
				}
				
				
				
			} catch (UsernameNotFoundException e) {
				out.write("{"state":2,"msg":"用户名不存在!"}".getBytes());
				out.flush();
				return;
			} catch (PasswordNotFoundException e) {
				out.write("{"state":1,"msg":"密码错误!"}".getBytes());
				out.flush();
				return;
			} catch (StateException e) {
				out.write("{"state":3,"msg":"账户被锁定,请联系客服!"}".getBytes());
				out.flush();
				return;
			} catch (SQLException e) {
				out.write("{"state":4,"msg":"未知错误!"}".getBytes());
				out.flush();
				return;
			}
			
		} catch (Exception e) {
			e.printStackTrace();
		}finally {
			try {	//关闭连接
				UserOnlineList.getUserOnlineList().logout(uid);
				in.close();
				out.close();
				socket.close();
			} catch (Exception e2) {
			}
		}
		
		
	}

	public static void openServer() throws Exception{
		ExecutorService execute = Executors.newFixedThreadPool(2000);	//创建线程池	- 再多线程就要集群了
		
		ServerSocket server = new ServerSocket(4001);	//注册开启TCP:4001这个端口 - 用于登陆业务 
		
		while(true) {	//死循环目的:可以无限服务
			
			Socket socket = server.accept();
			socket.setSoTimeout(10000);		//超过10s自动断开
			execute.execute(new Loginserver(socket));	//开启1个线程
			
			
		}
	}
	
	public static void main(String[] args) {
		try {
			openServer();
		} catch (Exception e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
	}
}

com.ym.server/ResServer.java - 注册服务器

package com.ym.server;

import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.ServerSocket;
import java.net.Socket;
import java.sql.SQLException;
import java.util.HashMap;
import java.util.Random;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

import com.ym.db.UserService;
import com.ym.db.UsernameException;

import net.sf.json.JSONObject;

/**
 * 	注册服务器	
 * 
 * @author 黄龙士
 *
 */


public class ResServer implements Runnable{

	private Socket socket = null;
	
	public ResServer(Socket socket) {
		// TODO Auto-generated constructor stub
		
		this.socket = socket;
	}
	
	private static HashMap<String, String> hashMap = new HashMap<String, String>();
	
	
	@Override
	public void run() {
		// TODO Auto-generated method stub

		InputStream in = null;
		OutputStream out = null;
		
		try {
			
			in = socket.getInputStream();
			out = socket.getOutputStream();
			
			//等待客户端信息 - 通用写法 其他等待消息也可以这样写	
			byte[] bytes =new byte[1024];
			int len = in.read(bytes);
			String json_str = new String(bytes,0,len);
			
			//传入消息
			JSONObject json = JSONObject.fromObject(json_str);	//解析json文本
			String type = json.getString("type");
			
			if (type.equals("code")) {	//请求验证码
				String username = json.getString("username");	//用户名 - 手机/email
				
				//随机生成验证码
				StringBuffer code = new StringBuffer();
				Random random = new Random();
				for (int i = 0; i < 6; i++) {		//验证码随机生成
					code.append(random.nextInt(10));
				}

				hashMap.put(username, code.toString());		//保存用户-验证码键值对
				
				if (username.trim().length()==11 && username.indexOf("@")<=-1) {	//手机号
					try {
						Long.parseLong(username);	//手机号全由数字组成
						
						SendCode.send(username, code.toString());
						
						out.write("{"state":0,"msg":"验证码发送至手机成功!" }".getBytes());
						out.flush();
						
					} catch (Exception e) {
						out.write("{"state":1,"msg":"验证码发送手机失败!" }".getBytes());
						out.flush();
					}
				}else {
					if(username.indexOf("@")>=0) {	//邮箱号
						SendCode.sendEmail(username, code.toString());
						
						out.write("{"state":0,"msg":"验证码发送至邮箱成功!" }".getBytes());
						out.flush();
						
					}else {
						out.write("{"state":1,"msg":"验证码发送邮箱失败!" }".getBytes());
						out.flush();
					}
				}
			}else if (type.equals("reg")) {	//请求注册
				
				String username = json.getString("username");
				String password = json.getString("password");
				String code = json.getString("code");
				String code_right = hashMap.get(username);
				
				if(code_right!=null) {
					
					hashMap.remove(username);		//正确与否都要删掉
				}
				
				if (code_right.equals(code)) {
					
					try {
						String qqnumber = new UserService().regUser(username, password);
						out.write(("{"state":0,"msg":"恭喜您!注册成功!" ,"qqnumber":""+qqnumber+""}").getBytes());
						out.flush();
					} catch (SQLException e) {
						out.write("{"state":3,"msg":"未知错误!" }".getBytes());
						out.flush();
						return;
					} catch (UsernameException e) {
						out.write("{"state":2,"msg":"用户名已存在!" }".getBytes());
						out.flush();
						return;
					}
					
				} else {
					out.write("{"state":1,"msg":"验证码错误,请重新获取!" }".getBytes());
					out.flush();
				}
			}else if (type.equals("recome")) {
				String username = json.getString("username");
				String qqnumber = json.getString("qqnumber");
				String code = json.getString("code");
				String code_right = hashMap.get(username);
				if(code_right!=null) {
					hashMap.remove(username);		//正确与否都要删掉
				}
				
				if (code_right.equals(code)) {
					try {
						String password = new UserService().recomePassword(qqnumber,username);
						out.write(("{"state":0,"msg":"找回密码成功!","password":""+password+""}").getBytes());
						out.flush();
					} catch (SQLException e) {
						out.write("{"state":3,"msg":"未知错误!" }".getBytes());
						out.flush();
						return;
					} catch (UsernameException e) {
						out.write("{"state":2,"msg":"需要找回的用户名不存在或者与QQ号不匹配!" }".getBytes());
						out.flush();
						return;
					}
				} else {
					out.write("{"state":1,"msg":"验证码错误,请重新获取!" }".getBytes());
					out.flush();
				}
			}
			
		} catch (Exception e) {
			
			e.printStackTrace();
		}finally {
			try {
				in.close();
				out.close();
			} catch (IOException e) {
				e.printStackTrace();
			}
		}
		
	}
	
	public static void openServer() throws Exception{
		ExecutorService service = Executors.newFixedThreadPool(2000);	//创建线程池	- 再多线程就要集群了
		
		ServerSocket server = new ServerSocket(4002);	//注册开启TCP:4002这个端口 - 用于注册业务 
		
		while(true) {	//死循环目的:可以无限服务
			
			Socket socket = server.accept();
			socket.setSoTimeout(10000);		//超过10s自动断开
			service.execute(new ResServer(socket));	//开启1个线程
			
			
		}
	}

}

com.ym.server/SendCode.java - 发送验证码


import org.apache.commons.mail.HtmlEmail;

import com.aliyuncs.DefaultAcsClient;
import com.aliyuncs.IAcsClient;
import com.aliyuncs.dysmsapi.model.v20170525.SendSmsRequest;
import com.aliyuncs.dysmsapi.model.v20170525.SendSmsResponse;
import com.aliyuncs.exceptions.ClientException;
import com.aliyuncs.profile.DefaultProfile;
import com.aliyuncs.profile.IClientProfile;


public class SendCode {

	//产品名称:云通信短信API产品,开发者无需替换
    static final String product = "Dysmsapi";
    //产品域名,开发者无需替换
    static final String domain = "dysmsapi.aliyuncs.com";

    // TODO 此处需要替换成开发者自己的AK(在阿里云访问控制台寻找)
    static final String accessKeyId = "开发者自己的AK";
    static final String accessKeySecret = "开发者自己的Secret";

    public static boolean send(String phoneNumber,String code) throws ClientException {		//发送手机验证码

    	try {
            //可自助调整超时时间
            System.setProperty("sun.net.client.defaultConnectTimeout", "10000");	//10分钟
            System.setProperty("sun.net.client.defaultReadTimeout", "10000");

            //初始化acsClient,暂不支持region化
            IClientProfile profile = DefaultProfile.getProfile("cn-hangzhou", accessKeyId, accessKeySecret);
            DefaultProfile.addEndpoint("cn-hangzhou", "cn-hangzhou", product, domain);
            IAcsClient acsClient = new DefaultAcsClient(profile);

            //组装请求对象-具体描述见控制台-文档部分内容
            SendSmsRequest request = new SendSmsRequest();
            //必填:待发送手机号
            request.setPhoneNumbers(phoneNumber);	//待发送手机号
            //必填:短信签名-可在短信控制台中找到
            request.setSignName("黄龙士大作业个人通讯");		//签名
            //必填:短信模板-可在短信控制台中找到
            request.setTemplateCode("SMS_177548966");		//短信模板
            //可选:模板中的变量替换JSON串,如模板内容为"亲爱的会员,您的验证码为${code}"时,此处的值为
            request.setTemplateParam("{"code":""+code+""}");

            //选填-上行短信扩展码(无特殊需求用户请忽略此字段)
            //request.setSmsUpExtendCode("90997");

            //可选:outId为提供给业务方扩展字段,最终在短信回执消息中将此值带回给调用者
            //request.setOutId("yourOutId");

            //hint 此处可能会抛出异常,注意catch
            SendSmsResponse sendSmsResponse = acsClient.getAcsResponse(request);	//发送失败可能就是阿里云账户没钱了

            return true;
		} catch (Exception e) {
			e.printStackTrace();
			return false;
		}
    }
    
    public static boolean sendEmail(String useremail,String code) {
			
    	try {
			
        	HtmlEmail email = new HtmlEmail();
        	email.setHostName("smtp.163.com");
        	email.setCharset("UTF-8");
        	email.addTo(useremail);
        	email.setFrom("你的163邮箱账号","黄龙人个人即时通讯系统");
        	email.setAuthentication("你的163邮箱账号", "你的163邮箱授权码");    //是授权码而不是密码
        	email.setSubject("黄龙士个人即时通讯系统注册验证");
        	email.setMsg("尊敬的用户,您的注册会员动态密码为:"+code+",请勿泄漏于他人!");
        	email.send();
    		return true;
		} catch (Exception e) {
			e.printStackTrace();
			return false;
		}

	}
    
    
	
}


com.ym.server/UDPMessageServer.java - UDP消息中转服务器

package com.ym.server;

import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.net.InetAddress;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

import net.sf.json.JSONObject;

/**
 * 
 * UDP消息中转服务器
 * @author 黄龙士
 *
 */

public class UDPMessageServer implements Runnable{

	private DatagramPacket packet = null;
	private static DatagramSocket datagramSocket = null; 
	
	public UDPMessageServer(DatagramPacket packet){		
		this.packet = packet;
	}
	
	@Override
	public void run() {
		try {
			
			String json_str = new String(packet.getData(),0,packet.getLength());	//报文转string
			JSONObject json = JSONObject.fromObject(json_str);	//解析json
			
			if(json.getString("type").equals("reg")) {	//处理心跳包
				
				String MyUID = json.getString("myUID");
				UserOnlineList.getUserOnlineList().updateOnlineUDP(MyUID, packet.getAddress().getHostAddress()
						, packet.getPort());	//更新最新的IP和端口号
				
				System.out.println("有注册消息发过来:"+json_str);	
				
			}else if (json.getString("type").equals("msg") || json.getString("type").equals("ack")) {	//处理信息转发	
				
				String MyUID = json.getString("myUID");
				String toUID = json.getString("toUID");
				UserOnlineList.getUserOnlineList().updateOnlineUDP(MyUID, packet.getAddress().getHostAddress()
						, packet.getPort());	//更新最新的IP和端口号
				
				//接受人信息
				UserInfo toUserInfo = UserOnlineList.getUserOnlineList().getOnlineUserInfo(toUID);
				
				//转发给接收人的数据包 - 不能转发未上线的好友,所有发送前要判断对方是否在线
				DatagramPacket datagramPacket = new DatagramPacket(packet.getData(), packet.getLength(),
						InetAddress.getByName(toUserInfo.getUdpip()),toUserInfo.getUdpport());
				
				//发送数据包
				datagramSocket.send(datagramPacket);
					
			}
			
		} catch (Exception e) {
			e.printStackTrace();
		}
		
	}

	//启动服务器
	public static void openServer() throws Exception{
		ExecutorService service = Executors.newFixedThreadPool(2000);	//创建线程池	- 再多线程就要集群了
		datagramSocket = new DatagramSocket(4003);	////注册开启UDP:4003这个端口 - 用于消息转发业务 
		while(true) {
			
			try {
				//等待报文传输
				byte[] bytes = new byte[1024*32];		//UDP报文最大65535字节≈64KB=1024*64 - 1024*10应该足够了
				DatagramPacket datagramPacket = new DatagramPacket(bytes, bytes.length);
				datagramSocket.receive(datagramPacket);
				
				//报文收到后创建一个线程处理
				service.execute(new UDPMessageServer(datagramPacket));
			} catch (Exception e) {
				e.printStackTrace();
			}
	
		}
	}
	
}

com.ym.server/UserInfo.java - 用户在线队列的用户信息

package com.ym.server;


import java.net.Socket;


// 用户队列的UserInfo
public class UserInfo {

	private String uid;
	private String passward;
	private Socket socket;
	private String udpip;
	private int udpport;
	private String qqnumber;
	public String getUid() {
		return uid;
	}
	public void setUid(String uid) {
		this.uid = uid;
	}
	public String getPassward() {
		return passward;
	}
	public void setPassward(String passward) {
		this.passward = passward;
	}
	public Socket getSocket() {
		return socket;
	}
	public void setSocket(Socket socket) {
		this.socket = socket;
	}
	public String getUdpip() {
		return udpip;
	}
	public void setUdpip(String udpip) {
		this.udpip = udpip;
	}
	public int getUdpport() {
		return udpport;
	}
	public void setUdpport(int udpport) {
		this.udpport = udpport;
	}
	public String getQqnumber() {
		return qqnumber;
	}
	public void setQqnumber(String qqnumber) {
		this.qqnumber = qqnumber;
	}
	
	
	
}

com.ym.server/UserOnlineList.java - 用户在线队列

package com.ym.server;

import java.net.Socket;
import java.util.HashMap;
import java.util.Set;



/**
 * 	在线用户列表
 * 
 * @author 黄龙士
 *
 */

public class UserOnlineList {

	private  UserOnlineList() {		//单例类 - 饿汉实现
		// TODO Auto-generated constructor stub
	}
	
	private static UserOnlineList userOnlineList = new UserOnlineList();
	
	public static UserOnlineList getUserOnlineList() {
		return userOnlineList;
	}
	
	//在线账户记录在字典里 - 方便查找 || 也可以放在linkedlist链表队列里
	private HashMap<String, UserInfo> hashMap = new HashMap<String,UserInfo>();	//前面id后面值
	
	//注册在线用户
	public void regOnline(String uid,String passward,Socket socket,String qqnumber) {
		
		//判断是否已经在线;抢占登陆
		UserInfo userInfo = hashMap.get(uid);
		if(userInfo!=null) {
			try {
				try {
					userInfo.getSocket().getOutputStream().write(4);  	//输出编号4提示
				} catch (Exception e) {
					// TODO: handle exception
				}
				userInfo.getSocket().close();		//抢占前者下线
			} catch (Exception e) {
				// TODO: handle exception
			}
			
		}
		
		userInfo = new UserInfo();		
		userInfo.setSocket(socket);
		userInfo.setUid(uid);
		userInfo.setPassward(passward);			
		userInfo.setQqnumber(qqnumber);
		hashMap.put(uid, userInfo);	//登记在线	
	}
	
	public void updateOnlineUDP(String uid,String udpip,int udpport) throws NullPointerException{		//后续再进行UDP信息更新
		
		UserInfo userInfo = hashMap.get(uid);
		userInfo.setUdpip(udpip);
		userInfo.setUdpport(udpport);
		
	}
	
	public boolean isUserOnline(String uid) {		//判断用户是否在线
		return hashMap.containsKey(uid);
	}
	
	public UserInfo getOnlineUserInfo(String uid) {		//得到指定在线用户信息
		return hashMap.get(uid);
	}
	
	public void logout(String uid) {		//下线
		hashMap.remove(uid);
	}
	
	public Set<String> getOnlineUserInfos(){	//得到在线用户列表 - 具体数据从数据库调用
		return hashMap.keySet();
	}
	

}

com.ym.server/Start.java - 开启所有服务器

package com.ym.server;

public class Start {


	public static void main(String[] args) {

		new Thread() {	//登陆线程
			
			public void run() {
				
				try {
					System.out.println("登陆服务器启动成功!");
					Loginserver.openServer();
				} catch (Exception e) {
					// TODO Auto-generated catch block
					e.printStackTrace();
				}
			}
			
		}.start();
		
		new Thread() {	//注册线程
			
			public void run() {
				
				try {
					System.out.println("注册服务器启动成功!");
					ResServer.openServer();
				} catch (Exception e) {
					// TODO Auto-generated catch block
					e.printStackTrace();
				}
				
			}
		}.start();
		
		new Thread() {	//消息线程
			
			public void run() {
				
				try {
					System.out.println("信息中转服务器启动成功!");
					UDPMessageServer.openServer();
				} catch (Exception e) {
					// TODO Auto-generated catch block
					e.printStackTrace();
				}
				
			}
		}.start();
		
	}

}




遇到的问题

  • 如何解决好友列表?

    • 单独将好友列表做成一个JPanelList,里面每一个好友都做成JPanel然后加入进去,在每个JPanel里面加入一个线程去检测消息,形成好友抖动等效果。
  • 如何实现记住密码,自动登陆,QQ号自动保存,账号头像记录等登陆界面的显示?

    • 借鉴腾讯自身的做法,每次登陆成功后,都将这些信息写到一个本地文件里面,下次启动客户端即使离线也能看到上次登录成功的账户信息。
  • 如何实现抢占登陆?

    • 做一个在线用户队列,用Hashtable实现,线程安全,每次登陆后进行检测,如果存在则抢占前者,前者返回json显示被人登陆,强制退出。
  • Java9以上版本JavaSE不使用了,无法按照原来的做法使用HTMLEmail包发送邮件?

  • 发邮件报错535 Error:authentication failed&553 authentication is required?

    • 邮箱发送模块里面填写的password不是密码,而是163代理的授权码。

参考案例

< QQ登录界面实现(JAVA)>

< Java项目案例之我的JavaQQ聊天工具 >

< 阿里(大于)发送手机验证码 >

原文地址:https://www.cnblogs.com/ymjun/p/12020504.html