网络知识 娱乐 java实现qq聊天(超详细)

java实现qq聊天(超详细)

java语言实践课内容:聊天室小程序或QQ

功能要求:
聊天室:使用图形用户界面,能实现一个聊天室中多人聊天,可以两人私聊。
QQ:实现类似QQ登录、聊天等功能。
注意:有一定等级。完全照搬别人的代码,不超过70分。
提示:使用socket通信
前面是逐步讲解,要是想看最终代码,请直接找到该文章的最下面
//

准备工作
建立包,类,文件
插入图片要创建一个文件夹,文件夹里保存的就是所需要的图片啦,比如我创建的文件夹为image;
两个设备上进行通信,要用到socket通信协议,即客户端的内容先传到服务器,在服务器上处理,传回客户端

在这里插入图片描述
image素材文件里存放的图片
链接:https://pan.baidu.com/s/1zns86NC6Qm80xKZHMIRSbA
提取码:3f6w
在这里插入图片描述

//

1.首先我们先建一个qq登陆界面

成品图如下:
在这里插入图片描述
详细代码及注释如下

// 客户端的登陆界面
package com.qq.client.view;
//抽象窗口工具包
import javax.swing.*;
import java.awt.*;
import java.awt.event.*;

public class QqClientlLogin  extends JFrame{  //JFrame的子类
	
	  JLabel jup;   //定义上部需要的组件   //标签
	
	//定义中部需要的组件
	  JPanel jmid ;
      JLabel jmid_num1,jmid_key1;
      JTextField jmid_num2;    //文本框
      JPasswordField jmid_key2;   //密码框
      JCheckBox jmid_rember,jmid_automatic;  //复选框
	  
	 JPanel  jdown;         //JPanel 面板
	 JButton jdown_1,jdown_2;    //定义下部需要的组件
	 
	 public  QqClientlLogin()      //构造方法
	 {
		  //处理上部      
	    jup=new JLabel(new  ImageIcon("image/up3.png"));
	
	    //处理中部
	    jmid=new JPanel(new GridLayout(3,3));//网格布局 3行3列
	    Font font = new Font("宋体", Font.PLAIN, 25);    //创建1个字体实例
	    jmid_num1=new JLabel("QQ号码:",JLabel.CENTER);  
	    jmid_key1=new JLabel("QQ密码:",JLabel.CENTER);
	    jmid_num1.setFont(font);    jmid_key1.setFont(font);  //字体大小
        jmid_num2= new JTextField() ;    
	    jmid_key2=new JPasswordField();
	    jmid_rember=new JCheckBox("自动登录");
	    jmid_automatic=new JCheckBox("记住密码");
	    jmid_rember.setFont(font);    jmid_automatic.setFont(font); //字体大小
	    jmid_rember.setForeground(Color.blue); 	  //设置字体颜色
	    jmid_automatic.setForeground(Color.blue);
	    jmid.add(jmid_num1);
	    jmid.add(jmid_num2);
	    jmid.add(jmid_key1);
	    jmid.add(jmid_key2);
	    jmid.add(jmid_rember);
	    jmid.add(jmid_automatic);
	    
	    //处理下部
	    jdown=new JPanel(new FlowLayout()); //流式布局
	    jdown_1=new JButton(new ImageIcon("image/denglu.png"));
	    jdown_2=new JButton(new ImageIcon("image/tuichu.png"));
	    jdown.add(jdown_1);
	    jdown.add(jdown_2);
	    
	    setLocation(300,300); //窗口的位置
	    add(jup,"North");   //放在最北部    
	    add(jmid,"Center");  //放在中间
	    add(jdown,"South"); //放在南部
	    setSize(700,540);     //设置大小                                  
	    setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); //结束窗口所在的应用程序  
	    setVisible(true);       //可见,默认是不可见
	 }
	 
	 public static void main(String[]  args)
	 {
		 QqClientlLogin  qqClientlLogin=new  QqClientlLogin();
	 } 
}

//

2.好友列表界面

成品图如下:
在这里插入图片描述

