使用socket的阻塞简单通信聊天工具

来源:互联网 发布:淘宝千里眼是什么 编辑:程序博客网 时间:2024/05/22 00:31
 
服务端:
package Server;
import Server.ServerThread;
import javax.swing.*;
import java.awt.*;
import java.awt.event.*;
import java.io.*;
import java.net.*;
import java.util.*;
import java.util.concurrent.Executor;
public class ChatServer extends Thread implements ActionListener{
 JLabel jlport,jlsendmsg;
 JTextField jtfport,jtfsendmsg;
 JButton jbaccept,jbsend;
 JTextArea jtamsg;
 JScrollPane jsp;
 JComboBox combox;
 
 ServerSocket server;
 Socket socket;
 ServerThread sthread;
 
 
 String time;
 public ChatServer()
 {
  JFrame jf = new JFrame("服务器");
  jlport = new JLabel("端口号");
  jlport.setBounds(new Rectangle(40,25,80,30));
  jtfport = new JTextField("8888");
  jtfport.setBounds(new Rectangle(110,25,300,30));
  
  jbaccept = new JButton("侦听");
  jbaccept.setBounds(new Rectangle(440,25,80,30));
  
  jlsendmsg = new JLabel("输入消息");
  jlsendmsg.setBounds(new Rectangle(40,65,80,30));
  
  jtfsendmsg = new JTextField();
  jtfsendmsg.setBounds(new Rectangle(110,65,210,30));
  
  combox = new JComboBox();
  combox.setBounds(new Rectangle(340,65,80,30));
  combox.addItem("所有人");
  
  jbsend = new JButton("发送");
  jbsend.setBounds(new Rectangle(440,65,80,30));
  
  jtamsg = new JTextArea();
  jtamsg.setEditable(false);
  jsp = new JScrollPane(jtamsg);
  jsp.setBounds(new Rectangle(40,105,480,240));
  
  jbaccept.addActionListener(this);
  jbsend.addActionListener(this);
  jf.setLayout(null);
  jf.add(jlport);
  jf.add(jtfport);
  jf.add(jbaccept);
  jf.add(jlsendmsg);
  jf.add(jtfsendmsg);
  jf.add(combox);
  jf.add(jbsend);
  jf.add(jsp);
  jf.setTitle("服务器");
  jf.setSize(550,400);
  jf.setVisible(true);
  jf.setResizable(false); 
                                                                                                                                                      
  jf.addWindowListener(new WindowAdapter()
  {
   public void windowClosing(WindowEvent e)
            {
    sthread = new ServerThread();
    try
    {
     sthread.sendtoAll("01");
    }
    catch (IOException e1)
    {
     // TODO Auto-generated catch block
     e1.printStackTrace();
    }
       try
       {
        JOptionPane.showMessageDialog(null, "服务器正在关闭","提示",JOptionPane.INFORMATION_MESSAGE);
     Thread.sleep(300);
    }
       catch (InterruptedException e1)
       {
     // TODO Auto-generated catch block
     e1.printStackTrace();
    } 
    System.exit(0); 
            }
  });
  
 }
 //事件处理
 public void actionPerformed(ActionEvent e){
  //侦听按钮
     if(e.getSource()==jbaccept){
      jtfport.setEditable(false);
   jbaccept.setEnabled(false);
         int port=Integer.parseInt(jtfport.getText().trim()); 
         try
         {
    server = new ServerSocket(port);
    jtamsg.append("服务器正在端口"+port+"侦听......\n");
    this.start();
   }
         catch (IOException e1)
         {
    JOptionPane.showMessageDialog(null, "端口已被占用!请稍后再尝试侦听,/n或者更改端口号","提示",
      JOptionPane.INFORMATION_MESSAGE);
    jtfport.setEditable(true);
    jbaccept.setEnabled(true);
    jtamsg.append("端口已被占用!请稍后再尝试侦听,或者更改端口号\n");
   }
   
     }
     if(e.getSource()==jbsend){
      String s=jtfsendmsg.getText().trim();
      String towho = (String)combox.getSelectedItem();
      sthread = new ServerThread();
      if(towho.equals("所有人"))
      {
       s = "02服务器 对 所有人  说:"+s;
       try {
     sthread.sendtoAll(s);
    }
       catch (IOException e1)
       {
     // TODO Auto-generated catch block
     e1.printStackTrace();
    }
    showTime();
    jtamsg.append(s+"\n");
      }
      else
      {
       String s1 = "02服务器对你说:"+s;
       try
       {
     sthread.sendtoOne(towho, s1);
    } catch (IOException e1)
    {
     // TODO Auto-generated catch block
     e1.printStackTrace();
    }
    showTime();
    s="服务器对"+combox.getSelectedItem()+"说:"+s;
    jtamsg.append(s);
      }
        
     }
   }
 
 
 //主程序线程run方法
 public void run()
 {
   while(true)
         {
             try
             {
              socket = server.accept();
              sthread = new ServerThread();
              sthread.socket = socket;
              sthread.jcb = combox;
              sthread.jta = jtamsg;
                 sthread.start();
                 showTime();
                 jtamsg.append("有客户连接服务器\n");
             }
             catch(IOException e)
             {
                 jtamsg.append("**ERROR**"+e);
             }
         }
 }
 
