java Socket实现多人群聊与私聊功能
时间:2018-07-22
这篇文章主要为大家详细介绍了java Socket实现多人群聊与私聊功能,具有一定的参考价值,感兴趣的小伙伴们可以参考一下
本文实例为大家分享了java Socket实现多人群聊与私聊的具体代码,供大家参考,具体内容如下
关于Socket套接字的一些基本知识与认识可以参见上一篇或自行查阅。
ServerSocket和Socket实现群聊与私聊涉及到多线程编程,实现过程的重点是利用Socket通信的原理,即不断的在服务端和客户端创建输入输出流来相互传递、交换数据等以达到通信的目的。具体实现如下:
服务端:
import java.io.*; import java.net.*; import java.util.HashMap; import java.util.Map; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; import java.util.concurrent.ThreadPoolExecutor; public class TCPServer { private ServerSocket serverSocket; /** * 创建线程池来管理客户端的连接线程 * 避免系统资源过度浪费 */ private ExecutorService exec; // 存放客户端之间私聊的信息 private Map<String,PrintWriter> storeInfo; public TCPServer() { try { serverSocket = new ServerSocket(6789); storeInfo = new HashMap<String, PrintWriter>(); exec = Executors.newCachedThreadPool(); } catch (Exception e) { e.printStackTrace(); } } // 将客户端的信息以Map形式存入集合中 private void putIn(String key,PrintWriter value) { synchronized(this) { storeInfo.put(key, value); } } // 将给定的输出流从共享集合中删除 private synchronized void remove(String key) { storeInfo.remove(key); System.out.println("当前在线人数为:"+ storeInfo.size()); } // 将给定的消息转发给所有客户端 private synchronized void sendToAll(String message) { for(PrintWriter out: storeInfo.values()) { out.println(message); } } // 将给定的消息转发给私聊的客户端 private synchronized void sendToSomeone(String name,String message) { PrintWriter pw = storeInfo.get(name); //将对应客户端的聊天信息取出作为私聊内容发送出去 if(pw != null) pw.println(message); } public void start() { try { while(true) { System.out.println("等待客户端连接... ... "); Socket socket = serverSocket.accept(); // 获取客户端的ip地址 InetAddress address = socket.getInetAddress(); System.out.println("客户端:“" + address.getHostAddress() + "”连接成功! "); /** * 启动一个线程,由线程来处理客户端的请求,这样可以再次监听 * 下一个客户端的连接 */ exec.execute(new ListenrClient(socket)); //通过线程池来分配线程 } } catch(Exception e) { e.printStackTrace(); } } /** * 该线程体用来处理给定的某一个客户端的消息,循环接收客户端发送 * 的每一个字符串,并输出到控制台 */ class ListenrClient implements Runnable { private Socket socket; private String name; public ListenrClient(Socket socket) { this.socket = socket; } // 创建内部类来获取昵称 private String getName() throws Exception { try { //服务端的输入流读取客户端发送来的昵称输出流 BufferedReader bReader = new BufferedReader( new InputStreamReader(socket.getInputStream(), "UTF-8")); //服务端将昵称验证结果通过自身的输出流发送给客户端 PrintWriter ipw = new PrintWriter( new OutputStreamWriter(socket.getOutputStream(), "UTF-8"),true); //读取客户端发来的昵称 while(true) { String nameString = bReader.readLine(); if ((nameString.trim().length() == 0) || storeInfo.containsKey(nameString)) { ipw.println("FAIL"); } else { ipw.println("OK"); return nameString; } } } catch(Exception e) { throw e; } } @Override public void run() { try { /* * 通过客户端的Socket获取客户端的输出流 * 用来将消息发送给客户端 */ PrintWriter pw = new PrintWriter( new OutputStreamWriter(socket.getOutputStream(), "UTF-8"), true); /* * 将客户昵称和其所说的内容存入共享集合HashMap中 */ name = getName(); putIn(name, pw); Thread.sleep(100); // 服务端通知所有客户端,某用户上线 sendToAll("[系统通知] “" + name + "”已上线"); /* * 通过客户端的Socket获取输入流 * 读取客户端发送来的信息 */ BufferedReader bReader = new BufferedReader( new InputStreamReader(socket.getInputStream(), "UTF-8")); String msgString = null; while((msgString = bReader.readLine()) != null) { // 检验是否为私聊(格式:@昵称:内容) if(msgString.startsWith("@")) { int index = msgString.indexOf(":"); if(index >= 0) { //获取昵称 String theName = msgString.substring(1, index); String info = msgString.substring(index+1, msgString.length()); info = name + ":"+ info; //将私聊信息发送出去 sendToSomeone(theName, info); continue; } } // 遍历所有输出流,将该客户端发送的信息转发给所有客户端 System.out.println(name+":"+ msgString); sendToAll(name+":"+ msgString); } } catch (Exception e) { // e.printStackTrace(); } finally { remove(name); // 通知所有客户端,某某客户已经下线 sendToAll("[系统通知] "+name + "已经下线了。"); if(socket!=null) { try { socket.close(); } catch(IOException e) { e.printStackTrace(); } } } } } public static void main(String[] args) { TCPServer server = new TCPServer(); server.start(); } }
客户端:
import java.io.*; import java.net.*; import java.util.Scanner; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; import java.util.concurrent.ThreadPoolExecutor; public class TCPClient { static private Socket clientSocket; public TCPClient() {} public static void main(String[] args) throws Exception { Scanner scanner = new Scanner(System.in); String serverIP; System.out.println("请设置服务器IP:"); serverIP = scanner.next(); clientSocket = new Socket(serverIP, 6789); TCPClient client = new TCPClient(); client.start(); } public void start() { try { Scanner scanner = new Scanner(System.in); setName(scanner); // 接收服务器端发送过来的信息的线程启动 ExecutorService exec = Executors.newCachedThreadPool(); exec.execute(new ListenrServser()); // 建立输出流,给服务端发信息 PrintWriter pw = new PrintWriter( new OutputStreamWriter(clientSocket.getOutputStream(), "UTF-8"), true); while(true) { pw.println(scanner.nextLine()); } } catch(Exception e) { e.printStackTrace(); } finally { if (clientSocket !=null) { try { clientSocket.close(); } catch(IOException e) { e.printStackTrace(); } } } } private void setName(Scanner scan) throws Exception { String name; //创建输出流 PrintWriter pw = new PrintWriter( new OutputStreamWriter(clientSocket.getOutputStream(), "UTF-8"),true); //创建输入流 BufferedReader br = new BufferedReader( new InputStreamReader(clientSocket.getInputStream(),"UTF-8")); while(true) { System.out.println("请创建您的昵称:"); name = scan.nextLine(); if (name.trim().equals("")) { System.out.println("昵称不得为空"); } else { pw.println(name); String pass = br.readLine(); if (pass != null && (!pass.equals("OK"))) { System.out.println("昵称已经被占用,请重新输入:"); } else { System.out.println("昵称“"+name+"”已设置成功,可以开始聊天了"); break; } } } } // 循环读取服务端发送过来的信息并输出到客户端的控制台 class ListenrServser implements Runnable { @Override public void run() { try { BufferedReader br = new BufferedReader( new InputStreamReader(clientSocket.getInputStream(), "UTF-8")); String msgString; while((msgString = br.readLine())!= null) { System.out.println(msgString); } } catch(Exception e) { e.printStackTrace(); } } } }
运行结果:
开始自己的实现也不是很完整,后来也是借鉴别人比较好的思想后完善的,权当分享。
以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持脚本之家。
- Java并发编程的艺术(三)——volatile
- java学习手册-java 新手入门必看的30个题
- Java并发编程的艺术(五)——中断
- 简易的深度学习框架Keras代码解析与应用
- Java并发编程的艺术(六)——线程间的通信
- 轻量级线程池的实现
- python根据BM25实现文本检索
- 稳扎稳打JavaScript(一)——作用域链内存模型
- 稳扎稳打JavaScript(二)——图解对象内存模型
- Swift学习资源
- 稳扎稳打JavaScript(三)——创建对象的几种方式
- 快速教程:使用Cython来扩展Python/NumPy库
- 稳扎稳打JavaScript(四)——闭包
- JavaScript奇淫技巧(一)
- java教程
- Java快速入门
- Java 开发环境配置
- Java基本语法
- Java 对象和类
- Java 基本数据类型
- Java 变量类型
- Java 修饰符
- Java 运算符
- Java 循环结构
- Java 分支结构
- Java Number类
- Java Character类
- Java String类
- Java StringBuffer和StringBuilder类
- Java 数组
- Java 日期时间
- Java 正则表达式
- Java 方法
- Java 流(Stream)、文件(File)和IO
- Java 异常处理
- Java 继承
- Java 重写(Override)与重载(Overload)
- Java 多态
- Java 抽象类
- Java 封装
- Java 接口
- Java 包(package)
- Java 数据结构
- Java 集合框架
- Java 泛型
- Java 序列化
- Java 网络编程
- Java 发送邮件
- Java 多线程编程
- Java Applet基础
- Java 文档注释
- Java——简单Java类深入(数据表与简单Java类、一对多映射、双向一对多映射、多对多映射)
- Java——代码块(普通块、构造块、静态块)
- JavaWeb——JQuery之五种选择器的应用及实践案例总结(基本选择器、层级选择器、属性选择器、过滤选择器、表单过滤选择器)
- Java——引用传递实例分析(进阶分析、对象比较、类与类的关联实现)
- JavaWeb——JQuery之基本概述与快速入门实践总结(JQuery各版本区别、JQuery对象和JS对象的区别与转换)
- Java——动态代理设计模型概述(实现步骤、增强方式)与代理商采购电脑模拟程序实战
- Java——this关键字(调用本类属性、调用本类方法、表示当前对象)
- 5分钟玩转Lighthouse|零基础也能拥有WordPress个人博客
- Java——String类常用方法总结,看这一篇就够啦(比较、查找、截取、替换、拆分、其他)
- Java——深入分析类与对象(封装性、构造方法与匿名对象、简单Java类开发原则)
- Java——类与对象(基本概念、定义、内存分析、引用传递)
- Java——Stream数据流
- JavaWeb——MyBatis框架之连接池原理、MyBatis事务提交设置、动态SQL语法总结
- 8种ETL算法归纳总结,看完这些你就全明白了
- JavaWeb——Maven基础之详细总结,从零开始搭建Maven工程,包含一些常见的坑org.eclipse.jdt.internal.compiler.classfmt.ClassFormatEx