1. 程式人生 > >JAVA入門到精通-第92講-山寨QQ專案6-多對多的聊天

JAVA入門到精通-第92講-山寨QQ專案6-多對多的聊天

QQ聊天專案演示-多對多的聊天 流同時併發的異常;
伺服器中轉 2/3號只有一個連線;
接收資訊:while迴圈不停地讀取;
當1號點開兩個視窗時,2/3會共同去爭取這個socket; 會出現流同時併發異常;
Socket處理成static不太合理;
啟動一個視窗就會佔據Socket不放;
------------- -實現真正的多人聊天,互相不出現錯誤
-每一個登入的賬號獨享一份socket -去掉static 當一個連結達成的時候, 啟動執行緒, 和伺服器保持通訊的狀態; 做一個類來管理客戶端這邊的執行緒;
-新的賬號登入,開一個執行緒ClientConServerThread
-管理執行緒的類,放入到一個HashMap裡面:ManageClientConServerThread;
HashMap<k,v> k:使用者賬號,某個賬號獨享的執行緒 v:ClientConServerThread


----------------- -當賬號登入成功,建立執行緒,把執行緒放入HashMap



-------------------- Qq聊天介面沒有必要成為一個執行緒;本身是不合理的;
通過 管理執行緒的類,取得了執行緒,通過執行緒取得socket, 通過socket,取得了輸出流:getOutputStream;
---------------------- 給每一個登入賬號分配執行緒;
把通訊的任務交給執行緒類; ---------------------
-多對多聊天 經測試,後臺傳送訊息正常;
--------------- -顯示後臺的訊息到各自的聊天介面

QqChat.java]

110 110   1
/**
2
 * 這是與好友聊天的介面
3
 * 因為客戶端,要處於讀取的狀態,因此我們把它做成一個執行緒
4
 */
5
package com.qq.client.view;
6
 
7
import java.awt.event.ActionEvent;
8
import java.awt.event.ActionListener;
9
import java.io.IOException;
10
import java.io.ObjectInputStream;
11
import java.io.ObjectOutputStream;
12
import java.util.Date;
13
import javax.swing.ImageIcon;
14
import javax.swing.JButton;
15
import javax.swing.JFrame;
16
import javax.swing.JPanel;
17
import javax.swing.JScrollPane;
18
import javax.swing.JTextArea;
19
import javax.swing.JTextField;
20
import com.qq.client.model.QqClientConServer;
21
import com.qq.client.tools.ManageClientConServerThread;
22
import com.qq.common.Message;
23
import com.qq.common.MessageType;
24
 
25
public class QqChat extends JFrame implements ActionListener {
26
    JTextArea jta;
27
    JTextField jtf;
28
    JButton jb;
29
    JPanel jp;
30
    JScrollPane jsp;
31
    String ownerId;
32
    String friendId;
33
 
34
    // public static void main(String[] args) {
35
    // new QqChat("小四");
36
    // }
37
 
38
    // 建構函式
39
    public QqChat(String friendId, String ownerId) {
40
        this.ownerId = ownerId;
41
        this.friendId = friendId;
42
 
43
        jta = new JTextArea();
44
        jtf = new JTextField(15);
45
        jb = new JButton("傳送");
46
        jb.addActionListener(this);
47
        jp = new JPanel();
48
        jp.add(jtf);
49
        jp.add(jb);
50
        jsp = new JScrollPane(jta);
51
 
52
        this.add(jsp, "Center");
53
        this.add(jp, "South");
54
 
55
        this.setTitle(ownerId + " 正在與 " + friendId + " 聊天");
56
        this.setSize(350, 300);
57
        this.setIconImage((new ImageIcon("image/qie.jpg")).getImage());
58
        this.setLocationRelativeTo(null);
59
        this.setVisible(true);
60
    }
61
 
62
    // 寫一個方法,讓它顯示訊息
63
    public void showMessage(Message m){
64
        String info=m.getSender()+" 對 "+m.getGetder()+" 說:"+m.getCon()+"\r\n";
65
        jta.append(info);
66
    }
67
 
68
    @Override
69
    public void actionPerformed(ActionEvent e) {
70
        if (e.getSource() == jb) {
71
            // 如果使用者點選了傳送按鈕
72
            Message m = new Message();
73
            m.setMesType(MessageType.message_comm_mes);
74
            m.setSender(this.ownerId);
75
            m.setGetder(this.friendId);
76
            m.setCon(this.jtf.getText());
77
            m.setSendTime(new Date().toString());
78
            jta.append(ownerId + " 對 " + friendId + " 說:" + jtf.getText()
79
                    + "\r\n");
80
            jtf.setText("");
81
            // 傳送給伺服器
82
            try {
83
                ObjectOutputStream oos = new ObjectOutputStream(
84
                        ManageClientConServerThread
85
                                .getClientConServerThread(ownerId).getS()
86
                                .getOutputStream());
87
                oos.writeObject(m);
88
            } catch (Exception e1) {
89
                e1.printStackTrace();
90
            }
91
        }
92
    }
93
 
94
    // @Override
95
    // public void run() {
96
    // while(true){
97
    // //讀取[如果讀不到就等待]
98
    // try {
99
    // ObjectInputStream ois=new
100
    // ObjectInputStream(QqClientConServer.s.getInputStream());
101
    // Message m=(Message)ois.readObject();
102
    // //顯示出來
103
    // String info=m.getSender()+" 對 "+m.getGetder()+" 說:"+m.getCon()+"\r\n";
104
    // jta.append(info);
105
    // } catch (Exception e) {
106
    // e.printStackTrace();
107
    // }
108
    // }
109
    // }
110
}