 /**
  * 显示时间用的,
  */
 public void showTime()
 {
  Date date = new Date();
  time = date.toLocaleString();
  jtamsg.append(time+"\n");
 }
 
 public static void main(String arg[])
 {
  new ChatServer();
 }
}
 
 
package Server;
import java.net.*;
import java.util.*;
import java.io.*;
import javax.swing.*;
public class ServerThread extends Thread{
 private static Hashtable<String, Socket> hasht = new Hashtable<String, Socket>();
 Socket socket;
 JComboBox jcb;
 JTextArea jta;
 static String allname = "所有人";
 String time;
 
 
 
 /**
  * run方法实现消息的接受和发生。
  * 用一个哈希表hasht来存用户的姓名及socket
  * 便于实现私聊的功能
  */
 public void run()
 {
  InputStreamReader is=null;
  OutputStreamWriter os=null;
  BufferedReader br =null;
  try
  {
   is = new InputStreamReader(socket.getInputStream());
         os = new OutputStreamWriter(socket.getOutputStream());
         br = new BufferedReader(is);
         String name = br.readLine();
         
         jcb.addItem(name);
         PrintWriter ps = new PrintWriter(os,true);
         //添加用户
         hasht.put(name, socket);
         ps.println("登陆成功");        
          
         sendtoAll("04"+name);
         allname=allname+"&"+name;
         //接受并发送消息
         Thread.sleep(100);
         sendtoOne(name, "05"+allname);
        
         //循环接受并发送消息
         while(true)
         {
                       
          String msg = br.readLine();
          
          String mark=msg.substring(0, 2);
          
          
          
          
          //判断是否有用户下线,有责通知所有人,并删除该用户
          if(mark.equals("03"))
          {
           //String name=
           jcb.removeItem(name);
           hasht.remove(name);
           showTime();
           jta.append(name+"下线了\n");
           sendtoAll("03"+name);
           
          }
          
          //对个人私聊的处理
          else if(mark.equals("02"))
          {
           String setName=msg.substring(2,12).trim();
           String msgText=msg.substring(12);
           sendtoOne(setName,"02"+name+"对你说:"+msgText);
           
          }
          //对所有人发送消息的处理
          else if(mark.equals("04"))
          {
           String msgText=msg.substring(2);
           msg = name+" 对  所有人  说:"+msgText;
           sendtoAll("02"+msgText);
           showTime();
           jta.append(msgText+"\n");
          }
          else {
     
    }
          
          
         }
       
        
        
  }
  catch(Exception e)
  {
   e.getStackTrace();
  }
  finally{
   try {
    is.close();
    os.close();
    br.close();
    
   } catch (Exception e2) {
    // TODO: handle exception
   }
   
  }
  
 }
 
