简单的基于socket通讯的聊天室,详细讲解
2012-12-06 00:16
447 查看
//下面是服务器端的代码 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(); } } } } //下面是客户端的代码: 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(); } } } }
相关文章推荐
- [转]基于C#的Socket简单通讯
- 基于Node.js,Express,Socket.io创建简单聊天室
- 基于socket---简单聊天室的实现
- 用java写的基于Socket的简单即时通讯程序
- 基于socket---简单聊天室的实现
- 基于socket的TCP和UDP通讯的简单建立
- Unity3D教程:实现基于Socket通讯的公共聊天室
- Unity3D教程:实现基于Socket通讯的公共聊天室
- 一个简单的基于socket的通讯处理程序
- 基于TCP协议的Socket通讯,实现多线程简单登录
- C#基于Socket的简单聊天室实践
- C#基于Socket的简单聊天室实践
- 简单而强大的多线程串口编程工具CserialPort类(附VC基于MFC单文档协议通讯源程序及详细编程步骤)
- 基于C#的Socket简单通讯
- java基于socket tcp的简单聊天室
- c语言socket简单聊天室基于linux环境
- [转]基于C#的Socket简单通讯
- 简单而强大的多线程串口编程工具CserialPort类(附VC基于MFC单文档协议通讯源程序及详细编程步骤)
- java--通过socket和多线程进行多个客服端与服务器的简单通讯--基于tcp
- 基于tcp/ip协议的Socket网络通讯 --> 简单的数据传送和库构造