要建立两个卡片的转换,即点击卡片一的"黑名单"转到第二个卡片界面,点击第二个卡片的"我的好友",转到第一个界面上
此时,就需要创建监听器的类,用到ActionListener接口
除此之外,我还建立了鼠标监听MouseListener接口,当鼠标停留在好友是,好友编号变红,离开时变回黑色,双击好友时,可得到该好友的编号(并进入与该好友的聊天界面)
详细代码及注释如下

//好友列表(也包括黑名单)
package com.qq.client.view;

import javax.swing.*;
import java.awt.*;
import java.awt.event.*;

public class QqFriendList extends JFrame implements ActionListener,MouseListener {   
	//负责创建监听器的类,  点击按钮触发ActionListener事件;  鼠标监听MouseListener 
	
    // 第一张卡片(我的好友);
		JPanel jup1,jmid1,jdown1;   //jup1即为总JPanel;
		JButton jup1_1,jdown1_1;
		JScrollPane jmid1_1;      //滑动窗口;
	
	//第二张卡片(黑名单);
		JPanel jup2,jup2_button,jmid2;
		JButton jup2_1,jup2_2;
		JScrollPane jmid2_1;      //滑动窗口;
		
  //把整个JFrame设置成CardLayout布局
		CardLayout cl;

   public  QqFriendList ()
   {         Font font = new Font("宋体", Font.PLAIN, 35);    //创建字体实例

	
	   //处理第一张卡片(我的好友);
	    jup1=new JPanel(new BorderLayout());   //第一张卡片的总JPane,BorderLayout()布局;
	   jup1_1=new JButton("我的好友");
	   jup1_1.setFont(font);  //字体
	
	   //假设有50个好友,具体是服务器返回的结果(好友信息存放在服务器)
	   jmid1=new JPanel(new  GridLayout(50,1,4,4));  //(4,4)行间距和列间距
       //给jmid1初始化50个好友
	   JLabel []jlist1=new JLabel [50];  //数组;
	   for ( int i=0; i<jlist1.length ; i++ )
	   {
		   jlist1[i]=new JLabel(i+1+" ",new ImageIcon("image/touxiang.png"),JLabel.LEFT);
		   jlist1[i].addMouseListener(this);
		   jmid1.add(jlist1[i]);  
	   }
	   jmid1_1=new JScrollPane(jmid1);
	   
	   jdown1_1=new JButton("黑名单");
	   jdown1_1.addActionListener(this);     //给jdown1_1按钮增加一个监听器,这个监听器对象就是“QqFriendList ”类型的,
	                          //当用户点击了jdown1_1按钮时,会程序自动前往QqFriendList类的“actionPerformed”方法中,处理产生的事件。
	   jdown1_1.setFont(font);        //字体
	   jdown1=new JPanel(new GridLayout(1,1));//黑名单按钮
	   jdown1.add(jdown1_1);  //加入按钮;
	   
       jup1.add(jup1_1,"North");
       jup1.add(jmid1_1,"Center");
       jup1.add(jdown1,"South");
   

       
       //处理第二张卡片(黑名单)	   
       jup2=new JPanel(new BorderLayout());   //第二张卡片的总JPane,BorderLayout()布局;
       jup2_1=new JButton("我的好友");
       jup2_1.addActionListener(this);   //监听
       jup2_2=new JButton("黑名单");
	   jup2_1.setFont(font);  //字体
       jup2_2.setFont(font);
       jup2_button=new JPanel(new GridLayout(2,1));//黑名单按钮
       jup2_button.add(jup2_1);
       jup2_button.add(jup2_2);
       
	   //假设有20个黑名单,具体是服务器返回的结果(好友信息存放在服务器)
	   jmid2=new JPanel(new  GridLayout(20,1,4,4));  //(4,4)行间距和列间距
       //给jmid2初始化20个好友
	   JLabel []jlist2=new JLabel [20];  //数组;
	   for ( int i=0; i<jlist2.length ; i++ )
	   {
		   jlist2[i]=new JLabel(i+1+" ",new ImageIcon("image/heitou.png"),JLabel.LEFT);
		   jmid2.add(jlist2[i]);
	   }
	   jmid2_1=new JScrollPane(jmid2);
	   
       jup2.add(jup2_button,"North");
       jup2.add(jmid2_1);
  
       cl=new CardLayout();
       setLayout(cl);
       add(jup1,"1");  //第一张卡片jup1;
       add(jup2,"2");  //第二张卡片jup2;
       setLocation(300,300); //窗口的位置
       setSize(440,800);
       setVisible(true);     
   }
  
