GUI聊天系统之总结


/* 学习开发这个聊天系统已经一周总结一下:
 * 1、客户端的接受数据功能要new一个线程来处理;服务器端接受并处理来自客户端的程序时也要
 * new一个线程来处理。就是说:对于一直要在后台服务的代码段要新建个线程来处理;由于服务器要
 * 处理很多个客户端,所以对应每一个客户端都要新建个线程来处理。
 *
 * 2、JFrame关闭按钮默认响应是隐藏(HIDE_ON_CLOSE),所以在给JFrame的关闭按钮添加监听器时,
 * 先设置关闭事件为DO_NOTHING_ON_CLOSE.
 *
 * 3、响应键盘事件(KeyEvent),当按下Ctrl+Enter时发送数据,Enter有键码,Ctrl没有,
 * Ctrl起修饰作用,通过这句:KeyEvent.getModifiersExText(e.getModifiersEx())得到
 * 修饰键的字符串(这里是"Ctrl")
 *
 *
 *  技巧:
 * 1、给组件注册监听器后,可以调用组件的setActionCommand()方法来设置命令字符串。如:
 * 语句:miClose.setActionCommand("退出");把关闭菜单项的命令字符串设置为"退出",因为面板
 * 上退出按钮的命令字符串为"退出",于是在监听器类只需处理命令字符串为"退出"的事件,代码如下:
 * if(e.getActionCommand.equals("退出")){quit();}
 * 2、JOptionPane有几个常用方法:showConfirmDialog 询问一个确认问题,如 yes/no/cancel;
 * showInputDialog 提示要求某些输入; showMessageDialog 告知用户某事已发生;
 * showOptionDialog 上述三项的大统一 。
 *
 *  最后:
 * 程序是调出来的。
 * 注意版本控制,保留以前的版本。
 * 不要混用awt组件和swing组件。
 * 参看API文档是好习惯。
 *
 *///客户端:chatClient.java

 import java.awt.BorderLayout;
import java.awt.Container;
import java.awt.Dimension;
import java.awt.Toolkit;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.KeyAdapter;
import java.awt.event.KeyEvent;
import java.awt.event.WindowAdapter;
import java.awt.event.WindowEvent;
import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.io.IOException;
import java.net.ConnectException;
import java.net.Socket;
import java.net.UnknownHostException;

import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JMenu;
import javax.swing.JMenuBar;
import javax.swing.JMenuItem;
import javax.swing.JOptionPane;
import javax.swing.JPanel;
import javax.swing.JScrollPane;
import javax.swing.JTextArea;

/**
 * @author hanwei
 *
 */
public class chatClient {

 private JFrame frame = new JFrame("Chat");
 private JTextArea input = new JTextArea(3,32);
 private JTextArea output = new JTextArea(10,37);
 private JScrollPane inputsp = new JScrollPane(input);
 private JScrollPane outputsp = new JScrollPane(output);
 private JButton send = new JButton("发送");
 private JButton quit = new JButton("退出");
 private JButton conn = new JButton("连接");
 private JPanel jp = new JPanel();
 private Container container = frame.getContentPane();
 private JMenuBar mb = new JMenuBar();
 private JMenu mFile = new JMenu("文件");
 private JMenu mHelp = new JMenu("帮助");
 private JMenuItem miClose = new JMenuItem("关闭");
 private JMenuItem miConn = new JMenuItem("连接");
 private JMenuItem miAbout = new JMenuItem("关于");
 