*******************************************************************************

com.qq.client.model

[QqClienConServer.java]

54 54   1
/**
2
 * 這是客戶端連線伺服器的後臺
3
 * 功能:客戶端與伺服器進行資料互動
4
 */
5
package com.qq.client.model;
6
 
7
import java.io.ObjectInputStream;
8
import java.io.ObjectOutputStream;
9
import java.net.Socket;
10
import com.qq.client.tools.ClientConServerThread;
11
import com.qq.client.tools.ManageClientConServerThread;
12
import com.qq.common.Message;
13
import com.qq.common.User;
14
 
15
public class QqClientConServer {
16
    public Socket s;
17
   
18
    //傳送第一次請求
19
    public boolean sendLoginInfoToServer(Object o){
20
        boolean b=false;
21
       
22
        try {
23
            s=new Socket("127.0.0.1",9999);
24
            ObjectOutputStream oos=new ObjectOutputStream(s.getOutputStream());
25
            oos.writeObject(o);
26
           
27
            ObjectInputStream ois=new ObjectInputStream(s.getInputStream());
28
            Message ms=(Message)ois.readObject();
29
            //這裡就是驗證使用者登入的地方
30
            if(ms.getMesType().equals("1")){
31
                b=true;
32
                //就建立一個該qq號和伺服器端保持通訊連線的執行緒
33
                ClientConServerThread ccst=new ClientConServerThread(s);
34
                //啟動該通訊執行緒
35
                ccst.start();
36
               
37
                ManageClientConServerThread.addClientConServerThread(((User)o).getUserId(), ccst);
38
            }
39
        } catch (Exception e) {
40
            e.printStackTrace();
41
        } finally{
42
        }
43
        return b;
44
    }
45
   
46
    //向伺服器傳送一個物件
47
    public void SendInfoToServer(Object o){
48
        try {
49
            Socket s=new Socket("127.0.0.1",9999);
50
        } catch (Exception e) {
51
            e.printStackTrace();
52
        } finally{
53
        }
54
    }

}

*******************************************************************************

[QqClientUser.java]

15 15   1
/**
2
 * 使用者操作邏輯類
3
 * QqClientUser會呼叫QqClientConServer的
4
 * 方法sendLoginInfoToServer向伺服器傳送
5
 */
6
package com.qq.client.model;
7
 
8
import com.qq.common.User;
9
 
10
public class QqClientUser {
11
    //驗證使用者是否合法
12
    public boolean checkUser(User u){
13
        return new QqClientConServer().sendLoginInfoToServer(u);
14
    }
15
}

*******************************************************************************

com.qq.client.tools

[ClientConServerThread.java]

58 58   1
/**
2
 * 這是客戶端和伺服器端保持通訊的執行緒
3
 */
4
package com.qq.client.tools;
5
 
6
import java.io.ObjectInputStream;
7
import java.net.Socket;
8
import com.qq.client.view.QqChat;
9
import com.qq.client.view.QqFriendList;
10
import com.qq.common.Message;
11
import com.qq.common.MessageType;
12
 
13
public class ClientConServerThread extends Thread{
14
    private Socket s;
15
   
16
    public Socket getS() {
17
        return s;
18
    }
19
 
20
    public void setS(Socket s) {
21
        this.s = s;
22
    }
23
 
24
    //建構函式
25
    public ClientConServerThread(Socket s){
26
        this.s=s;
27
    }
28
   
29
    public void run(){
30
        while(true){
31
            //不停的讀取從伺服器端發來的訊息
32
            try {
33
                ObjectInputStream ois=new ObjectInputS