 /**
  * 实现了向所有人发送消息
  * 通过遍历哈希表 h,向所有建立连接的
  * scoket发送消息,达到了向所有人发送消息的目的
  * @param hasht      哈希表
  * @param msg    要发送的消息
  * @throws IOException  异常处理
  */
 public void sendtoAll(String msg) throws IOException
 {
  Enumeration<Socket> em = hasht.elements();
  
  while(em.hasMoreElements())
  {
   Socket toAllsocket = (Socket)em.nextElement();  
   OutputStreamWriter opsw = new OutputStreamWriter(toAllsocket.getOutputStream());
   PrintWriter pwriter = new PrintWriter(opsw,true);
   pwriter.println(msg);
  }
 }
 
 public void land(String msg) throws IOException
 {
  Enumeration<Socket> em = hasht.elements();
  
  while(em.hasMoreElements())
  {
   Socket toAllsocket = (Socket)em.nextElement();  
   OutputStreamWriter opsw = new OutputStreamWriter(toAllsocket.getOutputStream());
   PrintWriter pwriter = new PrintWriter(opsw,true);
   pwriter.println(msg);
   
  }
 }
 /**
  * 函数实现了向某个指定的人发送消息,而其他人是收不到的,即私聊功能
  * 函数通过名字,查找哈希表中对应的socket,
  * 再获取socket的输出流,然后向输出流中写入信息
  * @param h  哈希表
  * @param name  要发送的姓名
  * @param msg   发送的消息
  * @throws IOException 异常处理
  */
 
 public void sendtoOne(String name,String msg) throws IOException
 {
  Socket toOnesocket =(Socket)hasht.get(name);
  OutputStreamWriter opsw = new OutputStreamWriter(toOnesocket.getOutputStream());
  PrintWriter pwriter = new PrintWriter(opsw,true);
  pwriter.println(msg);
 }
 
 /**
  * 显示时间用的,
  */
 
 public void showTime()
 {
  Date date = new Date();
  time = date.toLocaleString();
  jta.append(time+"\n");
 }
}
 
 
客户端:
package Client;
import javax.swing.*;
import java.awt.*;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.WindowAdapter;
import java.awt.event.WindowEvent;
import java.io.*;
import java.net.Socket;
import java.util.Date;
public class ChatClient extends JFrame implements ActionListener,Runnable{
 JTextArea jtamsg;
 JTextField jtasname,jtaport,jtfsendmsg,jtfname;
 JLabel jlsname,jlsport,jlmsg,jlname;
 JButton jbcon,jbsend;
 JScrollPane jsp;
 JComboBox combox;
 
 static Socket socket = null;
 static InputStreamReader is;
 static OutputStreamWriter os;
 BufferedReader br;
 PrintWriter pw;
 
 String ip;
 int port;
 String name;
 String message;
 String strcombox;
 String time;
 Thread thread;
 