 private Socket sk = null;
 private DataOutputStream dos = null;
 private DataInputStream dis = null;
 Thread getMsgThread = null;
 private boolean connected = false;
//窗体初始化
 public void launchFrame(){
  //JFrame关闭按钮默认响应是隐藏(HIDE_ON_CLOSE),
  //所以先设置关闭事件为DO_NOTHING_ON_CLOSE.
  frame.setDefaultCloseOperation(JFrame.DO_NOTHING_ON_CLOSE);
  frame.addWindowListener(new WindowAdapter(){
   public void windowClosing(WindowEvent e) {
    quit();
    return;
   }
  });
  send.setToolTipText("快捷键:Ctrl+Enter");
  quit.addActionListener(new ActionHandler());
  send.addActionListener(new ActionHandler());
  conn.addActionListener(new ActionHandler());
  
  jp.setLayout(new BorderLayout());
  output.setEditable(false);
  input.setToolTipText("发送快捷键:Ctrl+Enter");
  input.addKeyListener(new ActionHandler());
  jp.add(conn,BorderLayout.SOUTH);
  jp.add(send,BorderLayout.CENTER);
  jp.add(quit,BorderLayout.NORTH);
  container.add(jp,BorderLayout.EAST);
  container.add(inputsp,BorderLayout.SOUTH);
  container.add(outputsp,BorderLayout.CENTER);
  
  miClose.addActionListener(new ActionHandler());
  miClose.setActionCommand("退出");
  miAbout.addActionListener(new ActionHandler());
  miConn.addActionListener(new ActionHandler());
  mFile.add(miConn);
  mFile.addSeparator();
  mFile.add(miClose);
  mHelp.add(miAbout);
  mb.add(mFile);
  mb.add(mHelp);
  frame.setJMenuBar(mb);
  
  frame.setVisible(true);
  frame.pack();
  Dimension screenSize = Toolkit.getDefaultToolkit().getScreenSize();
  frame.setLocation((screenSize.width-frame.getWidth())/2
    ,(screenSize.height-frame.getHeight())/2);
 }
//事件监听内部类
 class ActionHandler extends KeyAdapter implements ActionListener{
  @Override
  public void keyPressed(KeyEvent e) {
   /*System.out.println("KeyCode"+e.getKeyCode()
     +"  KeyChar"+e.getKeyChar());
   System.out.println(KeyEvent.VK_ENTER);
   System.out.println(KeyEvent.getModifiersExText(e.getModifiersEx()));
   */
   if(KeyEvent.getModifiersExText(e.getModifiersEx()).equals("Ctrl")
     &&e.getKeyCode()==KeyEvent.VK_ENTER){
    sendMsg();
    return;
   }
   super.keyTyped(e);
  }

  public void actionPerformed(ActionEvent e) {
   String ActionCommand=e.getActionCommand();
   if(ActionCommand.equals("退出")){
    quit();
    return;
   }
   if(ActionCommand.equals("发送")){
    sendMsg();
    return;
   }
   if(ActionCommand.equals("连接")){
    conn();
    return;
   }
   if(ActionCommand.equals("关于")){
    about();
    return;
   }
  }
 }
//连接到服务器
 public void conn(){
  try {
   if(!connected){
    sk = new Socket("127.0.0.1",8888);
    dos = new DataOutputStream(sk.getOutputStream());
    dis = new DataInputStream(sk.getInputStream());
    connected = true;
    getMsg();
    output.append("连接上服务器:"
      +sk.getRemoteSocketAddress());
   }else{//connected==true时,即已经连接上服务器时
    JOptionPane.showMessageDialog(frame,"已连接上服务器:"
      +sk.getRemoteSocketAddress());
   }
  } catch (ConnectException e) {
   JOptionPane.showMessageDialog(frame,"请先启动服务器!");
  } catch (UnknownHostException e) {
   e.printStackTrace();
  } catch (IOException e) {
   e.printStackTrace();
  }
 }
//发送数据
 public void sendMsg() {
  if(!connected){
   JOptionPane.showMessageDialog(frame,"请先连接到服务器!");
   return;
  }
  String str = input.getText();
  if(!str.equals("")&&str!=null){
   input.setText("");
   try {
    dos.writeUTF(str);
    dos.flush();
   } catch (IOException e1) {
    e1.printStackTrace();
//    connected = false;
   }
  }else{
   JOptionPane.showMessageDialog(frame,"发送内容不能为空!");
  }
 }
 
