我已经实现了简单的客户端与服务器聊天,用的是tcp
我觉得我还需要两个步骤才能实现
1.可以连多个客户端到服务端,并且服务端都可以看到客户端所发信息。
2.各个客户端可以看到其他客户端发送的信息。
我想问,这两步应该怎样实现,我最大的问题是不知道怎么实现加入多线程的部分,我知道可以在服务器端用一个List把多个ServerSocket对象装起来,然后客户端没什么变化。但这具体点要怎么实现呢?求大哥哥大姐姐帮帮小弟。

解决方案 »

  1.   

    用队列吧,比如Queue每个线程都接收从各自客户端发来的信息,然后保存到队列里面
    从队列读取信息,发给每个客户端。
      

  2.   

    public class J_ChatServer extends JFrame
    {
    private ObjectInputStream m_input;
    private ObjectOutputStream m_output;
    private JTextField m_enter;
    private JTextArea m_display;
    private int m_clientNumber=0;

    public J_ChatServer()
    {
    super("聊天程序服务器端");
    Container c=getContentPane();
    m_enter=new JTextField();
    m_enter.setEnabled(false);
    m_enter.addActionListener(new ActionListener()
    {
    public void actionPerformed(ActionEvent event)
    {
    try
    {
    String s=event.getActionCommand();
    m_output.writeObject(s);
    m_output.flush();
    mb_displayAppend("服务器端:"+s);
    m_enter.setText("");
    }
    catch(Exception e)
    {
    System.err.println(e);
    e.printStackTrace();
    }
    }
    }
    );
    c.add(m_enter,BorderLayout.NORTH);
    m_display=new JTextArea();
    c.add(new JScrollPane(m_display),BorderLayout.CENTER);
    }
    public void mb_displayAppend(String s)
    {
    m_display.append(s+"\n");
    m_display.setCaretPosition(m_display.getText().length());
    m_enter.requestFocusInWindow();
    }
    public boolean mb_isEndSession(String m)
    {
    if(m.equalsIgnoreCase("q"))
    return (true);
    if(m.equalsIgnoreCase("quit"))
    return (true);
    if(m.equalsIgnoreCase("exit"))
    return (true);
    if(m.equalsIgnoreCase("end"))
    return (true);
    if(m.equalsIgnoreCase("结束"))
    return (true);
    return false;

    }
    public void mb_run()
    {
    try
    {
    ServerSocket server=new ServerSocket(5000);
    String m;
    while(true)
    {
    m_clientNumber++;
    mb_displayAppend("等待连接["+m_clientNumber+"]");
    Socket s=server.accept();
    mb_displayAppend("接收到客户端链接["+m_clientNumber+"]");
    m_output=new ObjectOutputStream(s.getOutputStream());
    m_input=new ObjectInputStream(s.getInputStream());
    m_output.writeObject("连接成功");
    m_output.flush();
    m_enter.setEnabled(true);
    do
    {
    m=(String)m_input.readObject();
    mb_displayAppend("客户端:"+m);
    }while(!mb_isEndSession(m));
    m_output.writeObject("q");
    m_output.flush();
    m_enter.setEnabled(false);
    m_output.close();
    m_input.close();
    s.close();
    mb_displayAppend("连接["+m_clientNumber+"]结束");
    }
    }
    catch(Exception e)
    {
    System.err.println(e);
    e.printStackTrace();
    mb_displayAppend("连接["+m_clientNumber+"]发生异常");
    }
    }
    public static void main(String args[])
    {
    J_ChatServer app =new J_ChatServer();
    app.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
    app.setSize(350,150);
    app.setVisible(true);
    app.mb_run();
    }}
    这个是我的服务器代码,能帮我改改么,,真心不会!!
      

  3.   

    这儿有个线程的代码,你可以看一下:
    import java.io.*;
    import java.net.*;
    import java.util.*;public class J_ChatThread extends Thread
    {
    Socket s;
    Map sockets;
    String name;
    public J_ChatThread(String n,Socket s,Map m)
    {
    super();
    this.s=s;
    this.sockets=m;
    this.name=n;
    }
    public void run()
    {
    try 
    {
    BufferedReader in = new BufferedReader(new InputStreamReader(s.getInputStream()));
    while(true)
    {
    String text = in.readLine();
    StringTokenizer st = new StringTokenizer(text,"$");
    String from=st.nextToken();
    String to = st.nextToken();
    if(to.equals("ALL"))
    {
    Collection values = sockets.values();
    Iterator it = values.iterator();
    while(it.hasNext())
    {
    Socket s1 = (Socket) it.next();
    PrintWriter out = new PrintWriter(s1.getOutputStream());
    out.println("Text:" +text);
    out.flush();
    }
    }
    else
    {
    Socket s2 = (Socket)sockets.get(to);
    PrintWriter out = new PrintWriter(s2.getOutputStream());
    out.println("Text:" + text);
    out.flush();
    }
    }
    }
    catch(Exception e)
    {
    e.printStackTrace();
    }
    finally
    {
    try
    {
    this.sockets.remove(name);
    Collection value=sockets.values();
    Iterator it = value.iterator();
    while(it.hasNext())
    {
    Socket s2 = (Socket) it.next();
    PrintWriter pw = new PrintWriter(s2.getOutputStream());
    pw.println("Del" + name);
    pw.flush();
    }
    }
    catch(IOException e)
    {
    e.printStackTrace();

    }
    }
    }
    }
      

  4.   

    //下面是我写的控制台聊天室小程序,希望对你有帮助
    //下面是服务器端的代码
    import java.net.*;
    import java.io.*;
    import java.io.PrintStream;
    import java.util.*;
    interface CrazyProtocol
    {
         int PROTOCOL_LEN=2;      //定义协议字符串长度
         String MSG_ROUND="MM";   //发群聊信息
         String USER_ROUND="UU";  //用户名前缀
         String LOGIN_SUCCESS="1";//用户名成功
         String NAME_PER="-1";    //用户名重复
         String PRIVATE_ROUND="PP";;//私聊前缀
         String SPLIT_SIGN="SS";  //字符串分隔
    }
    class CrazyMap<K,V> extends HashMap<K,V>
    {
         public void removeByValue(Object value)//根据value删除指定项
         {
              for(Object key:keySet())
                   if(get(key)==value)
                   {
                        remove(key);
                        break;
                   }
         }
         public Set<V> valueSet()//获取所有value组成的Set集合
         {
              Set<V> valueSet=new HashSet<V>();
              for(K key:keySet())
                   valueSet.add(get(key));
              return valueSet;
         }
         public K getKeyByValue(V value)//根据value查找指定的key
         {
              for(K key:keySet())
                   if(get(key).equals(value) && get(key)==value)
                        return key;
              return null;
         }
         //重写HashMap的put方法,该方法不允许value重复
         public V put(K key,V value)
         {
              for(V val : valueSet())
                   if (val.equals(value) && val.hashCode()==value.hashCode())
                        throw new RuntimeException("MyMap中不允许重复的value!");
              return super.put(key,value);          
         }
    }
    class Server
    {
         private static final int PORT=30000;
         public static CrazyMap<String,PrintStream> clients=new CrazyMap<>(); 
         public static void main(String[] args)
         {
              try(ServerSocket ss=new ServerSocket(PORT))
              {
                   System.out.println("服务器启动成功");
                   while(true)
                   {
                        Socket socket=ss.accept();
                        new Thread(new ServerThread(socket)).start();
                   }
              }
              catch(IOException e)
              {
                   System.out.println("服务器启动超时,知否端口"+PORT+"已被占用");
              }
         }
    }
    class ServerThread implements Runnable
    {
         private Socket s;
         BufferedReader br=null;
         PrintStream ps=null;
         public ServerThread(Socket s)
         {
              this.s=s;
         }
         //将读取的内容去掉前后的协议,恢复成真正数据
         public String getRealMsg(String line)
         {
              return line.substring(CrazyProtocol.PROTOCOL_LEN,(line.length()-CrazyProtocol.PROTOCOL_LEN));
         }
         public void run()
         {
              try
              {
                   br=new BufferedReader(new InputStreamReader(s.getInputStream()));
                   ps=new PrintStream(s.getOutputStream());
                   FileOutputStream fos=new FileOutputStream("fuwuqi.txt");
                   fos.write(new String("").getBytes()); //清空文本内容
                   PrintStream ser=new PrintStream(fos);
                   ser.println("服务器启动成功!");
                   String line=null;
                   while((line=br.readLine())!=null)
                   {
                        //如果读到的行是以CrazyProtocol.USER_ROUND开头,并以其结束,则可以确定读到的是用户登陆的用户名
                        if(line.startsWith(CrazyProtocol.USER_ROUND) && line.endsWith(CrazyProtocol.USER_ROUND))
                        {
                             String userName=getRealMsg(line);//得到真实姓名
                             if(Server.clients.containsKey(userName))
                             {
                                  System.out.println("用户名重复");
                                  ps.println(CrazyProtocol.NAME_PER);                         }
                             else
                             {
                                  Server.clients.put(userName,ps);
                                  ps.println(CrazyProtocol.LOGIN_SUCCESS);
                                  ser.println("恭喜用户:"+userName+" 登陆成功");
                                  ser.println("当前的用户数为:"+Server.clients.size());
                                  System.out.println("恭喜用户:"+userName+" 登陆成功");
                                  System.out.println("当前的用户数为:"+Server.clients.size());
                                  ps.println("恭喜登陆成功!");
                             }
                        }
                        //如果读到的是以CrazyProtocol.PRIVATE_ROUND开始并以其结束,则可以确定为私聊信息
                        else if(line.startsWith(CrazyProtocol.PRIVATE_ROUND) && line.endsWith(CrazyProtocol.PRIVATE_ROUND))
                        {
                             String userMsg=getRealMsg(line);
                             //以SPLIT_SIGN分割字符串,前半部分为私聊用户,后半部分为聊天信息
                             String user=userMsg.split(CrazyProtocol.SPLIT_SIGN)[0];
                             String msg=userMsg.split(CrazyProtocol.SPLIT_SIGN)[1];
                             boolean pp=false;//标记是否找到该用户
                             for(String name:Server.clients.keySet())
                             {
                                  if(name.equals(user))
                                  {
                                       pp=true;
                                       break;
                                  }
                             }
                             if(pp==false)//未找到该私聊对象
                                  ps.println("未找到该私聊对象");
                             else
                             {
                                  Server.clients.get(user).println(Server.clients.getKeyByValue(ps)+"悄悄对你说:"+msg);
                                  ser.println(Server.clients.getKeyByValue(ps)+" 对 "+user+" 说:"+msg);
                                  System.out.println(Server.clients.getKeyByValue(ps)+" 对 "+user+" 说:"+msg);
                             }
                        }
                        else  //公聊要对每个Client发送信息
                        {
                             String msg=getRealMsg(line);
                             String name=Server.clients.getKeyByValue(ps);//保存当前用户名
                             Server.clients.removeByValue(ps); //先把自己当前的线程删掉,后面再添加进去
                             for(PrintStream clientPs:Server.clients.valueSet())
                                  clientPs.println(name+"说: "+msg);
                             Server.clients.put(name,ps);
                             ser.println(name+" 对大家说: "+msg);
                             System.out.println(name+" 对大家说: "+msg);
                        }
                   }
              }
              //捕获到异常,表明Socket对应的用户客户端出现了问题,所以出现将其对应的输出流从Map中删除
              catch(IOException e)
              {
                   Server.clients.removeByValue(ps);
                   System.out.println("当前的用户数为:"+Server.clients.size());
                   //关闭网络,IO资源
                   try
                   {
                        if(br != null)
                             br.close();
                        if(ps != null)
                             ps.close();
                        if(s != null)
                        s.close();
                   }
                   catch(IOException m)
                   {
                        m.printStackTrace();
                   }
              }          
         }
    }
      

  5.   

    //下面是客户端的代码:import java.net.*;
    import java.io.*;
    import java.io.PrintStream;
    import java.util.*;
    import javax.swing.JOptionPane;
    class Client  //主要处理想服务器发送信息
    {
         private static final int SERVER_PORT=30000;
         private Socket s;
         private PrintStream ps;
         private BufferedReader keyIn;
         private BufferedReader brServer;
         public void init()
         {
              try
              {
                   keyIn=new BufferedReader(new InputStreamReader(System.in));
                   Socket s=new Socket("127.0.0.1",SERVER_PORT);
                   ps=new PrintStream(s.getOutputStream());
                   brServer=new BufferedReader(new InputStreamReader(s.getInputStream()));
                   String tip="";
                   while(true)
                   {
                        String userName=JOptionPane.showInputDialog(tip+"请输入用户名:");
                        ps.println(CrazyProtocol.USER_ROUND+userName+CrazyProtocol.USER_ROUND);
                        String result=brServer.readLine();
                        if(result.equals(CrazyProtocol.NAME_PER))
                        {
                             tip="用户名重复,请重新输入!";
                             continue;
                        }
                        if(result.equals(CrazyProtocol.LOGIN_SUCCESS))
                             break;
                   }
              }
              catch(UnknownHostException e)
              {
                   System.out.println("找不到远程服务器,请确认服务器是否已经启动");
                   closeRs();
                   System.exit(1);
              }
              catch(IOException e)
              {
                   System.out.println("网络异常,请重新登陆");
                   closeRs();
                   System.exit(1);
              }
              //以该Socket对应的输入流启动ClientThread线程
              new ClientThread(brServer).start();
         }
         //定义一个读取键盘输入,并发向网络的方法
         public void readAddSend()
         {
              try
              {
                   String line=null;
                   while((line=keyIn.readLine())!=null)
                   {
                        //如果发送信息中有冒号,且以/开头,则认为想发送私聊信息
                        if(line.startsWith("/") && line.indexOf(":")>0)
                        {
                             line=line.substring(1);
                             ps.println(CrazyProtocol.PRIVATE_ROUND+line.split(":")[0]+CrazyProtocol.SPLIT_SIGN+line.split(":")[1]+CrazyProtocol.PRIVATE_ROUND);
                        }
                        else
                        {
                             ps.println(CrazyProtocol.MSG_ROUND+line+CrazyProtocol.MSG_ROUND);
                        }
                   }
              }
              catch(IOException em)
              {
                   System.out.println("网络通讯异常!请重新登陆!");
                   closeRs();
                   System.exit(1);
              }
         }
         //关闭输入流,输出流,Socket的方法
         public void closeRs()
         {
              try
              {
                   if(keyIn!=null)
                        keyIn.close();
                   if(ps!=null)
                        ps.close();
                   if(s!=null)
                        s.close();
              }
              catch(IOException ex)
              {
                   ex.printStackTrace();
              }
         }
         public static void main(String[] args)
         {
              Client mm=new Client();
              mm.init();
              mm.readAddSend();
         }
    }//不断读取来自服务器的的信息
    class ClientThread extends Thread
    {
         BufferedReader br;
         public ClientThread(BufferedReader br)
         {
              this.br=br;
         }
         public void run()
         {
              try
              {
                   String line=null;
                   while((line=br.readLine())!=null)
                        System.out.println(line);
              }
              catch(IOException e)
              {
                   e.printStackTrace();
              }
              finally
              {
                   try
                   {
                        if(br!=null)
                             br.close();
                   }
                   catch(IOException e)
                   {
                        e.printStackTrace();
                   }
              }
         }
    }
      

  6.   

    楼主,你要的是不是类似QQ群聊的功能,我建议你用socketChannel,如果有什么问题,可以q我,867606947,我的毕业作品,做的就是聊天工具,在我的空间上,希望对你有帮助
      

  7.   

    我最近也在做这个,基本完成了,我是用MAP存的,把线程用账号的方式存到map里