 /**
  * 构造方法是实现窗口的界面布局
  */
 public ChatClient()
 {
  jlsname = new JLabel("服务器名");
  jlsname.setBounds(new Rectangle(40,25,80,30));
  jtasname = new JTextField("192.168.0.20");
  jtasname.setBounds(new Rectangle(110,25,80,30));
  
  jlsport = new JLabel("端口");
  jlsport.setBounds(new Rectangle(210,25,80,30));
  jtaport = new JTextField("8888");
  jtaport.setBounds(new Rectangle(240,25,50,30));
  
  jlname = new JLabel("姓名");
  jlname.setBounds(300,25,50,30);
  jtfname = new JTextField();
  jtfname.setBounds(340,25,80,30);
  
  jbcon = new JButton("连接");
  jbcon.setBounds(new Rectangle(440,25,80,30));
  
  jlmsg = new JLabel("输入信息");
  jlmsg.setBounds(new Rectangle(40,65,80,30));
  
  jtfsendmsg = new JTextField();
  jtfsendmsg.setBounds(new Rectangle(110,65,210,30));
  
  combox = new JComboBox();
  combox.setBounds(new Rectangle(340,65,80,30));
  combox.addItem("所有人");
  
  jbsend = new JButton("发送");
  jbsend.setBounds(new Rectangle(440,65,80,30));
  
  jtamsg = new JTextArea();
  jtamsg.setEditable(false);
  jsp = new JScrollPane(jtamsg);
  jsp.setBounds(new Rectangle(40,105,480,240));
  
  jbcon.addActionListener(this);
  jbsend.addActionListener(this);
  this.setLayout(null);
  this.add(jlsname);
  this.add(jtasname);
  
  this.add(jlsport);
  this.add(jtaport);
  this.add(jbcon);
  
  this.add(jlname);
  this.add(jtfname);
  
  this.add(jlmsg);
  this.add(jtfsendmsg);
  this.add(combox);
  this.add(jbsend);
  this.add(jsp);
  
  this.setSize(550, 400);
  this.setResizable(false);
  this.setVisible(true);
  
  
  //添加关闭窗口事件处理,发送下线消息
  this.addWindowListener(new WindowAdapter()
  {
   public void windowClosing(WindowEvent e)
            {
    if(socket == null)
     System.exit(0);
    else
    {
     sendlikai();
     System.exit(0);
    }
     
            }
  });
 }
 
 /**
  * 事件处理函数
  * 有连接和发送两个事件
  * 连接:判断基本信息是否填写好;
  *      如果填写好,则调用connect()函数建立连接
  *      接着禁用连接按钮及相关编辑框
  * 发送:判断是对个人说还是对所有人说
  *      根据对象的不同对message进行加工,如对个人说是
  *      将message改为message@name
  *      最后都是通过send()函数发送消息
  */
 public void actionPerformed(ActionEvent e)
 {
  //连接事件处理
  if(e.getSource() == jbcon)
  {
   name = jtfname.getText().trim();
   ip = jtasname.getText().trim();
   this.setTitle("客户端:"+name);
   port = Integer.parseInt(jtaport.getText().trim());
   if(!name.equals("")&&!ip.equals("")&&!jtaport.getText().trim().equals(""))
   {
    if(name.length()>10)
    {
     JOptionPane.showMessageDialog(null, "名字在10个字符以内" +
       "请谅解!","提示",JOptionPane.INFORMATION_MESSAGE);
     
    }
    else {
     
    
    try {
     connect(ip,port);
     jtasname.setEditable(false);
     jtaport.setEditable(false);
     jtfname.setEditable(false);
     jbcon.setEnabled(false);
    } catch (Exception e1) {
     
     JOptionPane.showMessageDialog(null, "服务器未启动或者异常,请稍后再尝试\n连接,给您带来不便," +
       "请谅解!","提示",JOptionPane.INFORMATION_MESSAGE);
     jtasname.setEditable(true);
     jtaport.setEditable(true);
     jtfname.setEditable(true);
     jbcon.setEnabled(true);
    }
    }
    
   }
   else JOptionPane.showMessageDialog(null, "请填写基本信息","提示",JOptionPane.INFORMATION_MESSAGE);
  }
  
  //发送事件处理
  if(e.getSource() == jbsend)
  {
         if(!jtfsendmsg.getText().trim().equals(""))
         {
          message = jtfsendmsg.getText();
          strcombox = (String) combox.getSelectedItem();
          if(strcombox.equals("所有人"))
          {
           send("04"+message);
          }
          else
          {
           showTime();
           while(strcombox.length()<10)
            strcombox+=" ";
                 jtamsg.append("你  对  "+strcombox+" 说:"+message+"\n");
           message = "02"+strcombox+message;
           
           send(message);
          }
          jtfsendmsg.setText("");
         }
         else
          JOptionPane.showMessageDialog(null, "发送内容不能为空,请重新发送","提示",JOptionPane.WARNING_MESSAGE);
  }
 }
 