 class getMsg implements Runnable{
  public void run() {
   try {
    while(connected){
     String str = dis.readUTF();
     output.append("/n"+str);
    }
   } catch (IOException e) {
    try {
     if (dis != null) {
      dis.close();
      dis = null;
     }
    } catch (IOException e1) {
     e1.printStackTrace();
    }//这地方偶尔抛出异常java.net.SocketException: Socket closed,
    //可我有时狂点测试却没有抛出异常,很奇怪。所以只好把下面这行注释
    //e.printStackTrace();
   }
  }
 }
//接收数据
 public void getMsg(){ 
  getMsg gm = new getMsg();
  getMsgThread = new Thread(gm);
  getMsgThread.start();
 }
//关闭事件
 @SuppressWarnings("deprecation")
 public void quit() {
  int yes =JOptionPane.showConfirmDialog(frame, "真的要退出吗?"
    ,"退出",JOptionPane.YES_NO_OPTION);
  if(yes==0){
//   connected = false;
   try {
    if(dos!=null){
     dos.close();
     dos = null;
    }
    if(dis!=null){
     dis.close();
     dis = null;
    }
    if(sk!=null){
     sk.close();
     sk = null;
    }
   } catch (Exception e) {
    e.printStackTrace();
   }
   if(getMsgThread!=null){
    getMsgThread.stop();
    getMsgThread=null;
   }
   frame.dispose();
   System.exit(0);
  }
 }
//关于菜单事件
 public void about() {
  String str = "Chat v1.2/n"
   +"作者:/n西安邮电学院/n东软2班/n计科0401/n寒微";
  JOptionPane.showMessageDialog(frame, str
    , "Chat 关于", JOptionPane.INFORMATION_MESSAGE);
 }
//main方法在这
 public static void main(String[] args) {
  chatClient chat = new chatClient();
  chat.launchFrame();
 }
}

 

服务器:chatServer.java

import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.io.EOFException;
import java.io.IOException;
import java.net.BindException;
import java.net.ServerSocket;
import java.net.Socket;
import java.util.ArrayList;
import java.util.List;

import javax.swing.JOptionPane;

/**
 * @author hanwei
 *
 */
public class chatServer {

 private ServerSocket ss = null;
 private List<acceptClient> clients = new ArrayList<acceptClient>();

 public static void main(String[] args) {
  new chatServer().start();
 }

 public void start() {
  boolean serverstarted = false;
  try {
    ss= new ServerSocket(8888);
    serverstarted = true;
    System.out.println("服务器正在启动!");
  }catch(BindException be){
   JOptionPane.showMessageDialog(null, "服务器已经启动!");
   System.exit(0);
  }catch (IOException e) {
   e.printStackTrace();
  }
  try {
   int i=0;//连接到服务器的客户端数
   while(serverstarted){
    Socket sk = ss.accept();
    System.out.println("第"+(++i)+"个客户端连接到服务器!");
    acceptClient ac=new acceptClient(sk);//启动一个线程来处理一个客户端
    clients.add(ac);
    new Thread(ac).start();
   }
  } catch (IOException e) {
   e.printStackTrace();
  }finally {
   try {
    if(ss!=null) ss.close();
   } catch (IOException e) {
    e.printStackTrace();
   }
  }
 }
//acceptClient内部类,线程类,处理连接到服务器的客户端所发来的数据。
 class acceptClient implements Runnable{
  private DataInputStream dis  = null;
  private DataOutputStream dos  = null;
  private Socket sk = null;
  private boolean connected = false;
  
  public acceptClient(Socket s){
   sk = s;
   try {
    dis = new DataInputStream(sk.getInputStream());
    dos = new DataOutputStream(sk.getOutputStream());
    connected = true;
   } catch (IOException e) {
    e.printStackTrace();
   }
  }
  
  public void run() {
   try{ 
    while(connected){
     String str = dis.readUTF();
     System.out.println("客户端("+this.sk.getRemoteSocketAddress()+"):"+str);
     for(int i=0;i<clients.size();i++){
      acceptClient c = clients.get(i);
     // if(!this.equals(c))
       c.send(this.sk.getRemoteSocketAddress()+"::"+str);
     }
    }
   } catch (EOFException e) {
    System.out.println("客户端("
      +this.sk.getRemoteSocketAddress()+")已关闭!");
    clients.remove(this);
//    if(sk!=null) sk.close();
   } catch (IOException e) {
    e.printStackTrace();
   } finally{
    connected = false;
    try {
     if(dis!=null){
      dis.close();
      dis = null;
     }
     if(dos!=null){
      dos.close();
      dos = null;
     }
     if(sk!=null){
      sk.close();
      sk  =null;
     }
    } catch (IOException e) {
     e.printStackTrace();
    }
   }
  }
  
  public void send(String str){
   try {
    dos.writeUTF(str);
    dos.flush();
   } catch (IOException e) {
    e.printStackTrace();
   }
  }
 }
}
 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值