QQ_2
4. 升级版三-----解决:一对多发送,多对多。
4.1 假设客户端1号QQ给多个客户端用户发送消息时,socket会被占用,在服务器中转时出现异常。1号QQ给2号QQ和3号QQ发消息,就会出现socket占用问题。

下图中每个方框假设是一台电脑,一台电脑登陆多个客户端。

(1)新建ClientConServerThread类,只要一个账号登陆了,就开启一个线程,让这个线程和服务器保持通讯。
新建构造方法,继承Thread,run()不断循环获取
(2)新建类:ManageClientConServerThread,管理客户端和服务器保持通讯的线程类。
定义HashMap集合,k:你登陆的那个QQ的号码,
(3)找到sendLoginInfoToServer,进行验证用户登陆。
在ClientConServerThread里不断读取来自服务器的内容。


/*换工作区之后,设置失效了,87集15分钟30秒左右。 * 1.新建两个项目,命名为QqClint、QqServer. * 2.当用户点击登录后,吧QQ号码和密码发送给QQServer端去验证,如果合法返回OK,如果不合法返回error * 补充讲:新知识,如何在网络间传递对象流。写个擦拭类TestClient、TestServer * 3.对message规定一些规则。 * mesType 1 --> 表明登录成功 * mesType 2 --> 表明登录失败 * mesType 3 --> 表明是普通消息包 * *步骤: *1.在客户端QqClint--view里建类1.login-----2.FriendList-----3.Chat *2. 服务器端QqServer--view里新建类MyServerFrame,这是服务器端的控制界面,可以完成启动服务器,关闭服务器;通过实现ActionListener接口--actionPerformed调用new MyQqServer(); MyQqServer类在服务器端QqServer---model里,这是QQ服务器,它在监听,等待某个QQ客户端,未连接。 3.客户端QqClint---common里面定义Message类和User类,User类:用户信息类,包含userId和passwd。Message类里实现序列化接口并包含mesType变量,1.实现java.io.Serializable接口;2.定义私有变量mesType,并将类拷贝到QQServer类下的common里一份。 4.客户端QqClint---model里面定义(1)QqClientConServer类:客户端连接服务器的后台. 5.客户端QqClint---view----QqLogin类的jp1_jb1登陆按钮添加响应---监听actionPerformed里---创建QqClientUser并调用checkUser,验证方法是否合法,其实调用的是new QqClientConServer().sendLoginInfoToServer---发送第一次请求---调用getMesType方法, 新建类(数据库相关的):QqServer----model---QqServerUser 新建包:数据库包DB,并在其中定义SqlHelper类 */ package view; import java.awt.Color; import java.awt.GridLayout; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; import javax.swing.ImageIcon; import javax.swing.JButton; import javax.swing.JCheckBox; import javax.swing.JFrame; import javax.swing.JLabel; import javax.swing.JOptionPane; import javax.swing.JPanel; import javax.swing.JPasswordField; import javax.swing.JTabbedPane; import javax.swing.JTextField; import common.User; import model.QqClientUser; public class QqLogin extends JFrame implements ActionListener { // 4.修改前景色。 public static void main(String[] args) { QqLogin l = new QqLogin(); } JLabel jbl1; // 1.定义北部,JLabel JPanel jp1; // 2.定义南部流布局JPanel以及3个按钮 JButton jp1_jb1, jp1_jb2, jp1_jb3; // 3.定义中部--有3个JPanel,由一个叫做选项卡窗口管理;jp2放置网格布局,就是首页那9个 JTabbedPane jtp; JPanel jp2, jp3, jp4; JLabel jp2_jbl1, jp2_jbl2, jp2_jbl3, jp2_jbl4; JButton jp2_jb1; JTextField jp2_jtf; JPasswordField jp2_jpf; JCheckBox jp2_jcb1, jp2_jcb2; public QqLogin() { jbl1 = new JLabel(new ImageIcon("image/tou.gif"));// 北部 jp1 = new JPanel(); // 南部 jp1_jb1 = new JButton(new ImageIcon("image/denglu.gif")); jp1_jb1.addActionListener(this); // 06-12-01响应用户登录 jp1_jb2 = new JButton(new ImageIcon("image/quxiao.gif")); jp1_jb3 = new JButton(new ImageIcon("image/xiangdao.gif")); // 把三个按钮放到jp1,即登录、取消、注册向导 jp1.add(jp1_jb1); jp1.add(jp1_jb2); jp1.add(jp1_jb3); // 中部--有3个JPanel,由一个叫做选项卡窗口管理;jp2放置网格布局,就是首页那9个 jtp = new JTabbedPane(); jp2 = new JPanel(new GridLayout(3, 3)); jtp.add("QQ号码", jp2); jp3 = new JPanel(); jtp.add("手机号码", jp3); jp4 = new JPanel(); jtp.add("电子邮件", jp4); jp2_jbl1 = new JLabel("QQ号码", JLabel.CENTER); jp2_jtf = new JTextField(); jp2_jb1 = new JButton(new ImageIcon("clear.gif")); jp2_jbl2 = new JLabel("QQ密码", JLabel.CENTER); jp2_jpf = new JPasswordField(); jp2_jbl3 = new JLabel("忘记密码", JLabel.CENTER); jp2_jcb1 = new JCheckBox("隐身登录"); jp2_jcb2 = new JCheckBox("记住密码"); jp2_jbl4 = new JLabel("申请密码保护", JLabel.CENTER); // 添加前景色 jp2_jbl3.setForeground(Color.blue); // 把控件按照顺序加入到jp2 jp2.add(jp2_jbl1); jp2.add(jp2_jtf); jp2.add(jp2_jb1); jp2.add(jp2_jbl2); jp2.add(jp2_jpf); jp2.add(jp2_jbl3); jp2.add(jp2_jcb1); jp2.add(jp2_jcb2); jp2.add(jp2_jbl4); this.add(jbl1, "North"); this.add(jp1, "South"); this.add(jtp, "Center"); this.setSize(350, 240); this.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); this.setVisible(true); } @Override public void actionPerformed(ActionEvent e) { if (e.getSource() == jp1_jb1) { //jp1_jb1登录按钮---并给jp1_jb1进行注册监听(69行左右) //1.点击登陆------->2.调用QqClientUser的checkUser,其实调用的是 User u = new User(); u.setUserId(jp2_jtf.getText().trim()); //输入框JTextField的jp2_jtf里面拿内容 u.setPasswd(new String(jp2_jpf.getPassword())); //密码框JPasswordField的jp2_jpf的内容为字符串数组,需要new一下 //7.客户端的QqLogin的actionPerformed QqClientUser qqClientUser = new QqClientUser(); if(qqClientUser.checkUser(u)){ new QqFriendList(u.getUserId()); //1_2---->1_3QQFriendList好友列表将其显示 //关闭登录界面 this.dispose(); }else{ JOptionPane.showMessageDialog(this, "用户名密码错误"); } } } }
/*1.GridLayout 类是一个布局处理器,它以矩形网格形式对容器的组件进行布置。容器被分成大小相等的矩形,一个矩形中放置一个组件。 *2.这是一个布置容器的边框布局,它可以对容器组件进行安排,并调整其大小,使其符合下列五个区域:北、南、东、西、中。每个区域最多只能包含一个组件:NORTH、SOUTH、EAST、WEST、CENTER。 *JLabel:用于短文本字符串或图像或二者的显示区。 GridLayout() 一行一列 GridLayout(int rows, int cols) rows行 cols列 GridLayout(int rows, int cols, int hgap, int vgap) rows行 cols 列, 列间距hgap, 行间距vgap * */ package view; import java.awt.BorderLayout; import java.awt.CardLayout; import java.awt.Color; import java.awt.GridLayout; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; import java.awt.event.MouseEvent; import java.awt.event.MouseListener; import javax.swing.ImageIcon; import javax.swing.JButton; import javax.swing.JFrame; import javax.swing.JLabel; import javax.swing.JPanel; import javax.swing.JScrollPane; import model.ManageQqChat; public class QqFriendList extends JFrame implements MouseListener,ActionListener{ /* 1.主类继承JFrame---定义构造方法---定义3个JPanel,3个按钮,1个滚动条 * 2.初始化3个按钮:jp_jb1,jp_jb2,jp_jb3 * 3.定义中间的滚动列表。初始化JPanel:jp2----定义for循环产生50个列表---将jp2添加到JScrollPane:jsp1 * 4.定义最下面: 初始化JPanel:jp3-----GridLayout布局,放入两个按钮:jp_jb2,jp_jb3 * 5.定义整体: 初始化JPanel:jp1-----BorderLayout布局---将jp2,jp3,jp_jb1加入进来 * 6.添加到显示: jp1添加到显示并做基本设置。 重点代码:2. jp_jb1 = new JButton("我的好友"); 3.jp2 = new JPanel(new GridLayout(50,1,4,4)); JLabel[] jbls = new JLabel[50]; jbls[i] = new JLabel(i+1+"",new ImageIcon("image/mm.jpg"),JLabel.LEFT); jsp1 = new JScrollPane(jp2); 4.jp3 = new JPanel(new GridLayout(2,1)); 5.jp1 = new JPanel(new BorderLayout()); jp1.add(jp_jb1,"North"); jp1.add(jsp1,"Center"); 6. this.add(jp1,"Center"); 1.复制上面代码并修改名称---定义CardLayout c1并把整个JFrame设置成CardLayout---定义显示相关设置 2.添加MouseListener---定义mouseEntered\mouseExited 3.添加ActionListener--定义actionPerformed */ public static void main(String[] args) { // QqFriendList t = new QqFriendList(); } JPanel jp1,jp2,jp3; JButton jb1,jb2,jb3; JScrollPane jsp1; String ownerId; //2.1 JPanel jpmsr1,jpmsr2,jpmsr3; JButton jpmsr_jb1,jpmsr_jb2,jpmsr_jb3; JScrollPane jsp2; //2.4把整个JFrame设置成CardLayout CardLayout c1; public QqFriendList(String ownerId){ //1_1添加好友的号码----->1_2注册QQLogin的actionPerformed里 this.ownerId = ownerId; jb1 = new JButton("我的好友"); jb2 = new JButton("陌生人"); jb3 = new JButton("黑名单"); jb2.addActionListener(this); //2.6(3) jp1 = new JPanel(new GridLayout(50,1)); JLabel[] jls = new JLabel[50]; for (int i = 0; i < jls.length; i++) { jls[i] = new JLabel(i+1+"",new ImageIcon("image/mm.jpg"),JLabel.LEFT); //默认只有自己显示为彩色。 jls[i].setEnabled(false); if(jls[i].getText().equals(ownerId)){ jls[i].setEnabled(true); } jls[i].addMouseListener(this); //2.6(1) jp1.add(jls[i]); } jsp1 = new JScrollPane(jp1); jp2 = new JPanel(new GridLayout(2,1)); jp2.add(jb2); jp2.add(jb3); jp1 = new JPanel(new BorderLayout()); jp1.add(jb1,"North"); jp1.add(jsp1,"Center"); jp1.add(jp2,"South"); //处理第二张 jpmsr_jb1 = new JButton("我的好友"); jpmsr_jb2 = new JButton("陌生人"); jpmsr_jb3 = new JButton("黑名单"); jpmsr_jb1.addActionListener(this); //2.6(3) //2.2-jpmsr2---jpmsr2添加到滚动组件JScrollPane jpmsr2 = new JPanel(new GridLayout(20,1,4,4)); //rows行cols列,列间距hgap,行间距vgap JLabel[] jbls2 = new JLabel[20]; //给jphy2初始化20个好友 for (int i = 0; i < jbls2.length; i++) { jbls2[i] = new JLabel(i+1+"",new ImageIcon("image/mm.jpg"),JLabel.LEFT); jbls2[i].addMouseListener(this); //2.6(2) jpmsr2.add(jbls2[i]); } jsp2 = new JScrollPane(jpmsr2); //2.3.初始化jpmsr3 jpmsr3 = new JPanel(new GridLayout(2,1)); jpmsr3.add(jpmsr_jb1); jpmsr3.add(jpmsr_jb2); //2.1.初始化jpmsr1 jpmsr1 = new JPanel(new BorderLayout()); jpmsr1.add(jpmsr3,"North"); jpmsr1.add(jsp2,"Center"); jpmsr1.add(jpmsr_jb3,"South"); //2.5----2.6实现MouseListener、ActionListener----注册监听 c1 = new CardLayout(); this.setLayout(c1); this.add(jp1,"1"); //将第一个整体布局jp1放到这里 this.add(jpmsr1,"2"); //将第二个整体布局jpmsr1放到这里 this.setTitle(ownerId); //1_3 在窗口显示自己的编号 this.setSize(200,500); this.setVisible(true); } @Override public void mouseClicked(MouseEvent e) { //响应用户双击的事件,并得到好友的编号 if(e.getClickCount()==2){ //得到该好友的编号 String friend = ((JLabel)e.getSource()).getText(); System.out.println("我在QqFriendList,你希望和"+friend+"谁聊天"); Chat chat = new Chat(this.ownerId,friend); //启动聊天界面的线程。 /*Thread t = new Thread(chat); t.start();*/ //把聊天页面加入到管理类 ManageQqChat.addQqChat(this.ownerId+" "+friend, chat); } } @Override public void mousePressed(MouseEvent e) { } @Override public void mouseReleased(MouseEvent e) { } @Override public void mouseEntered(MouseEvent e) { JLabel j1 = (JLabel)e.getSource(); j1.setForeground(Color.red); } @Override public void mouseExited(MouseEvent e) { JLabel j1 = (JLabel)e.getSource(); j1.setForeground(Color.black); } @Override public void actionPerformed(ActionEvent e) { if(e.getSource()==jb2){ //第一个的按钮2,就显示第二张卡片 c1.show(this.getContentPane(), "2"); }else if(e.getSource()==jpmsr_jb1){ //第二个的按钮1 c1.show(this.getContentPane(), "1"); } } }
package view; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; import java.io.*; import java.util.Date; import javax.swing.ImageIcon; import javax.swing.JButton; import javax.swing.JFrame; import javax.swing.JPanel; import javax.swing.JTextArea; import javax.swing.JTextField; import model.ManageClientConServerThread; import common.Message; public class Chat extends JFrame implements ActionListener{ private static final long serialVersionUID = 1L; /*与朋友聊天的截面 * 1.定义JTextArea---定义JTextField和JButton----定义JPanel * 2.设置默认设置 * 3.将Chat()构造方法改为有参构造方法。 * 4.在FriendList方法mouseClicked()中调用本类的Chat(String friend)方法 */ public static void main(String[] args) { // Chat c = new Chat("1"); } JTextArea jta ; JTextField jtf ; JButton jb1; JPanel jp1; String ownerId; String friendId; public Chat(String ownerId,String friend){ this.ownerId = ownerId; this.friendId = friend; jta = new JTextArea(); jtf = new JTextField(15); jb1 = new JButton("发送"); jb1.addActionListener(this); //添加监听 jp1 = new JPanel(); jp1.add(jtf); jp1.add(jb1); this.add(jta,"Center"); this.add(jp1,"South"); this.setTitle(ownerId+"正在和"+friend+"聊天"); this.setIconImage(new ImageIcon("image/qq.gif").getImage()); this.setSize(300,250); this.setVisible(true); } //写一个方法,让他显示 public void showMessage(Message m){ String info = "我在Chat,"+m.getSender() + "对"+m.getGetter()+"说"+ m.getCon()+"\r\n"; this.jta.append(info); } @Override public void actionPerformed(ActionEvent e) { //如果点击发送按钮,获取发送者的编号、接收者的编号、文本框里的发送内容、获取发送时间 if(e.getSource() == jb1){ Message m = new Message(); m.setSender(this.ownerId); m.setGetter(this.friendId); m.setCon(jtf.getText()); m.setSendTime(new Date().toString()); //转为字符串 //使用输出流,发送给服务器,将Socket做成静态的 try { //管理线程的类取到线程,再取到socket,再通过socket取到输出流 ObjectOutputStream oos = new ObjectOutputStream(ManageClientConServerThread.getClientConServerThread(ownerId).getS().getOutputStream()); oos.writeObject(m); } catch (Exception e2) { e2.printStackTrace(); } } } /* @Override public void run() { while(true){ //读取 try { ObjectInputStream ois= new ObjectInputStream(QqClientConServer.s.getInputStream()); Message m = (Message) ois.readObject(); //并将信息显示在文本域上jta //显示 String info = m.getSender() + "对"+m.getGetter()+"说"+ m.getCon()+"\r\n"; this.jta.append(info); } catch (Exception e) { e.printStackTrace(); } } }*/ }
------model-------
package model; import java.io.ObjectInputStream; import java.net.Socket; import view.Chat; import common.Message; 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(); System.out.println("我在ClientConServerThread,读取从服务器发来的消息"+m.getSender()+"给"+m.getGetter()+"内容"+m.getCon()); //把从服务器获得消息,显示到该显示的聊天界面 Chat qqchat0 = ManageQqChat.getQqChat(m.getGetter()+" "+m.getSender()); qqchat0.showMessage(m); //显示 } catch (Exception e) { // TODO: handle exception } } } }
package model; import java.util.HashMap; public class ManageClientConServerThread { private static HashMap hm = new HashMap<String,ClientConServerThread>(); //把创建好的ClientConServerThread放到hm public static void addClientConServerThread(String qqId,ClientConServerThread cost){ hm.put(qqId, cost); } //通过qqId取的线程 public static ClientConServerThread getClientConServerThread(String qqId){ return (ClientConServerThread)hm.get(qqId); } }
package model; import java.util.HashMap; import view.Chat; public class ManageQqChat { private static HashMap hm = new HashMap<String,Chat>(); //加入 public static void addQqChat(String loginIdAnFriendId,Chat qqchat){ hm.put(loginIdAnFriendId,qqchat); } // 取出 public static Chat getQqChat(String loginIdAnFriendId){ return (Chat)hm.get(loginIdAnFriendId); } }
//QqClientConServer类:客户端连接服务器的后台 package model; import java.io.ObjectInputStream; import java.io.ObjectOutputStream; import java.net.Socket; import common.Message; import common.User; public class QqClientConServer { public static Socket s; //6.sendLoginInfoToServer(发送第一次请求)----------->2.QqClientUser的checkUser public boolean sendLoginInfoToServer(Object o){ boolean b = false; try { System.out.println("我在QqClientConServer的try里面"); //1.客户端通过9999端口号通过ObjectOutputStream对象输出流给服务器端发送内容----->2.MyQqServer s = new Socket("127.0.0.1",9999); ObjectOutputStream oos = new ObjectOutputStream(s.getOutputStream()); oos.writeObject(o); //写过去又读回来 //4.下面的ObjectInputStream获取服务器端的ObjectOutputStream输出----->5.进行判断,给sendLoginInfoToServer返回true/false ObjectInputStream ois = new ObjectInputStream(s.getInputStream()); Message ms = (Message)ois.readObject(); //5.进行判断,给sendLoginInfoToServer返回true/false------>6.sendLoginInfoToServer if(ms.getMesType().equals("1")){ //创建一个该qq号和服务器端保持通讯连接的线程 ClientConServerThread ccst = new ClientConServerThread(s); //qq号取出来作为第一个参数,创建的线程作为第二个参数。启动该通讯线程 ccst.start(); ManageClientConServerThread.addClientConServerThread(((User)o).getUserId(), ccst); b = true; } } catch (Exception e) { e.printStackTrace(); }finally{ } return b; } }
package model; import common.User; //2.验证方法是否合法,返回true/false------>7.客户端的QqLogin的actionPerformed public class QqClientUser { public boolean checkUser(User u){ return new QqClientConServer().sendLoginInfoToServer(u); //客户端QqClientConServer的sendLoginInfoToServer。返回一个对象 } }
------common----
package common; //1.实现java.io.Serializable接口, //2.定义私有变量mesType,并将本类拷贝到QQServer类下的common里 public class Message implements java.io.Serializable{ private String mesType; private String sender; private String getter; private String con; private String sendTime; 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 getSendTime() { return sendTime; } public void setSendTime(String sendTime) { this.sendTime = sendTime; } public String getMesType() { return mesType; } public void setMesType(String mesType) { this.mesType = mesType; } }
package 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; } }
服务器端:----------------------QqServer------------------------
--------------view------------(同上)
package model; import java.util.HashMap; public class ManagerClientThread { public static HashMap hm = new HashMap<String,SerConClientThread>(); //向HashMap中添加一个客户端通讯线程 public static void addClientThread(String uid,SerConClientThread ct){ hm.put(uid,ct); } // 获取用户的id public static SerConClientThread getClientThread(String uid){ return (SerConClientThread)hm.get(uid); } }
package model; import java.io.ObjectInputStream; import java.io.ObjectOutputStream; import java.net.ServerSocket; import java.net.Socket; import common.Message; import common.User; public class MyQqServer { /**这是QQ服务器,它在监听,等待某个QQ客户端,未连接 * 1.构造方法MyQqServer---try、finally--- * 2.添加while循环进行循环监听。避免当输入错误时,服务器不能两次登录 */ public static void main(String[] args) { } public MyQqServer(){ try { System.out.println("我在MyQqServer,99999监听"); ServerSocket ss = new ServerSocket(9999); //在9999监听 while(true){ Socket s = ss.accept(); //阻塞、等待连接 //2.接收客户端发来的信息。第一次发的是用户名和密码---(传输到)-->3.服务器端MyQqServer的ObjectOutputStream输出 ObjectInputStream ois = new ObjectInputStream(s.getInputStream()); User u = (User)ois.readObject(); System.out.println("我在MyQqServer,服务器接收用户ID"+u.getUserId()+" 密码 "+u.getPasswd()); //3.调用Message,给mesType赋值--->下面的ObjectOutputStream输出----->4.客户端QqClientConServer的sendLoginInfoToServer的ObjectInputStream ObjectOutputStream oos = new ObjectOutputStream(s.getOutputStream()); Message m = new Message(); if(u.getPasswd().equals("123456")){ m.setMesType("1"); //返回一个成功登录的信息包 oos.writeObject(m); //这里就单开一个线程,让该线程与客户端保持通讯 SerConClientThread scct = new SerConClientThread(s); ManagerClientThread.addClientThread(u.getUserId(),scct); //启动与给客户端通讯的线程 System.out.println("我在MyQqServer:hello"); scct.start(); }else{ m.setMesType("2"); oos.writeObject(m); s.close(); } } } catch (Exception e) { // TODO: handle exception }finally{ } } }
package model; //功能:服务器和某个客户端的通信线程 import java.net.*; import java.io.*; import common.Message; public class SerConClientThread extends Thread{ Socket s; public SerConClientThread(Socket s){ //把服务器和该客户端的连接赋值给s this.s = s ; } public void run(){ while(true){ try { ObjectInputStream ois = new ObjectInputStream(s.getInputStream()); Message m =(Message) ois.readObject(); //将Object转成Message System.out.println("我在SerConClientThread,"+m.getSender()+"给"+m.getGetter()+" 说 "+m.getCon()); //一会完成转发任务。找到接收人的通讯线程,然后发出去 SerConClientThread sc2 = ManagerClientThread.getClientThread(m.getGetter()); ObjectOutputStream oos = new ObjectOutputStream(sc2.s.getOutputStream()); oos.writeObject(m); //将从m里读取到的内容发出去 } catch (Exception e) { // TODO: handle exception } } } }
--------view------
package view; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; import javax.swing.JButton; import javax.swing.JFrame; import javax.swing.JPanel; import model.MyQqServer; public class MyServerFrame extends JFrame implements ActionListener{ /**服务器端的控制界面,可以完成启动服务器,关闭服务器 */ public static void main(String[] args) { MyServerFrame m = new MyServerFrame(); } JPanel jp1; JButton jb1,jb2; public MyServerFrame(){ jb1 = new JButton("开启服务器"); jb1.addActionListener(this); //注册监听 jb2 = new JButton("强制下线"); jp1 = new JPanel(); jp1.add(jb1); jp1.add(jb2); this.add(jp1,"Center"); this.setSize(500,400); this.setVisible(true); } @Override public void actionPerformed(ActionEvent e) { if(e.getSource()==jb1){ new MyQqServer(); } } }

浙公网安备 33010602011771号