java語言實踐課內容:聊天室小程序或QQ
功能要求:
聊天室:使用圖形用戶界面,能實現(xiàn)一個聊天室中多人聊天,可以兩人私聊。
QQ:實現(xiàn)類似QQ登錄、聊天等功能。
注意:有一定等級。完全照搬別人的代碼,不超過70分。
提示:使用socket通信
前面是逐步講解,要是想看最終代碼,請直接找到該文章的最下面//
感謝這位up博主的視頻講解?。。?br>準備工作
建立包,類,文件
插入圖片要創(chuàng)建一個文件夾,文件夾里保存的就是所需要的圖片啦,比如我創(chuàng)建的文件夾為image;
兩個設備上進行通信,要用到socket通信協(xié)議,即客戶端的內容先傳到服務器,在服務器上處理,傳回客戶端
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); //創(chuàng)建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.好友列表界面
成品圖如下:
要建立兩個卡片的轉換,即點擊卡片一的"黑名單"轉到第二個卡片界面,點擊第二個卡片的"我的好友",轉到第一個界面上
此時,就需要創(chuàng)建監(jiān)聽器的類,用到ActionListener接口
除此之外,我還建立了鼠標監(jiān)聽MouseListener接口,當鼠標停留在好友是,好友編號變紅,離開時變回黑色,雙擊好友時,可得到該好友的編號(并進入與該好友的聊天界面)
詳細代碼及注釋如下
//好友列表(也包括黑名單)
package com.qq.client.view;
import javax.swing.*;
import java.awt.*;
import java.awt.event.*;
public class QqFriendList extends JFrame implements ActionListener,MouseListener {
//負責創(chuàng)建監(jiān)聽器的類, 點擊按鈕觸發(fā)ActionListener事件; 鼠標監(jiān)聽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); //創(chuàng)建字體實例
//處理第一張卡片(我的好友);
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]; //數(shù)組;
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按鈕增加一個監(jiān)聽器,這個監(jiān)聽器對象就是“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); //監(jiān)聽
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]; //數(shù)組;
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) { //進入組件觸發(fā)鼠標事件
// TODO Auto-generated method stub
JLabel jl=(JLabel)arg0.getSource(); //變成JLable形式
//getSource()獲取鼠標事件的事件源;
jl.setForeground(Color.red); //設置前景色
}
public void mouseExited(MouseEvent arg0) { //鼠標離開組件觸發(fā)的鼠標事件
// TODO Auto-generated method stub
JLabel jl=(JLabel)arg0.getSource();
jl.setForeground(Color.black);
}
public void mouseClicked(MouseEvent arg0) { //點擊鼠標觸發(fā)鼠的標事件
// TODO Auto-generated method stub
//響應用戶雙擊的事件,并得到好友的編號.
if(arg0.getClickCount()==2) //getClickCount() 獲取鼠標被單機的次數(shù)
{
//得到該好友的編號
//getText() 的意思是:返回數(shù)據(jù)窗口控件中 懸浮在當前行列之上的
String friendNum=((JLabel)arg0.getSource()).getText();
//System.out.println("你希望和 "+friendNum+" 聊天");
}
}
public void mousePressed(MouseEvent arg0) { //按下按鼠標鍵觸發(fā)的鼠標事件
// TODO Auto-generated method stub
}
public void mouseReleased(MouseEvent arg0) { //釋放鼠標鍵觸發(fā)的鼠標事件
// TODO Auto-generated method stub
}
}
3.聊天界面,用服務器判斷登錄qq,對象流
這部分的代碼成果圖:
注:正常是要有數(shù)據(jù)庫來存qq賬號密碼信息的,秉著由簡至難的原則,此處先不設計數(shù)據(jù)庫,直接用服務器判斷
有了上訴兩個界面的設置,相信設置聊天界面應該很好模擬退出
聊天界面初步成果:
//與好友聊天的界面
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("發(fā)送");
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中即可
現(xiàn)在來實現(xiàn)登錄操作:當用戶點擊登錄后,把qq號碼和密碼發(fā)送給QqServer(服務器)去驗證,如果該用戶合法,返回true,否則返回false;
以對象流的方式讀取客戶端發(fā)來的賬號和密碼,在網絡間傳遞對象流,
避免直接讀入由特殊符號,空格,換行等干擾
對象流是一種高級流,可以方便我們將java中的任何對象進行讀寫操作。
java.io.objectoutputstream對象輸出流,可以將對象轉換為一組字節(jié)寫出。
java.io.objectinputstream對象輸入流,可以讀取一組字節(jié)轉換為原對象,
還原為原對象的條件是讀取這個字節(jié)應該是對象輸出流將一個對象轉換的字節(jié)。
一個對象要想序列化,該類必須實現(xiàn)java.io.Serializable 接口
Serializable 是一個標記接口,不實現(xiàn)此接口的類將不會使任何狀態(tài)序列化或反序列化,會拋出NotSerializableException 。
使用ObjectOutputStream對象中的方法writeObject,把對象寫入到文件中
在這里,我們還要用到socket socket是兩臺主機之間的一個連接
網絡上的兩個程序通過一個雙向的通信連接實現(xiàn)數(shù)據(jù)的交換,這個連接的一端稱為一個socket。
建立網絡通信連接需要一對socket,兩個socket之間形成一個管道(通道),進行信息流的傳輸(聯(lián)想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;
}
}
對發(fā)送服務端的信息規(guī)定一些規(guī)則
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登錄界面的代碼中加上監(jiān)聽(登錄按鈕),將得到的賬戶和密碼信息逐步傳入到破服務器里面,再有服務器返回,判斷是否能登錄,如果能,打開好友列表界面,
否則,觸發(fā)提示框"用戶名或密碼錯誤".
客戶端
注意:我判斷的是如果密碼為"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); //創(chuàng)建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_1.addActionListener(this); //監(jiān)控
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();
}
public void actionPerformed(ActionEvent arg0)
{
if(arg0.getSource()==jdown_1); //請求登錄
{
User u=new User();
u. setUserId(jmid_num2.getText().trim()); //trim() 方法用于 刪除字符串的頭尾空白符
u. setPasswd(new String (jmid_key2.getPassword())); //字符串;
QqClientUser qqclientuser=new QqClientUser();
if(qqclientuser.checkUser(u))
{
new QqFriendList(u.getUserId()); //好友列表
this.dispose(); //關閉登陸界面
}
else
{
JOptionPane.showMessageDialog(this,"用戶名或密碼錯誤");
//JOptionPane彈窗,消息提示框
//showMessageDialog這個方法里有兩個參數(shù)
//第一個參數(shù)-確定Frame在其中顯示的對話框
//第二個參數(shù)message就是我們要在提示框里顯示的信息
}
}
}
}
//好友列表(也包括黑名單)
package com.qq.client.view;
import javax.swing.*;
import java.awt.*;
import java.awt.event.*;
public class QqFriendList extends JFrame implements ActionListener,MouseListener {
//負責創(chuàng)建監(jiān)聽器的類, 點擊按鈕觸發(fā)ActionListener事件; 鼠標監(jiān)聽MouseListener
String owner;
// 第一張卡片(我的好友);
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 (String ownerId)
{
Font font = new Font("宋體", Font.PLAIN, 35); //創(chuàng)建字體實例
owner=ownerId;
//處理第一張卡片(我的好友);
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]; //數(shù)組;
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按鈕增加一個監(jiān)聽器,這個監(jiān)聽器對象就是“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); //監(jiān)聽
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]; //數(shù)組;
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;
setTitle(ownerId);//顯示自己的編號
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) { //進入組件觸發(fā)鼠標事件
// TODO Auto-generated method stub
JLabel jl=(JLabel)arg0.getSource(); //變成JLable形式
//getSource()獲取鼠標事件的事件源;
jl.setForeground(Color.red); //設置前景色
}
public void mouseExited(MouseEvent arg0) { //鼠標離開組件觸發(fā)的鼠標事件
// TODO Auto-generated method stub
JLabel jl=(JLabel)arg0.getSource();
jl.setForeground(Color.black);
}
public void mouseClicked(MouseEvent arg0) { //點擊鼠標觸發(fā)鼠的標事件
// TODO Auto-generated method stub
//響應用戶雙擊的事件,并得到好友的編號.
if(arg0.getClickCount()==2) //getClickCount() 獲取鼠標被單機的次數(shù)
{
//得到該好友的編號
//getText() 的意思是:返回數(shù)據(jù)窗口控件中 懸浮在當前行列之上的
String friendNum=((JLabel)arg0.getSource()).getText();
//System.out.println("你希望和 "+friendNum+" 聊天");
new QqChat(owner,friendNum); //傳入編號
}
}
public void mousePressed(MouseEvent arg0) { //按下按鼠標鍵觸發(fā)的鼠標事件
// TODO Auto-generated method stub
}
public void mouseReleased(MouseEvent arg0) { //釋放鼠標鍵觸發(fā)的鼠標事件
// TODO Auto-generated method stub
}
}
package com.qq.client.model;
import com.qq.common.*;
public class QqClientUser {
public boolean checkUser(User u)
{
return new QqClientConServer().sendLoginInfotoSSErver(u);
}
}
//客戶端鏈接服務器的部分
package com.qq.client.model;
import java.util.*;
import java.net.*;
import java.io.*;
import com.qq.common.*;
public class QqClientConServer{
//發(fā)送第一次請求,登錄請求
public boolean sendLoginInfotoSSErver(Object o) //發(fā)送對象到服務器
{
boolean b=false;
try
{
Socket s=new Socket("127.0.0.1",9999); //傳入要連接主機與端口
ObjectOutputStream oos=new ObjectOutputStream(s.getOutputStream()); //輸出流
oos.writeObject(o); //把o發(fā)出去
ObjectInputStream ois=new ObjectInputStream(s.getInputStream());
Message ms=(Message)ois.readObject();
if(ms.getMesType().equals("1"))
b=true;
}
catch (Exception e)
{
e.printStackTrace();
}
finally
{
}
return b;
}
}
服務端
除了需要的Message.java和User.java兩個類還需要
//服務器端控制界面,可以完成啟動服務器,關閉服務器,可以管理和監(jiān)控用戶
package com.qq.server.view;
import javax.swing.*;
import java.awt.*;
import java.awt.event.*;
import com.qq.server.model.MyQqServer;
public class ControlPanel extends JFrame implements ActionListener{
JPanel jp1;
JButton jstart,jend;
public ControlPanel()
{ Font font = new Font("宋體",Font.PLAIN,30);
jp1=new JPanel();
jstart=new JButton("啟動服務器");
jstart.addActionListener(this);
jend=new JButton("關閉服務器");
jstart.setFont(font); jend.setFont(font);
jstart.setForeground(Color.BLUE); jend.setForeground(Color.BLUE);
jp1.add(jstart);
jp1.add(jend);
add(jp1);
setLocation(150,150);
setSize(900,900);
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); //結束窗口所在的應用程序
setVisible(true);
}
public static void main(String[] args) {
// TODO Auto-generated method stub
ControlPanel controlpanel =new ControlPanel();
}
public void actionPerformed(ActionEvent arg0)
{
if(arg0.getSource()==jstart)
{
new MyQqServer();
}
}
}
//qq服務器,監(jiān)聽,等待某個qq客戶端鏈接
package com.qq.server.model;
import java.io.*; //(I/O)流庫,提供大量的流類
import java.net.*;//導入網絡包
import java.util.*; //實用工具類,提供了一些實用的方法和數(shù)據(jù)結構
import com.qq.common.*;
public class MyQqServer {
public MyQqServer ()
{
//try-catch語句處理異常
try
{
//ServerSocket類表示服務器socket
//在9999監(jiān)聽
System.out.println("服務器正在9999監(jiān)聽");//簡單判斷一下是否啟動服務端
ServerSocket ss=new ServerSocket(9999);
while(true) //服務器可多次監(jiān)聽
{
Socket s=ss.accept() ; //阻塞,等待連接(與客戶端)
//接收客戶端發(fā)來的信息
ObjectInputStream ois=new ObjectInputStream(s.getInputStream());
User u=(User)ois.readObject();
System.out.println("用戶Id:"+u.getUserId()+" 密碼"+u.getPasswd());
Message m=new Message();
ObjectOutputStream oos=new ObjectOutputStream(s.getOutputStream());
if(u.getPasswd().equals("123456"))
{
//返回一個成功登錄的信息
m.setMesType("1"); //mesType1 =1 表明登陸成功
oos.writeObject(m);
}
else
{
m.setMesType("2");
oos.writeObject(m);
s.close(); //關閉連接(流)
}
}
}
catch(Exception e)
{
e.printStackTrace();//將錯誤信息全部打印
}
finally{
}
}
}
4.一對一的聊天
進程概念:進程是代碼在數(shù)據(jù)集合上的一次運行活動,是系統(tǒng)進行資源分配和調度的基本單位。
線程概念:線程是進程的一個執(zhí)行路徑,一個進程中至少有一個線程,進程中的多個線程共享進程的資源。
此處要用線程用來接收客戶端的消息,因為不僅僅一個用戶(可實現(xiàn)多用戶登錄)
要在message類里面加入,用戶編號,接受方,發(fā)送信息(即獲得聊天界面文本框的內容)等
在chat類里面,加入讀取,文本域中顯示聊天代碼等內容
在管理客戶端線程中使用HashMap<關鍵字,值>記錄發(fā)送者與服務器的socket,關鍵字記錄用戶編號,值記錄socket
每個包(類)或多或少都有所更改,完整代碼如下,希望對大家有所幫助,同時請大佬指正錯誤%%%
//
客戶端代碼文章來源:http://www.zghlxwxcb.cn/news/detail-464763.html
package com.qq.common;
public class Message implements java.io.Serializable { //序列化
private String mesType;
private String sender;//發(fā)送者
private String getter; //接收者
private String con; //信息
public String getSender()
{
return sender;
}
public void setSender(String sender)
{
this.sender=sender;
}
public String getGetter()
{
return getter;
}
public void setGetter(String getter)
{
this.getter=getter;
}
public String getCon()
{
return con;
}
public void setCon(String con)
{
this.con=con;
}
public String getMesType()
{
return mesType;
}
public void setMesType(String mesType)
{
this.mesType =mesType;
}
}
//用戶信息類
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;
}
}
// 客戶端的登陸界面
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); //創(chuàng)建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_1.addActionListener(this); //監(jiān)控
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();
}
public void actionPerformed(ActionEvent arg0)
{
if(arg0.getSource()==jdown_1); //請求登錄
{
User u=new User();
u. setUserId(jmid_num2.getText().trim()); //trim() 方法用于 刪除字符串的頭尾空白符
u. setPasswd(new String (jmid_key2.getPassword())); //字符串;
QqClientUser qqclientuser=new QqClientUser();
if(qqclientuser.checkUser(u))
{
QqFriendList qqList=new QqFriendList(u.getUserId()); //好友列表
this.dispose(); //關閉登陸界面
}
else
{
JOptionPane.showMessageDialog(this,"用戶名或密碼錯誤");
//JOptionPane彈窗,消息提示框
//showMessageDialog這個方法里有兩個參數(shù)
//第一個參數(shù)-確定Frame在其中顯示的對話框
//第二個參數(shù)message就是我們要在提示框里顯示的信息
}
}
}
}
//好友列表(也包括黑名單)
package com.qq.client.view;
import javax.swing.*;
import java.awt.*;
import java.awt.event.*;
public class QqFriendList extends JFrame implements ActionListener,MouseListener {
//負責創(chuàng)建監(jiān)聽器的類, 點擊按鈕觸發(fā)ActionListener事件; 鼠標監(jiān)聽MouseListener
String owner;
// 第一張卡片(我的好友);
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 (String ownerId)
{
Font font = new Font("宋體", Font.PLAIN, 35); //創(chuàng)建字體實例
owner=ownerId;
//處理第一張卡片(我的好友);
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]; //數(shù)組;
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按鈕增加一個監(jiān)聽器,這個監(jiān)聽器對象就是“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); //監(jiān)聽
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]; //數(shù)組;
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;
setTitle(ownerId);//顯示自己的編號
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) { //進入組件觸發(fā)鼠標事件
// TODO Auto-generated method stub
JLabel jl=(JLabel)arg0.getSource(); //變成JLable形式
//getSource()獲取鼠標事件的事件源;
jl.setForeground(Color.red); //設置前景色
}
public void mouseExited(MouseEvent arg0) { //鼠標離開組件觸發(fā)的鼠標事件
// TODO Auto-generated method stub
JLabel jl=(JLabel)arg0.getSource();
jl.setForeground(Color.black);
}
public void mouseClicked(MouseEvent arg0) { //點擊鼠標觸發(fā)鼠的標事件
// TODO Auto-generated method stub
//響應用戶雙擊的事件,并得到好友的編號.
if(arg0.getClickCount()==2) //getClickCount() 獲取鼠標被單機的次數(shù)
{
//得到該好友的編號
//getText() 的意思是:返回數(shù)據(jù)窗口控件中 懸浮在當前行列之上的
String friendNum=((JLabel)arg0.getSource()).getText();
//System.out.println("你希望和 "+friendNum+" 聊天");
QqChat qqChat=new QqChat(owner,friendNum); //傳入編號
Thread t=new Thread(qqChat);
t.start(); // 啟動線程
}
}
public void mousePressed(MouseEvent arg0) { //按下按鼠標鍵觸發(fā)的鼠標事件
// TODO Auto-generated method stub
}
public void mouseReleased(MouseEvent arg0) { //釋放鼠標鍵觸發(fā)的鼠標事件
// TODO Auto-generated method stub
}
}
//與好友聊天的界面
//因為客戶端,要處于讀取的狀態(tài),因此我們把它做成一個線程
package com.qq.client.view;
import javax.swing.*;
import java.awt.*;
import java.io.*;
import java.awt.event.*;
import com.qq.common.*;
import com.qq.client.model.*;
public class QqChat extends JFrame implements ActionListener,Runnable{ //監(jiān)聽發(fā)送按鈕
Font font = new Font("宋體", Font.PLAIN, 20); //實現(xiàn)Runnable接口實現(xiàn)多線程,
JTextArea jta; //文本域 存聊天內容
JTextField jtf; //文本框
JButton jb;
JPanel jp;
String ownerId;
String friendId;
public QqChat(String owner,String friend)
{
ownerId= owner;
friendId=friend;
jta=new JTextArea();
jtf=new JTextField(25); //25個字符那么寬
jb=new JButton("發(fā)送");
jb.addActionListener(this);
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 void showMessage(Message m)
{
String info=m.getSender()+" 給"+m.getGetter()+" 發(fā)送:"+m.getCon()+"\r\n";
this.jta.append(info);
}
public void actionPerformed(ActionEvent arg0) {
// TODO Auto-generated method stub
if(arg0.getSource()==jb)//如果用戶點擊了發(fā)送按鈕
{
Message m=new Message();
m.setSender(this.ownerId);
m.setGetter(this.friendId);
m.setCon(jtf.getText()); //文本框中得到
//發(fā)送給服務器 要拿到socket,可以把socket寫成靜態(tài)的,從類里面拿出來;
try
{
ObjectOutputStream oos=new ObjectOutputStream(QqClientConServer.s.getOutputStream());
oos.writeObject(m);
}
catch (Exception e)
{
e.printStackTrace();
}
}
}
public void run()
{
while(true)
{ //一直處于讀取狀態(tài)(如果讀不到就等待)
try
{
ObjectInputStream ois=new ObjectInputStream(QqClientConServer.s.getInputStream());
Message m =(Message)ois.readObject();
//顯示
String info=m.getSender()+"給"+m.getGetter()+"發(fā)送"+m.getCon()+"\r\n";
this.jta.append(info); //(添加)追加到文本域
}
catch(Exception e)
{
e.printStackTrace();
}
}
}
}
package com.qq.client.model;
import com.qq.common.*;
public class QqClientUser {
public boolean checkUser(User u)
{
return new QqClientConServer().sendLoginInfotoSSErver(u);
}
}
//客戶端鏈接服務器的部分
package com.qq.client.model;
import java.util.*;
import java.net.*;
import java.io.*;
import com.qq.common.*;
public class QqClientConServer{
public static Socket s;
//發(fā)送第一次請求,登錄請求
public boolean sendLoginInfotoSSErver(Object o) //發(fā)送對象到服務器
{
boolean b=false;
try
{
s=new Socket("127.0.0.1",9999); //傳入要連接主機與端口
ObjectOutputStream oos=new ObjectOutputStream(s.getOutputStream()); //輸出流
oos.writeObject(o); //把o發(fā)出去
ObjectInputStream ois=new ObjectInputStream(s.getInputStream());
Message ms=(Message)ois.readObject();
if(ms.getMesType().equals("1"))
b=true;
}
catch (Exception e)
{
e.printStackTrace();
}
finally
{
}
return b;
}
}
服務端代碼(message,user與客戶端一樣,這里不在寫出,直接復制過去就可以)文章來源地址http://www.zghlxwxcb.cn/news/detail-464763.html
//服務器端控制界面,可以完成啟動服務器,關閉服務器,可以管理和監(jiān)控用戶
package com.qq.server.view;
import javax.swing.*;
import java.awt.*;
import java.awt.event.*;
import com.qq.server.model.MyQqServer;
public class ControlPanel extends JFrame implements ActionListener{
JPanel jp1;
JButton jstart,jend;
public ControlPanel()
{ Font font = new Font("宋體",Font.PLAIN,30);
jp1=new JPanel();
jstart=new JButton("啟動服務器");
jstart.addActionListener(this);
jend=new JButton("關閉服務器");
jstart.setFont(font); jend.setFont(font);
jstart.setForeground(Color.BLUE); jend.setForeground(Color.BLUE);
jp1.add(jstart);
jp1.add(jend);
add(jp1);
setLocation(150,150);
setSize(900,900);
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); //結束窗口所在的應用程序
setVisible(true);
}
public static void main(String[] args) {
// TODO Auto-generated method stub
ControlPanel controlpanel =new ControlPanel();
}
public void actionPerformed(ActionEvent arg0)
{
if(arg0.getSource()==jstart)
{
new MyQqServer();
}
}
}
//qq服務器,監(jiān)聽,等待某個qq客戶端鏈接
package com.qq.server.model;
import java.io.*; //(I/O)流庫,提供大量的流類
import java.net.*;//導入網絡包
import java.util.*; //實用工具類,提供了一些實用的方法和數(shù)據(jù)結構
import com.qq.common.*;
public class MyQqServer {
public MyQqServer ()
{
//try-catch語句處理異常
try
{
//ServerSocket類表示服務器socket
//在9999監(jiān)聽
System.out.println("服務器正在9999監(jiān)聽");//簡單判斷一下是否啟動服務端
ServerSocket ss=new ServerSocket(9999);
while(true) //服務器可多次監(jiān)聽
{
Socket s=ss.accept() ; //阻塞,等待連接(與客戶端)
//接收客戶端發(fā)來的信息
ObjectInputStream ois=new ObjectInputStream(s.getInputStream());
User u=(User)ois.readObject();
System.out.println("用戶Id:"+u.getUserId()+" 密碼"+u.getPasswd());
Message m=new Message();
ObjectOutputStream oos=new ObjectOutputStream(s.getOutputStream());
if(u.getPasswd().equals("123456"))
{
//返回一個成功登錄的信息
m.setMesType("1"); //mesType1 =1 表明登陸成功
oos.writeObject(m);
//單開一個線程,讓該線程與該客戶端保持通信
SerConClientThread scct=new SerConClientThread(s);
ManageClientThread.addClientThread(u.getUserId(),scct);//放到HashMap
scct.start(); //啟動與該客戶端通信的線程
}
else
{
m.setMesType("2");
oos.writeObject(m);
s.close(); //關閉連接(流)
}
}
}
catch(Exception e)
{
e.printStackTrace();//將錯誤信息全部打印
}
finally{
}
}
}
//服務器和某個客戶端的通信線程
package com.qq.server.model;
import java.net.*;
import java.io.*;
import com.qq.common.*;
public class SerConClientThread extends Thread //繼承Thread類
{
Socket s;
public SerConClientThread(Socket s)
{
this.s=s; //把服務器和該客戶端的連接賦給s;
}
public void run()
{
while(true)
{
//這里該線程就可以接受客戶端的信息
try
{
ObjectInputStream ois=new ObjectInputStream(s.getInputStream());
Message m=(Message)ois.readObject();
System.out.println(m.getSender()+"給 "+m.getGetter()+"發(fā)送: "+m.getCon());//判斷接受到客戶端的信息
//拿到發(fā)送者與服務器的socket,此時就需要hashmap;
SerConClientThread sc=ManageClientThread.getClientThread(m.getGetter());
ObjectOutputStream oos=new ObjectOutputStream(sc.s.getOutputStream()); //接受人的通信線程;
oos.writeObject(m);
}
catch(Exception e)
{
e.printStackTrace();
}
}
}
}
//管理客戶端線程
package com.qq.server.model;
import java.util.*;
public class ManageClientThread {
public static HashMap hm=new HashMap<String, SerConClientThread>();
//向hm中添加一個客戶通訊線程
public static void addClientThread(String uid,SerConClientThread ct)
{
hm.put(uid, ct);
}
//返回socket
public static SerConClientThread getClientThread(String uid)
{
return (SerConClientThread) hm.get(uid);
}
}
到了這里,關于java實現(xiàn)qq聊天(超詳細)的文章就介紹完了。如果您還想了解更多內容,請在右上角搜索TOY模板網以前的文章或繼續(xù)瀏覽下面的相關文章,希望大家以后多多支持TOY模板網!