 /**
  * 函数实现了连接指定服务器的功能
  * 1、创建指定(用户定义)服务器端口号的Socket对象
  * 2、创建与S哦创可贴对象绑定的输入输出流,并建立相应的数据输入输出流
  * @param ip    服务器IP
  * @param port  服务器端口号
  * @throws Exception 异常处理
  */
 
 public void connect(String ip,int port) throws Exception
 {
  socket = new Socket(ip,port);
  is = new InputStreamReader(socket.getInputStream());
  os = new OutputStreamWriter(socket.getOutputStream());
  
  BufferedReader br = new BufferedReader(is);
  PrintWriter pw = new PrintWriter(os,true);
  pw.println(name);
  String login = br.readLine();
  jtamsg.append(login+"\n");
  
  thread = new Thread(this);
  thread.start();
 }
 
 /**
  * 下线通知函数
  */
 public void sendlikai()
 {
  pw = new PrintWriter(os,true);
     pw.println("03");
 
 }
 
 /**
  * 发送消息函数
  * 将消息写入数据输出流中输出
  * @param message  要发送的消息
  */
 public void send(String message)
 { 
  pw = new PrintWriter(os,true);
  pw.println(message);
 }
 
 /**
  * 函数实现了消息的接受和显示
  * 首先创建也Socket绑定的输入流
  * 其次建立相应的数据输入流,并读取消息
  * 消息分为3类,一类是用户在线情况通知,以“&”区分
  * 一类是用户下线通知,以“下线”区分
  * 最后一类是聊天的消息
  * 将着三类消息进行处理
  */
 public void run()
 {
  while(true)
  {
   try
   {
    InputStreamReader ins = new InputStreamReader(socket.getInputStream());
    BufferedReader bufin = new BufferedReader (ins);
    String msg = bufin.readLine();
    
    String operationId =msg.substring(0, 2);
    
    
    //此处用switch代替if结构更加容易维护等。。。
    if(operationId.equals("01"))
    {
     ///前2位01代表服务器关闭
     showTime();
     jtamsg.append("服务器已关闭,程序将在3秒之后关闭");
     JOptionPane.showMessageDialog(null, "服务器已关闭,程序将在3秒后关闭!","警告!!",
       JOptionPane.WARNING_MESSAGE);
     Thread.sleep(3000);
     System.exit(0);
    
    }
    else if(operationId.equals("02")){
     ///前2位02代表接收消息
     String getMsg=msg.substring(2);//02代号下第三位后面都是消息
     showTime();
     jtamsg.append(getMsg+"\n");
     Thread.sleep(300);
    }
    else if (operationId.equals("03")) {
     ///前2位03代表下线
     String name=msg.substring(2);//下线人账号
        combox.removeItem(name);
        jtamsg.append(name+"下线");
    }
    else if (operationId.equals("04")) {
     //前2位04表示有人上线
     String name=msg.substring(2);//上线人账号
        combox.addItem(name);
        jtamsg.append(name+"上线\n");
    }
    else if (operationId.equals("05")) {
     //接收所有在线名单
     String[] allname=msg.substring(2).split("&");
     combox.removeAllItems();
     
     for(int i=0;i<allname.length;i++)
     {
      
      combox.addItem(allname[i]);
     }
     combox.removeItem(name);
    }
    
    
    
    
   }catch(Exception e)
   {
    e.getStackTrace();
   }
  }
 }
 
 /**
  * 显示时间函数,目的是压缩代码
  */
 public void showTime()
 {
  Date date = new Date();
  time = date.toLocaleString();
  jtamsg.append(time+"\n");
 }
 
 
 public static void main(String arg[])
 {
  new ChatClient();
 }
}