多线程实现tcp多客户聊天程序

蔡Sir 2012-12-05 01:00:48
我已经实现了简单的客户端与服务器聊天,用的是tcp
我觉得我还需要两个步骤才能实现
1.可以连多个客户端到服务端,并且服务端都可以看到客户端所发信息。
2.各个客户端可以看到其他客户端发送的信息。
我想问,这两步应该怎样实现,我最大的问题是不知道怎么实现加入多线程的部分,我知道可以在服务器端用一个List把多个ServerSocket对象装起来,然后客户端没什么变化。但这具体点要怎么实现呢?求大哥哥大姐姐帮帮小弟。。。。
...全文
554 14 打赏 收藏 举报
写回复
用AI写文章
14 条回复
切换为时间正序
请发表友善的回复…
发表回复
其实偶不疼 2012-12-11
  • 打赏
  • 举报
回复
我最近也在做这个,基本完成了,我是用MAP存的,把线程用账号的方式存到map里
WE__Bug 2012-12-09
  • 打赏
  • 举报
回复
楼主,你要的是不是类似QQ群聊的功能,我建议你用socketChannel,如果有什么问题,可以q我,867606947,我的毕业作品,做的就是聊天工具,在我的空间上,希望对你有帮助
cml_bright 2012-12-06
  • 打赏
  • 举报
回复
//下面是客户端的代码:

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();
               }
          }
     }
}


cml_bright 2012-12-06
  • 打赏
  • 举报
回复
//下面是我写的控制台聊天室小程序,希望对你有帮助
//下面是服务器端的代码
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();
               }
          }          
     }
}
nj_dobetter 2012-12-05
  • 打赏
  • 举报
回复
用 U D P?
zhaoming262350 2012-12-05
  • 打赏
  • 举报
回复
这儿有个线程的代码,你可以看一下: 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(); } } } }
蔡Sir 2012-12-05
  • 打赏
  • 举报
回复
求帮忙帮忙!思考大半天还是不会,,唉,,
蔡Sir 2012-12-05
  • 打赏
  • 举报
回复
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(); } } 这个是我的服务器代码,能帮我改改么,,真心不会!!
傲雪kimi 2012-12-05
  • 打赏
  • 举报
回复
引用 1 楼 java2000_net 的回复:
用队列吧,比如Queue 每个线程都接收从各自客户端发来的信息,然后保存到队列里面 从队列读取信息,发给每个客户端。
+1
fgdgdaasdf 2012-12-05
  • 打赏
  • 举报
回复
用双向缓冲队列
老紫竹 2012-12-05
  • 打赏
  • 举报
回复
用队列吧,比如Queue 每个线程都接收从各自客户端发来的信息,然后保存到队列里面 从队列读取信息,发给每个客户端。

62,620

社区成员

发帖
与我相关
我的任务
社区描述
Java 2 Standard Edition
社区管理员
  • Java SE
加入社区
  • 近7日
  • 近30日
  • 至今
社区公告
暂无公告

试试用AI创作助手写篇文章吧