	public static void main(String[] args) {
		// TODO Auto-generated method stub
		QqFriendList  qqFriendList=new  QqFriendList();
	}
	
	public void actionPerformed(ActionEvent arg0)
	{ 
		//如果点击了黑名单按钮,就显示第二张卡片
		if(arg0.getSource()==jdown1_1)
		{
			cl.show(this.getContentPane(), "2");  //用getContentPane()方法获得JFrame的内容面板
		}
		else
			if(arg0.getSource()==jup2_1)
			{
				cl.show(this.getContentPane(), "1");
			}
	}
	public void mouseEntered(MouseEvent arg0) {    //进入组件触发鼠标事件
		// TODO Auto-generated method stub
		JLabel jl=(JLabel)arg0.getSource(); //变成JLable形式
                                              //getSource()获取鼠标事件的事件源;
		jl.setForeground(Color.red);   //设置前景色
	}

	public void mouseExited(MouseEvent arg0) {  //鼠标离开组件触发的鼠标事件
		// TODO Auto-generated method stub
		JLabel jl=(JLabel)arg0.getSource();   
		jl.setForeground(Color.black);
	}
	
	public void mouseClicked(MouseEvent arg0) {   //点击鼠标触发鼠的标事件
		// TODO Auto-generated method stub
		//响应用户双击的事件,并得到好友的编号.
		if(arg0.getClickCount()==2)   //getClickCount() 获取鼠标被单机的次数
		{
			//得到该好友的编号 
			//getText() 的意思是:返回数据窗口控件中 悬浮在当前行列之上的
			String friendNum=((JLabel)arg0.getSource()).getText();
			
			//System.out.println("你希望和 "+friendNum+" 聊天");
		}
	}


	public void mousePressed(MouseEvent arg0) { //按下按鼠标键触发的鼠标事件
		// TODO Auto-generated method stub
		
	}

	public void mouseReleased(MouseEvent arg0) {   //释放鼠标键触发的鼠标事件
		// TODO Auto-generated method stub
		
	}

} 

3.聊天界面,用服务器判断登录qq,对象流

这部分的代码成果图:
在这里插入图片描述

注:正常是要有数据库来存qq账号密码信息的,秉着由简至难的原则,此处先不设计数据库,直接用服务器判断
有了上诉两个界面的设置,相信设置聊天界面应该很好模拟退出
聊天界面初步成果:在这里插入图片描述

//与好友聊天的界面
package com.qq.client.view;
import javax.swing.*;
import java.awt.*;
import java.awt.event.*;
public class QqChat extends JFrame {
	 Font font = new Font("宋体", Font.PLAIN, 20); 
	JTextArea jta;    //文本域  存聊天内容
	JTextField jtf;    //文本框
	JButton jb;
	JPanel jp;
	String ownerId;
	String friendId;
	
	public QqChat(String owner,String friend)
	{
		jta=new JTextArea();
		jtf=new JTextField(25);     //25个字符那么宽
		jb=new JButton("发送");
		jb.setFont(font);
		jp=new JPanel();
		jp.add(jtf);
		jp.add(jb);	
		add(jta,"Center");
		add(jp,"South");
		setTitle(owner+"正在和 "+friend+" 聊天");  //设置标题
		setIconImage((new ImageIcon("image/qq.png").getImage())); //获取图像
		setLocation(800,400); //窗口的位置
		setSize(600, 500);
		setVisible(true);
	}
	
