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]

/**
* 这是与好友聊天的界面
* 因为客户端,要处于读取的状态,因此我们把它做成一个线程
*/
package com.qq.client.view;

import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.util.Date;
import javax.swing.ImageIcon;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.JScrollPane;
import javax.swing.JTextArea;
import javax.swing.JTextField;
import com.qq.client.model.QqClientConServer;
import com.qq.client.tools.ManageClientConServerThread;
import com.qq.common.Message;
import com.qq.common.MessageType;

public class QqChat extends JFrame implements ActionListener {
JTextArea jta;
JTextField jtf;
JButton jb;
JPanel jp;
JScrollPane jsp;
String ownerId;
String friendId;

// public static void main(String[] args) {
// new QqChat("小四");
// }

// 构造函数
public QqChat(String friendId, String ownerId) {
this.ownerId = ownerId;
this.friendId = friendId;

jta = new JTextArea();
jtf = new JTextField(15);
jb = new JButton("发送");
jb.addActionListener(this);
jp = new JPanel();
jp.add(jtf);
jp.add(jb);
jsp = new JScrollPane(jta);

this.add(jsp, "Center");
this.add(jp, "South");

this.setTitle(ownerId + " 正在与 " + friendId + " 聊天");
this.setSize(350, 300);
this.setIconImage((new ImageIcon("image/qie.jpg")).getImage());
this.setLocationRelativeTo(null);
this.setVisible(true);
}

// 写一个方法,让它显示消息
public void showMessage(Message m){
String info=m.getSender()+" 对 "+m.getGetder()+" 说:"+m.getCon()+"\r\n";
jta.append(info);
}

@Override
public void actionPerformed(ActionEvent e) {
if (e.getSource() == jb) {
// 如果用户点击了发送按钮
Message m = new Message();
m.setMesType(MessageType.message_comm_mes);
m.setSender(this.ownerId);
m.setGetder(this.friendId);
m.setCon(this.jtf.getText());
m.setSendTime(new Date().toString());
jta.append(ownerId + " 对 " + friendId + " 说:" + jtf.getText()
+ "\r\n");
jtf.setText("");
// 发送给服务器
try {
ObjectOutputStream oos = new ObjectOutputStream(
ManageClientConServerThread
.getClientConServerThread(ownerId).getS()
.getOutputStream());
oos.writeObject(m);
} catch (Exception e1) {
e1.printStackTrace();
}
}
}

// @Override
// public void run() {
// while(true){
// //读取[如果读不到就等待]
// try {
// ObjectInputStream ois=new
// ObjectInputStream(QqClientConServer.s.getInputStream());
// Message m=(Message)ois.readObject();
// //显示出来
// String info=m.getSender()+" 对 "+m.getGetder()+" 说:"+m.getCon()+"\r\n";
// jta.append(info);
// } catch (Exception e) {
// e.printStackTrace();
// }
// }
// }
}

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]

/**
* 这是客户端连接服务器的后台
* 功能:客户端与服务器进行数据交互
*/
package com.qq.client.model;

import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.net.Socket;
import com.qq.client.tools.ClientConServerThread;
import com.qq.client.tools.ManageClientConServerThread;
import com.qq.common.Message;
import com.qq.common.User;

public class QqClientConServer {
public Socket s;

//发送第一次请求
public boolean sendLoginInfoToServer(Object o){
boolean b=false;

try {
s=new Socket("127.0.0.1",9999);
ObjectOutputStream oos=new ObjectOutputStream(s.getOutputStream());
oos.writeObject(o);

ObjectInputStream ois=new ObjectInputStream(s.getInputStream());
Message ms=(Message)ois.readObject();
//这里就是验证用户登录的地方
if(ms.getMesType().equals("1")){
b=true;
//就创建一个该qq号和服务器端保持通讯连接的线程
ClientConServerThread ccst=new ClientConServerThread(s);
//启动该通讯线程
ccst.start();

ManageClientConServerThread.addClientConServerThread(((User)o).getUserId(), ccst);
}
} catch (Exception e) {
e.printStackTrace();
} finally{
}
return b;
}

//向服务器发送一个对象
public void SendInfoToServer(Object o){
try {
Socket s=new Socket("127.0.0.1",9999);
} catch (Exception e) {
e.printStackTrace();
} finally{
}
}

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]

/**
* 用户操作逻辑类
* QqClientUser会调用QqClientConServer的
* 方法sendLoginInfoToServer向服务器发送
*/
package com.qq.client.model;

import com.qq.common.User;

public class QqClientUser {
//验证用户是否合法
public boolean checkUser(User u){
return new QqClientConServer().sendLoginInfoToServer(u);
}
}

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]

