< JAVA - 大作業(2)仿qq即時通訊軟件 >
背景
- JAVA上機大作業:設計一個仿qq即時通訊軟件
任務簡要敘述:設計一款仿QQ的個人用戶即時通訊軟件,能夠實現注冊,登陸,與好友聊天等功能。要求使用GUI界面設計,網絡通信,數據庫連接,泛型容器等技術。
- 注意:
-
參考該代碼時注意修改Config.java中的IP地址為自己(服務器工程)的IP地址
-
參考該代碼時注意修改DBManage.java中的數據庫連接內容為自己的數據庫連接
-
客戶端運行Login.java啟動客戶端;服務器運行Start.java啟動服務器
-
客戶端/服務端都有相應需要導入的額外包,可以在如下網站搜索需要的jar包:MvnJar
-
阿里大於發送短信驗證碼需要自己去注冊賬戶填寫自己的AK;163郵箱同理需要對應修改
-
需求分析
-
客戶端:
- 注冊賬戶:使用手機或者email注冊,要求使用驗證碼驗證手機號或者email再注冊。
- 找回密碼:使用手機/email找回密碼,要求使用驗證碼驗證是否本人操作再找回
- 登陸賬戶:使用系統自動生成的qq號與自己設置的密碼進行登陸,能夠記住密碼與自動登陸,設置登陸狀態,保留多賬號登陸,二維碼登陸的接口。
- 主界面:好友列表/群組列表/常用聯系人,可以被搶占下線
- 聊天框:與好友聊天,字體設置,抖動好友,發送文件
- 個人資料:查看修改個人資料
- 搜索框:搜索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("\u6CE8\u518C\u8D26\u6237");
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("\u624B\u673A/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(" \u8F93\u5165\u5BC6\u7801");
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(" \u786E\u5B9A\u5BC6\u7801");
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("\u53D1\u9001\u9A8C\u8BC1");
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("\u9000\u51FA");
btn_exit.setBounds(15, 15, 100, 25);
panelS.add(btn_exit);
btn_register = new JButton("\u6CE8\u518C\u8D26\u6237");
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("\u627E\u56DE\u5BC6\u7801");
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("\u8F93\u5165 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("\u624B\u673A/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("\u53D1\u9001\u9A8C\u8BC1");
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("\u627E\u56DE\u5BC6\u7801");
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("\u5B8C\u5584\u4E2A\u4EBA\u8D44\u6599");
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("\u9EC4\u9F99\u58EB"); //設置名稱
labelname.setFont(new Font("微軟雅黑", Font.BOLD, 18));
labelname.setBounds(72, 6, 180, 35);
panelNorth.add(labelname);
labelinfo = new JLabel("\u9EC4\u9F99\u58EB\u7684qq\u7B7E\u540D"); //設置個性簽名
labelinfo.setToolTipText("\u4E2A\u6027\u7B7E\u540D\u66F4\u65B0");
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("\u8BBE\u7F6E");
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("\u9000\u51FA");
btn_exit.setBounds(80, 20, 60, 20);
panelSouth.add(btn_exit);
JButton btn_search = new JButton("\u67E5\u627E");
btn_search.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
new Select();
}
});
btn_search.setToolTipText("\u67E5\u627E");
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 = "\n" + this.netname + "\t"+ new Date().toLocaleString() + "\n" +msg.getMsg() + "\n";
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 = "\n" + JSONObject.fromObject(Config.personality_json_data).getString("netname")+"\t"
+ new Date().toLocaleString() + "\n" +msg.getMsg() + "\n";
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("\u5B8C\u5584\u4E2A\u4EBA\u8D44\u6599");
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("\u5B57\u4F53");
btn_font.setToolTipText("\u5B57\u4F53");
btn_font.setLocation(3, 4);
btn_font.setSize(67, 23);
panel_1.add(btn_font);
JButton btn_zhendong = new JButton("\u6296\u52A8");
btn_zhendong.setToolTipText("\u5411\u597D\u53CB\u53D1\u9001\u7A97\u53E3\u6296\u52A8");
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("\u5173\u95ED");
btn_close.setToolTipText("\u5173\u95ED");
btn_close.setBounds(671, 5, 67, 23); //738
panel_2.add(btn_close);
JButton btn_send = new JButton("\u53D1\u9001");
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("Enter\u952E\u53D1\u9001\u6D88\u606F");
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("\u4E2A\u4EBA\u8D44\u6599");
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("\u8BBE\u7F6E\u5934\u50CF");
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("\u9EC4\u9F99\u58EB");
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("\u9EC4\u9F99\u58EB\u7684\u4E2A\u6027\u7B7E\u540D");
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("\u771F\u5B9E\u59D3\u540D");
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("\u51FA\u751F\u5E74\u6708");
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("\u60C5\u611F\u5173\u7CFB");
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("\u4E2A\u4EBA\u8BF4\u660E");
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("\u4E2A\u4EBA\u8D44\u6599");
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("\u4FDD\u5B58");
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("\u67E5\u8BE2\u597D\u53CB");
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("\u6635\u79F0");
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("\u67E5\u8BE2");
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包發送郵件?
- 需要導入一些其他包,具體做法可參考:[Exception in thread "main" java.lang.NoClassDefFoundError: javax/activation/DataSource](https://github.com/highsource/jsonix-schema-compiler/issues/81 "Exception in thread "main" java.lang.NoClassDefFoundError: javax/activation/DataSource")
-
發郵件報錯535 Error:authentication failed&553 authentication is required?
- 郵箱發送模塊里面填寫的password不是密碼,而是163代理的授權碼。