	public static void main(String[] args) {
		// TODO Auto-generated method stub
	    QqChat qqChat=new QqChat("1");   //传入好友1,你正在和1聊天;
	}

思考一下,如果是要与其余好友聊天应该如何表示呢?
在 QqFriendList中,我们双击得到好友的编号,只需把编号传到QqChat中即可
在这里插入图片描述
现在来实现登录操作:当用户点击登录后,把qq号码和密码发送给QqServer(服务器)去验证,如果该用户合法,返回true,否则返回false;
以对象流的方式读取客户端发来的账号和密码,在网络间传递对象流,
避免直接读入由特殊符号,空格,换行等干扰

对象流是一种高级流,可以方便我们将java中的任何对象进行读写操作。
java.io.objectoutputstream对象输出流,可以将对象转换为一组字节写出。
java.io.objectinputstream对象输入流,可以读取一组字节转换为原对象,
还原为原对象的条件是读取这个字节应该是对象输出流将一个对象转换的字节。

一个对象要想序列化,该类必须实现java.io.Serializable 接口
Serializable 是一个标记接口,不实现此接口的类将不会使任何状态序列化或反序列化,会抛出NotSerializableException 。
使用ObjectOutputStream对象中的方法writeObject,把对象写入到文件中

在这里,我们还要用到socket socket是两台主机之间的一个连接
网络上的两个程序通过一个双向的通信连接实现数据的交换,这个连接的一端称为一个socket。
建立网络通信连接需要一对socket,两个socket之间形成一个管道(通道),进行信息流的传输(联想IO流中文件和程序之间读写)。

ServerSocket与Socket入门详解
分别在Qqclient(客户端)和QqServe(服务端)项目里建立com.qq.common包,包里存放Message.java和User.java两个类
User类就是获得用户输入的账号和密码

//用户信息类
package com.qq.common;

public class User  implements java.io.Serializable{
	 
	private String userId;
	private String passwd;
	public String getUserId()
	{
		return userId;
	}
	public void setUserId(String userId) {
		this.userId = userId;
	}
	public String getPasswd()
	{
		return passwd;
	}
	public void setPasswd(String passwd)
	{
		this.passwd=passwd;
	}
}

对发送服务端的信息规定一些规则
mesType为true 表明登陆成功
mesType为false 表明登录失败
所以Message就是用来判断的

package com.qq.common;
public class Message implements java.io.Serializable { //序列化     
	private String mesType;
	public String getMesType()
	{
		return mesType;
	}
	public void setMesType(String mesType)
	{
		this.mesType =mesType;
	}
}

主要思想
在这里插入图片描述
要在第一部分qq登录界面的代码中加上监听(登录按钮),将得到的账户和密码信息逐步传入到破服务器里面,再有服务器返回,判断是否能登录,如果能,打开好友列表界面,
否则,触发提示框"用户名或密码错误".
客户端
注意:我判断的是如果密码为"123456"即为正确

// 客户端的登陆界面
package com.qq.client.view;
//抽象窗口工具包
import javax.swing.*;
import java.awt.*;  
import java.awt.event.*;
import com.qq.client.model.*;
import com.qq.common.*;
public class QqClientlLogin  extends JFrame implements ActionListener{  //JFrame的子类
	
	  JLabel jup;   //定义上部需要的组件   //标签
	
	//定义中部需要的组件
	  JPanel jmid ;
      JLabel jmid_num1,jmid_key1;
      JTextField jmid_num2;    //文本框
      JPasswordField jmid_key2;   //密码框
      JCheckBox jmid_rember,jmid_automatic;  //复选框
	  
	 JPanel  jdown;         //JPanel 面板
	 JButton jdown_1,jdown_2;    //定义下部需要的组件
	 
	 public  QqClientlLogin()      //构造方法
	 {
		  //处理上部      
	    jup=new JLabel(new  ImageIcon("image/up3.png"));
	
	    //处理中部
	    jmid=new JPanel(new GridLayout(3,3));//网格布局 3行3列
	    Font font = new Font("宋体", Font.PLAIN, 25);    //创建1个字体实例
	    jmid_num1=new JLabel("QQ号码:",JLabel.CENTER);  
	    jmid_key1=new JLabel("QQ密码:",JLabel.CENTER);