基于TCP的网络实时聊天室(socket通信案例)

来源:https://blog.csdn.net/Charzous
开门见山
(1) L(list):查看当前上线用户;
(2) G(group):群聊;
(3) O(one-one):私信;
(4) E(exit):退出当前聊天状态;
(5) bye:离线;
(6) H(help):查看帮助
本篇将详细记录网络实时聊天室的实现步骤
一、数据结构Map
二、保证线程安全
三、群聊核心方法
private void sendToMembers(String msg,String hostAddress,Socket mySocket) throws IOException{PrintWriter pw;OutputStream out;Iterator iterator=users.entrySet().iterator();while (iterator.hasNext()){Map.Entry entry=(Map.Entry) iterator.next();Socket tempSocket = (Socket) entry.getKey();String name = (String) entry.getValue();if (!tempSocket.equals(mySocket)){out=tempSocket.getOutputStream();pw=new PrintWriter(new OutputStreamWriter(out,"utf-8"),true);pw.println(hostAddress+":"+msg);}}}
private void sendToOne(String msg,String hostAddress,Socket another) throws IOException{PrintWriter pw;OutputStream out;Iterator iterator=users.entrySet().iterator();while (iterator.hasNext()){Map.Entry entry=(Map.Entry) iterator.next();Socket tempSocket = (Socket) entry.getKey();String name = (String) entry.getValue();if (tempSocket.equals(another)){out=tempSocket.getOutputStream();pw=new PrintWriter(new OutputStreamWriter(out,"utf-8"),true);pw.println(hostAddress+"私信了你:"+msg);}}}
四、聊天室具体设计
private ConcurrentHashMapString > users=new ConcurrentHashMap();0、用户登录服务器
pw.println("From 服务器:欢迎使用服务!");pw.println("请输入用户名:");String localName = null;while ((hostName=br.readLine())!=null){users.forEach((k,v)->{if (v.equals(hostName))flag=true;//线程修改了全局变量});if (!flag){localName=hostName;users.put(socket,hostName);flag=false;break;}else{flag=false;pw.println("该用户名已存在,请修改!");}}
1、查看当前上线用户
if (msg.trim().equalsIgnoreCase("L")){users.forEach((k,v)->{pw.println("用户:"+v);});continue;}
2、群聊
else if (msg.trim().equals("G")){pw.println("您已进入群聊。");while ((msg=br.readLine())!=null){if (!msg.equals("E")&&users.size()!=1)sendToMembers(msg,localName,socket);else if (users.size()==1){pw.println("当前群聊无其他用户在线,已自动退出!");break;}else {pw.println("您已退出群组聊天室!");break;}}}
3、私信
//一对一私聊else if (msg.trim().equalsIgnoreCase("O")){pw.println("请输入私信人的用户名:");String name=br.readLine();//查找map中匹配的socket,与之建立通信//有待改进,处理输入的用户名不存在的情况users.forEach((k, v)->{if (v.equals(name)) {isExist=true;//全局变量与线程修改问题}});//已修复用户不存在的处理逻辑Socket temp=null;for(Map.Entry<Socket,String> mapEntry : users.entrySet()){if(mapEntry.getValue().equals(name))temp = mapEntry.getKey();// System.out.println(mapEntry.getKey()+":"+mapEntry.getValue()+'\n');}if (isExist){isExist=false;//私信后有一方用户离开,另一方未知,仍然发信息而未收到回复,未处理这种情况while ((msg=br.readLine())!=null){if (!msg.equals("E")&&!isLeaved(temp))sendToOne(msg,localName,temp);else if (isLeaved(temp)){pw.println("对方已经离开,已断开连接!");break;}else{pw.println("您已退出私信模式!");break;}}}elsepw.println("用户不存在!");}
4、退出当前聊天状态
//判断用户是否已经下线private Boolean isLeaved(Socket temp){Boolean leave=true;for(Map.EntryString > mapEntry : users.entrySet()) {if (mapEntry.getKey().equals(temp))leave = false;}return leave;}
5、离线
if (msg.trim().equalsIgnoreCase("bye")) {pw.println("From 服务器:服务器已断开连接,结束服务!");users.remove(socket,localName);sendToMembers("我下线了",localName,socket);System.out.println("客户端离开。");//加当前用户名break;}
6、查看帮助
else if (msg.trim().equalsIgnoreCase("H")){pw.println("输入命令功能:(1)L(list):查看当前上线用户;(2)G(group):进入群聊;(3)O(one-one):私信;(4)E(exit):退出当前聊天状态;(5)bye:离线;(6)H(help):帮助");continue;//返回循环}
五、聊天室服务完整代码
class Handler implements Runnable {private Socket socket;public Handler(Socket socket) {this.socket = socket;}public void run() {//本地服务器控制台显示客户端连接的用户信息System.out.println("New connection accept:" + socket.getInetAddress().getHostAddress());try {BufferedReader br = getReader(socket);PrintWriter pw = getWriter(socket);pw.println("From 服务器:欢迎使用服务!");pw.println("请输入用户名:");String localName = null;while ((hostName=br.readLine())!=null){users.forEach((k,v)->{if (v.equals(hostName))flag=true;//线程修改了全局变量});if (!flag){localName=hostName;users.put(socket,hostName);flag=false;//可能找出不一致问题break;}else{flag=false;pw.println("该用户名已存在,请修改!");}}// System.out.println(hostName+": "+socket);sendToMembers("我已上线",localName,socket);pw.println("输入命令功能:(1)L(list):查看当前上线用户;(2)G(group):进入群聊;(3)O(one-one):私信;(4)E(exit):退出当前聊天状态;(5)bye:离线;(6)H(help):帮助");String msg = null;//用户连接服务器上线,进入聊天选择状态while ((msg = br.readLine()) != null) {if (msg.trim().equalsIgnoreCase("bye")) {pw.println("From 服务器:服务器已断开连接,结束服务!");users.remove(socket,localName);sendToMembers("我下线了",localName,socket);System.out.println("客户端离开。");//加当前用户名break;}else if (msg.trim().equalsIgnoreCase("H")){pw.println("输入命令功能:(1)L(list):查看当前上线用户;(2)G(group):进入群聊;(3)O(one-one):私信;(4)E(exit):退出当前聊天状态;(5)bye:离线;(6)H(help):帮助");continue;//返回循环}else if (msg.trim().equalsIgnoreCase("L")){users.forEach((k,v)->{pw.println("用户:"+v);});continue;}//一对一私聊else if (msg.trim().equalsIgnoreCase("O")){pw.println("请输入私信人的用户名:");String name=br.readLine();//查找map中匹配的socket,与之建立通信users.forEach((k, v)->{if (v.equals(name)) {isExist=true;//全局变量与线程修改问题}});//已修复用户不存在的处理逻辑Socket temp=null;for(Map.EntrymapEntry : users.entrySet()){ if(mapEntry.getValue().equals(name))temp = mapEntry.getKey();}if (isExist){isExist=false;//私信后有一方用户离开,另一方未知,仍然发信息而未收到回复,未处理这种情况while ((msg=br.readLine())!=null){if (!msg.equals("E")&&!isLeaved(temp))sendToOne(msg,localName,temp);else if (isLeaved(temp)){pw.println("对方已经离开,已断开连接!");break;}else{pw.println("您已退出私信模式!");break;}}}elsepw.println("用户不存在!");}//选择群聊else if (msg.trim().equals("G")){pw.println("您已进入群聊。");while ((msg=br.readLine())!=null){if (!msg.equals("E")&&users.size()!=1)sendToMembers(msg,localName,socket);else if (users.size()==1){pw.println("当前群聊无其他用户在线,已自动退出!");break;}else {pw.println("您已退出群组聊天室!");break;}}}elsepw.println("请选择聊天状态!");}} catch (IOException e) {e.printStackTrace();} finally {try {if (socket != null)socket.close();} catch (IOException e) {e.printStackTrace();}}}}
六、效果演示:基于TCP的网络实时聊天室
结语
评论