/**
* 这是客户端和服务器端保持通讯的线程
*/
package com.qq.client.tools;

import java.io.ObjectInputStream;
import java.net.Socket;
import com.qq.client.view.QqChat;
import com.qq.client.view.QqFriendList;
import com.qq.common.Message;
import com.qq.common.MessageType;

public class ClientConServerThread extends Thread{
private Socket s;

public Socket getS() {
return s;
}

public void setS(Socket s) {
this.s = s;
}

//构造函数
public ClientConServerThread(Socket s){
this.s=s;
}

public void run(){
while(true){
//不停的读取从服务器端发来的消息
try {
ObjectInputStream ois=new ObjectInputStream(s.getInputStream());
Message m=(Message)ois.readObject();

//按消息包的类型来处理不同的包
if(m.getMesType().equals(MessageType.message_comm_mes)){
//把从服务器获得的消息,显示到该显示的聊天界面上
QqChat qqChat=ManageQqChat.getQqChat(m.getGetder()+" "+m.getSender());
//显示
qqChat.showMessage(m);
}else if(m.getMesType().equals(MessageType.message_ret_onLineFriend)){
String con=m.getCon();
String []friends=con.split(" ");
String getter=m.getGetder();
//修改相应的好友列表
QqFriendList qqFrindList=ManageQqFriendList.getQqFriendList(getter);
//更新在线好友
if(qqFrindList!=null){
qqFrindList.updateFriend(m);
}
}
} catch (Exception e) {
e.printStackTrace();
}
}
}
}

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 ObjectInputStream(s.getInputStream());

34

                Message m=(Message)ois.readObject();

35

               

36

                //按消息包的类型来处理不同的包

37

                if(m.getMesType().equals(MessageType.message_comm_mes)){

38

                    //把从服务器获得的消息,显示到该显示的聊天界面上

39

                    QqChat qqChat=ManageQqChat.getQqChat(m.getGetder()+" "+m.getSender());

40

                    //显示

41

                    qqChat.showMessage(m);

42

                }else if(m.getMesType().equals(MessageType.message_ret_onLineFriend)){

43

                    String con=m.getCon();

44

                    String []friends=con.split(" ");

45

                    String getter=m.getGetder();

46

                    //修改相应的好友列表

47

                    QqFriendList qqFrindList=ManageQqFriendList.getQqFriendList(getter);

48

                    //更新在线好友

49

                    if(qqFrindList!=null){

50

                        qqFrindList.updateFriend(m);

51

                    }

52

                }

53

            } catch (Exception e) {

54

                e.printStackTrace();

55

            }

56

        }

57

    }

58

}

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

[ManageClientConServerThread.java]

/**
* 这是一个管理客户端与服务器保持通讯的线程类
*/
package com.qq.client.tools;

import java.util.HashMap;

public class ManageClientConServerThread {
private static HashMap hm=new HashMap<String, ClientConServerThread>();
//把创建好的ClientConServerThread放入到hm中
public static void addClientConServerThread(String qqId,ClientConServerThread ccst){
hm.put(qqId, ccst);
}

//可以通过qqId取得该线程
public static ClientConServerThread getClientConServerThread(String qqId){
return (ClientConServerThread)hm.get(qqId);
}
}

x

1

/**

2

 * 这是一个管理客户端与服务器保持通讯的线程类

3

 */

4

package com.qq.client.tools;

5

 

6

import java.util.HashMap;

7

 

8

public class ManageClientConServerThread {

9

    private static HashMap hm=new HashMap<String, ClientConServerThread>();

10

    //把创建好的ClientConServerThread放入到hm中

11

    public static void addClientConServerThread(String qqId,ClientConServerThread ccst){

12

        hm.put(qqId, ccst);

13

    }

14

   

15

    //可以通过qqId取得该线程

16

    public static ClientConServerThread getClientConServerThread(String qqId){

17

        return (ClientConServerThread)hm.get(qqId);

18

    }

19

}

20


来自为知笔记(Wiz)

原文地址:https://www.cnblogs.com/xuxaut-558/p/10047918.html

时间: 2024-12-09 15:44:37

JAVA入门到精通-第92讲-山寨QQ项目6-多对多的聊天的相关文章

JAVA入门到精通-第94讲-山寨QQ项目8-好友在线提示

遇到的问题: 现在每登录一个人,后面的人是能看到前面的登录的人的: 但是,前面的人看不到后面的人登录: //后面每登录一个人,有责任去通知前面登录的人他已经上线了: 通知了完以后,需要更改好友在线的情况: //不让同一个账号反复登录 //如果 好友不在线,则不能聊天 ================================ [ManageQqChat.java] /** * 这是一个管理用户聊天界面的类 */ package com.qq.client.tools; import jav

JAVA入门到精通-第93讲-山寨QQ项目7-好友在线提示

-显示聊天信息 -用一个类来管理聊天界面,放到HashMap里面: QqChat放到一个类里面进行管理: HashMap管理就是引用了一下: ManageQqChat类对显示来说非常重要了: ------------------------- -好友在线提示  在线-头像彩色:  不在线-头像灰色: 只有服务器才能知道谁在线.谁不在线: -QqFriendList -默认只有自己在线,别人不在线: -1/4应该知道互相在线 -思路:当一个用户登录成功后,就向服务器发送一个要求  返回在线好友的包

JAVA入门到精通-第32讲-swing编程实战

 JBorderLayout,左边是JList,拆分窗格: 图片是放在JLabel上的,图片的地址写好就行:  在相关项目下面建立一个文件夹Folder,放置图片:或者复制粘贴:   拆分:水平拆分?垂直拆分?    拆分之后把jList和jl1这两个组件放进去: 可以伸缩:  在窗口左上角出现自己的图标? Border布局: 多行文本框带滚动条,聊天窗口可滚动:   加入QQ左上角图标:      转成图片,getImage; Border布局很有用, CENTER会被自动挤满:  QQ号码.

JAVA入门到精通-第33讲-绘图技术.坦克大战1

    用的是Access数据库,安全性不够好,性能也不够强大:   躲在这里,坦克很少过来,做了一个随机数: 把得分的记录保存在数据库里面: SQLServer2005中型数据库:    (x,y)坐标:     800x600:     表示计算机屏幕上的每一行由800个点组成,共有600行:   共有480000个像素:   java绘图技术:   绘图一般在JPane上画: 覆盖JPanel的paintat方法 Graphics是一个图形画笔类,绘图的重要类,可以理解为一只画笔 supe

JAVA入门到精通-第72讲-学生管理系统4-model 2模式(1)

删除.查询.添加.修改: -修改: 每个字段都可能修改: //给?赋值 //在prepareStatement语句后面赋值才好用 //修改完了,更新数据 目前为止,已经完成了增删改查-Model1模式 最大特点: 界面和业务逻辑操作是放在一起的: 优点:简单,开发方便: 缺点:代码复用性不高: 中大型项目可读性差,可维护性不高: 前台后端混杂在一起的: 比如:"数据库密码换了, 所有涉及到数据库的地方都需要发生变化" =================================

JAVA入门到精通-第36讲-事件监听-坦克大战4

Java事件处理机制:  事件源:事件:事件监听者:事件处理方法:  任何一个类,只要实现了相应的接口,就可以去监听某个事件源: 一个类要实现监听的基本步骤:  a.实现相应的接口[KeyListener,MouseListener,ActionListener,WindowListener] b.把接口的处理方法根据需要重新编写(override) c.在事件源注册监听 d.事件传递是靠事件对象 通过ActionEvent传递事件: 通过事件进行委派事件模型进行事件处理:  一个事件源可能会生

JAVA入门到精通-第31讲-常见组件

面板组件-Jpanel  一个JFrame,多个JPanel: JPanel默认是流布局:FlowLayout:  通过JPanel达到复杂布局的目的:  可以认为是网格布局,有3个JPanel:2个Label:2个按钮: 一个密码框和一个文本输入框:   最终展现出来的还是靠JFrame展现的  网格布局好点,3个JPanel,用流布局,控件往里加: ButtonGroup按钮组:一组里面只能选择一个:    对单选框管理,就是把它加入到ButtonGroup里面: 单选框也是一个个放进JPa

JAVA入门到精通-第70讲-学生管理系统2-增删改

-完成一个mini版本学生管理系统 jude软件:很好使的,安装一下就可以用了: -学生管理系统的添加 .修改.删除.上下页分页 ---------------------------------------------- 上面是一个流布局,JPanel 下面是一个流布局,JPanel 中间是Border布局,边界布局,CENTER JTextField jtf; 先加JLable--->Jtf文本域->JButton按钮 ------------------------- mis 系统:信息

JAVA入门到精通-第46讲-IO编程.记事本开发

IO编程-记事本开发 fis.read(bytes) 字节数组: 通过fis把文件里面的1800个字符,尽可能的读, 最多读1024个.记录一下,0-1024记录到bytes; 再去读的首,fis.read试图1024个字节,不够了, 第二次读了776个,n=776:(0,776): 第三次,又准备读1024,这次一个读不到,返回-1: 退出循环: JMenuBar 菜单条是一个JMenuBar: JMenu File是一个JMenu; JMenuItem 下面这些选项是JMenuItem; 一