基于websocket和java的多人聊天室
- 格式:docx
- 大小:29.65 KB
- 文档页数:9
⽤javaWebSocket做⼀个聊天室最近⼀个项⽬中,需要⽤到Java的websocket新特性,于是就学了⼀下,感觉这技术还挺好玩的,瞬间知道⽹页上⾯的那些在线客服是怎么做的了。
先看图:实现了多客户机进⾏实时通讯。
下⾯看代码项⽬结构图:很简单,就1个类,1个页⾯然后看具体代码先看后端代码package com.main;import java.io.IOException;import java.util.concurrent.CopyOnWriteArraySet;import javax.websocket.*;import javax.websocket.server.ServerEndpoint;/*** @ServerEndpoint 注解是⼀个类层次的注解,它的功能主要是将⽬前的类定义成⼀个websocket服务器端,* 注解的值将被⽤于监听⽤户连接的终端访问URL地址,客户端可以通过这个URL来连接到WebSocket服务器端*/@ServerEndpoint("/websocket")public class H5ServletServerSocket {// 静态变量,⽤来记录当前在线连接数。
应该把它设计成线程安全的。
private static int onlineCount = 0;// concurrent包的线程安全Set,⽤来存放每个客户端对应的MyWebSocket对象。
若要实现服务端与单⼀客户端通信的话,可以使⽤Map来存放,其中Key可以为⽤户标识 private static CopyOnWriteArraySet<H5ServletServerSocket> webSocketSet = new CopyOnWriteArraySet<H5ServletServerSocket>();// 与某个客户端的连接会话,需要通过它来给客户端发送数据private Session session;/*** 连接建⽴成功调⽤的⽅法** @param session* 可选的参数。
基于websocket的聊天系统设计与实现WebSocket是一种在Web浏览器和服务器之间进行全双工通信的通信协议。
它允许客户端和服务器之间实时地进行双向通信,而不需要客户端发起请求。
基于WebSocket的聊天系统是一个能够实现实时通信的应用程序,用户可以通过该应用程序在一个或多个聊天室中发送消息、接收消息,并与其他用户进行实时交流。
设计和实现一个基于WebSocket的聊天系统需要考虑以下几个方面:1. 前后端交互设计:前端通过WebSocket与后端建立连接,后端负责处理接收到的消息并转发给目标用户或聊天室。
前端需要实现用户注册、登录、发送消息、接收消息等功能。
后端需要实现身份验证、消息路由和转发等功能。
2. 用户认证和身份验证:聊天系统需要确保只有经过身份验证的用户才能进入聊天室。
可以使用JWT(JSON Web Token)或其他身份验证机制来验证用户的身份,并在每个WebSocket连接上进行验证。
3. 消息的发送和接收:用户可以通过界面输入消息并将其发送到聊天室或特定的用户。
接收消息时,前端应能够实时地接收到其他用户发送的消息,并将其显示在用户界面上。
后端需要将接收到的消息转发给相应的聊天室或用户。
4. 聊天室管理:聊天系统中可以有多个聊天室,用户可以选择进入或创建聊天室。
后端需要提供API,用于创建聊天室、查找聊天室、加入聊天室和离开聊天室等功能。
5. 消息存储和历史记录:可以考虑将用户发送的消息存储在数据库中,以便后续查询和展示历史记录。
后端需要设计相应的数据库表结构,并提供API用于存储和查询消息。
6. 安全性和性能优化:聊天系统需要考虑安全性,并采取必要的措施防止恶意攻击和数据泄露。
同时,为了提高系统的性能,可以使用消息队列或缓存技术来处理用户的消息请求。
基于以上需求,设计和实现一个基于WebSocket的聊天系统可以按如下步骤进行:1. 前端页面设计与开发:设计用户界面,包括用户注册、登录、聊天室列表、聊天室界面等。
Java WebSocket案例:聊天室应用背景WebSocket是一种在Web浏览器和服务器之间进行双向通信的技术标准。
相比传统的HTTP请求-响应模式,WebSocket允许服务器主动向客户端推送消息,实现了实时通信的能力。
在Java中,我们可以使用Java API for WebSocket(JSR 356)来开发WebSocket应用。
本案例将展示如何使用Java WebSocket API来构建一个简单的聊天室应用。
在这个聊天室中,多个用户可以同时加入并实时发送消息,所有用户都能看到其他用户发送的消息。
过程1. 创建WebSocket服务器首先,我们需要创建一个WebSocket服务器来处理客户端的连接和消息。
在Java 中,可以使用javax.websocket包中的注解和接口来实现WebSocket服务器。
import java.io.IOException;import java.util.Collections;import java.util.HashSet;import java.util.Set;import javax.websocket.OnClose;import javax.websocket.OnError;import javax.websocket.OnMessage;import javax.websocket.OnOpen;import javax.websocket.Session;import javax.websocket.server.ServerEndpoint;@ServerEndpoint("/chat")public class ChatServer {private static Set<Session> sessions = Collections.synchronizedSet(new Has hSet<>());@OnOpenpublic void onOpen(Session session) {sessions.add(session);}@OnMessagepublic void onMessage(String message, Session session) throws IOException{for (Session s : sessions) {s.getBasicRemote().sendText(message);}}@OnClosepublic void onClose(Session session) {sessions.remove(session);}@OnErrorpublic void onError(Session session, Throwable error) {System.err.println("发生错误:" + error.getMessage());}}在上述代码中,我们使用@ServerEndpoint注解来指定WebSocket服务器的URL路径。
JAVA结合WebSocket实现简单客服聊天功能说明:该⽰例只简单的实现了客服聊天功能。
1、聊天记录没有保存到数据库中,⼀旦服务重启,消息记录将会没有,如果需要保存到数据库中,可以扩展2、页⾯样式⽤的⽹上模板,样式可以⾃⼰进⾏修改3、只能由⽤户主要发起会话,管理员⽆法主动进⾏对话4、页⾯之间跳转代码没有包含在⾥⾯,请⾃⼰书写,在管理员消息列表页中,需要把该咨询的⽤户ID带到客服回复页⾯中5、${websocket_url} 这个为项⽬的URL地址效果截图:客服回复页⾯(member_admin_chat.html)管理员消息列表页(member_admin_chat_list.html)⽤户咨询页⾯(member_chat.html)代码:页⾯所需要⽤到的基础样式、图⽚,下载链接:(这个只是⾃⼰⽹上下载的样式demo,可以根据⾃⼰的来)pom.xml <dependency><groupId>javax</groupId><artifactId>javaee-api</artifactId><version>8.0</version><scope>provided</scope></dependency>或者jar包javax.websocket-api-1.0.jar下载地址:配置类WebSocketConfig.javapackage com.config;import javax.websocket.Endpoint;import javax.websocket.server.ServerApplicationConfig;import javax.websocket.server.ServerEndpointConfig;import java.util.Set;public class WebSocketConfig implements ServerApplicationConfig {@Overridepublic Set<ServerEndpointConfig> getEndpointConfigs(Set<Class<? extends Endpoint>> endpointClasses) { return null;}@Overridepublic Set<Class<?>> getAnnotatedEndpointClasses(Set<Class<?>> scanned) {//在这⾥会把含有@ServerEndpoint注解的类扫描加载进来,可以在这⾥做过滤等操作return scanned;}}消息DTO类(使⽤了lombok,这⾥不在多做说明)ChatDTO.javapackage com.websocket.dto;import lombok.AllArgsConstructor;import lombok.Data;import lombok.NoArgsConstructor;import lombok.experimental.Accessors;/*** @author 。
JavaSocket+多线程实现多⼈聊天室功能本⽂实例为⼤家分享了Java Socket+多线程实现多⼈聊天室的具体代码,供⼤家参考,具体内容如下思路简介分为客户端和服务器两个类,所有的客户端将聊的内容发送给服务器,服务器接受后,将每⼀条内容发送给每⼀个客户端,客户端再显⽰在终端上。
客户端设计客户端包含2个线程,1个⽤来接受服务器的信息,再显⽰,1个⽤来接收键盘的输⼊,发送给服务器。
import java.io.IOException;import java.io.InputStream;import java.io.OutputStream;import .Socket;import java.nio.charset.StandardCharsets;import java.util.Scanner;public class WeChatClient { //WeChat的客户端类private Socket client;private String name;private InputStream in;private OutputStream out;private MassageSenter massageSenter;private MassageGeter massageGeter;class MassageGeter extends Thread{ //⼀个⼦线程类,⽤于客户端接收消息MassageGeter() throws IOException{in = client.getInputStream();}@Overridepublic void run() {int len;byte[] bytes = new byte[1024];try {while ((len = in.read(bytes)) != -1) { //此函数是阻塞的System.out.println(new String(bytes,0,len, StandardCharsets.UTF_8));}}catch (IOException e){System.out.println(e.toString());}System.out.println("Connection interruption");}}class MassageSenter extends Thread{ //⼀个⼦线程类,⽤于发送消息给服务器MassageSenter() throws IOException{out = client.getOutputStream();}@Overridepublic void run() {Scanner scanner = new Scanner(System.in);try {while (scanner.hasNextLine()) { //此函数为阻塞的函数String massage = scanner.nextLine();out.write((name + " : " + massage).getBytes(StandardCharsets.UTF_8));if(massage.equals("//exit"))break;}}catch (IOException e){e.printStackTrace();}}}WeChatClient(String name, String host, int port) throws IOException {//初始化,实例化发送和接收2个线程 = name;client = new Socket(host,port);massageGeter = new MassageGeter();massageSenter = new MassageSenter();}void login() throws IOException{//登录时,先发送名字给服务器,在接收到服务器的正确回应之后,启动线程out.write(name.getBytes(StandardCharsets.UTF_8));byte[] bytes = new byte[1024];int len;len = in.read(bytes);String answer = new String(bytes,0,len, StandardCharsets.UTF_8);if(answer.equals("logined!")) {System.out.println("Welcome to WeChat! "+name);massageSenter.start();massageGeter.start();try {massageSenter.join();//join()的作⽤是等线程结束之后再继续执⾏主线程(main)massageGeter.join();}catch (InterruptedException e){System.err.println(e.toString());}}else{System.out.println("Server Wrong");}client.close();}public static void main(String[] args) throws IOException{//程序⼊⼝String host = "127.0.0.1";WeChatClient client = new WeChatClient("Uzi",host,7777);client.login();}}服务器设计服务器包含3个线程类,端⼝监听线程,客户端接收信息线程,发送信息线程。
使⽤Java和WebSocket实现⽹页聊天室实例代码在没介绍正⽂之前,先给⼤家介绍下websocket的背景和原理:背景在浏览器中通过http仅能实现单向的通信,comet可以⼀定程度上模拟双向通信,但效率较低,并需要服务器有较好的⽀持; flash中的socket 和xmlsocket可以实现真正的双向通信,通过 flex ajax bridge,可以在javascript中使⽤这两项功能. 可以预见,如果websocket⼀旦在浏览器中得到实现,将会替代上⾯两项技术,得到⼴泛的使⽤.⾯对这种状况,HTML5定义了WebSocket协议,能更好的节省服务器资源和带宽并达到实时通讯。
在JavaEE7中也实现了WebSocket协议。
原理WebSocket protocol 。
现很多⽹站为了实现即时通讯,所⽤的技术都是轮询(polling)。
轮询是在特定的的时间间隔(如每1秒),由浏览器对服务器发出HTTP request,然后由服务器返回最新的数据给客户端的浏览器。
这种传统的HTTP request 的模式带来很明显的缺点 – 浏览器需要不断的向服务器发出请求,然⽽HTTP request 的header是⾮常长的,⾥⾯包含的有⽤数据可能只是⼀个很⼩的值,这样会占⽤很多的带宽。
⽽⽐较新的技术去做轮询的效果是Comet – ⽤了AJAX。
但这种技术虽然可达到全双⼯通信,但依然需要发出请求。
在 WebSocket API,浏览器和服务器只需要做⼀个握⼿的动作,然后,浏览器和服务器之间就形成了⼀条快速通道。
两者之间就直接可以数据互相传送。
在此WebSocket 协议中,为我们实现即时服务带来了两⼤好处:1. Header互相沟通的Header是很⼩的-⼤概只有 2 Bytes2. Server Push服务器的推送,服务器不再被动的接收到浏览器的request之后才返回数据,⽽是在有新数据时就主动推送给浏览器。
使⽤WebSocket实现即时通讯(⼀个群聊的聊天室)随着互联⽹的发展,传统的HTTP协议已经很难满⾜Web应⽤⽇益复杂的需求了。
近年来,随着HTML5的诞⽣,WebSocket协议被提出,它实现了浏览器与服务器的全双⼯通信,扩展了浏览器与服务端的通信功能,使服务端也能主动向客户端发送数据。
传统的HTTP协议是⽆状态的,每次请求(request)都要由客户端(如浏览器)主动发起,服务端进⾏处理后返回response结果,⽽服务端很难主动向客户端发送数据;这种客户端是主动⽅,服务端是被动⽅的传统Web模式对于信息变化不频繁的Web应⽤来说造成的⿇烦较⼩,⽽对于涉及实时信息的Web应⽤却带来了很⼤的不便,如带有即时通信、实时数据、订阅推送等功能的应⽤。
在WebSocket规范提出之前,开发⼈员若要实现这些实时性较强的功能,经常会使⽤折衷的解决⽅法:轮询(polling)和Comet技术。
其实后者本质上也是⼀种轮询,只不过有所改进。
轮询是最原始的实现实时Web应⽤的解决⽅案。
轮询技术要求客户端以设定的时间间隔周期性地向服务端发送请求,频繁地查询是否有新的数据改动。
明显地,这种⽅法会导致过多不必要的请求,浪费流量和服务器资源。
Comet技术⼜可以分为长轮询和流技术。
长轮询改进了上述的轮询技术,减⼩了⽆⽤的请求。
它会为某些数据设定过期时间,当数据过期后才会向服务端发送请求;这种机制适合数据的改动不是特别频繁的情况。
流技术通常是指客户端使⽤⼀个隐藏的窗⼝与服务端建⽴⼀个HTTP长连接,服务端会不断更新连接状态以保持HTTP长连接存活;这样的话,服务端就可以通过这条长连接主动将数据发送给客户端;流技术在⼤并发环境下,可能会考验到服务端的性能。
这两种技术都是基于请求-应答模式,都不算是真正意义上的实时技术;它们的每⼀次请求、应答,都浪费了⼀定流量在相同的头部信息上,并且开发复杂度也较⼤。
**伴随着HTML5推出的WebSocket,真正实现了Web的实时通信,使B/S模式具备了C/S模式的实时通信能⼒。
javasocket实现聊天室java实现多⼈聊天功能⽤java socket做⼀个聊天室,实现多⼈聊天的功能。
看了极客学院的视频后跟着敲的。
(1DAY)服务端:1. 先写服务端的类MyServerSocket,⾥⾯放⼀个监听线程,⼀启动就好2. 实现服务端监听类ServerListener.java,⽤accept来监听,⼀旦有客户端连上,⽣成新的socket,就新建个线程实例ChatSocket。
启动线程后就把线程交给ChatManager管理3. 在ChatSocket中实现从客户端读取内容,把读取到的内容发给集合内所有的客户端4. ChatManager⾥⾯⽤vector来管理socket线程实例ChatSocket,并实现发送信息给其他的客户端客户端:1. 新建⼀个继承JFrame的MainWindow.java类,主要实现聊天窗⼝的UI,以及事件响应。
2. 新建StartClient.java类,把MainWindow中⽣成MainWindow主⽅法部分代码拷贝过来,这样就能在主程序中把窗体执⾏出来了。
3. 新建ChatManager(需要单例化的类)管理socket,实现聊天的输⼊输出功能。
最后记得在1中新建窗⼝后,传⼀份frame 的引⽤到ChatManager中,才能实现ChatManager对界⾯的显⽰。
⼯程结构如图以下为代码服务端:1. 先写服务端的类MyServerSocket,⾥⾯放⼀个监听线程,⼀启动就好package com.starnet.testserversocket.main;public class MyServerSocket {public static void main(String[] args) {new ServerListener().start();}}2.实现服务端监听类ServerListener.java,⽤accept来监听,⼀旦有客户端连上,⽣成新的socket,就新建个线程实例ChatSocket。
多线程+socket实现多⼈聊天室最近在学习多线程的时候打算做⼀个简单的多线程socke聊天的程序,结果发现⽹上的代码都没有完整的实现功能,所以⾃⼰实现了⼀个demo:demo功能⼤致就是,有⼀个服务端负责信息转发,多个客户端发送消息,当⼀个客户端发送消息时,其他的客户端都可以接受到。
服务端:客户端:客户端代码:package com.cky.client;import java.io.IOException;import java.io.InputStream;import java.io.PrintWriter;import .Socket;import .UnknownHostException;import java.util.Scanner;public class Client {private PrintWriter out;//private BufferedReader br;private Scanner scan;private Boolean flag=true;private Socket s;private InputStream is;public Client() throws UnknownHostException, IOException {s=new Socket("127.0.0.1", 5001);is=s.getInputStream();}public static void main(String []args) throws UnknownHostException, IOException {Client client =new Client();client.startup();}public void startup() throws UnknownHostException, IOException {out = new PrintWriter(s.getOutputStream(), true);//开启⼀个线程监听服务端的消息Thread ct=new Thread(new Runnable() {@Overridepublic void run() {while(true) {if(!flag) break;try {receive();} catch (IOException e) {// TODO Auto-generated catch blocke.printStackTrace();}}}});ct.start();//主线程负责发送消息System.out.println("请输⼊你的⽤户名:");scan = new Scanner(System.in);String name=scan.nextLine();out.println(name);System.out.println(name+",欢迎进⼊聊天室,输⼊quit退出");while(flag) {String read=scan.nextLine();if(read.equalsIgnoreCase("quit")) {flag=false;}//System.out.println(read);out.println(read);}s.close();}public void receive() throws IOException {byte ss[]=new byte[1024];int length=s.getInputStream().read(ss);System.out.println(new String(ss,0,length));}}服务端代码:package com.cky.server;import java.io.BufferedReader;import java.io.IOException;import java.io.InputStreamReader;import java.io.PrintWriter;import .ServerSocket;import .Socket;import java.util.ArrayList;import java.util.List;public class Server {private List<ThreadServer> clients=new ArrayList<ThreadServer>();public void startup() throws IOException {System.out.println("监听5001端⼝");ServerSocket ss=new ServerSocket(5001);while(true){Socket socket=ss.accept();System.out.println("发现新⽤户");Thread st=new Thread(new ThreadServer(socket));st.start();}}public class ThreadServer implements Runnable{private Socket socket;private BufferedReader br;private PrintWriter out;private String name;private Boolean flag=true;public ThreadServer(Socket socket) throws IOException {this.socket=socket;br=new BufferedReader(new InputStreamReader(socket.getInputStream())); out=new PrintWriter(socket.getOutputStream(),true);String str=br.readLine();name=str+"["+socket.getInetAddress().getHostAddress()+":"+socket.getPort()+"]"; System.out.println(name+"加⼊该聊天室");send(name+"加⼊该聊天室");clients.add(this);}private void send(String message) {for (ThreadServer threadServer : clients) {System.out.println("-->已向线程"++"发送消息");threadServer.out.print(message);threadServer.out.flush();}}private void receive() throws IOException {String message;while(flag=true) {message=br.readLine();if(message.equalsIgnoreCase("quit")) {System.out.println("⽤户"+name+"退出了");out.println("quit");out.flush();clients.remove(this);flag=false;}System.out.println(name+":"+message);send(name+":"+message);}}@Overridepublic void run() {try {while(flag=true) {receive();}} catch (IOException e) {e.printStackTrace();}finally {try {socket.close();} catch (IOException e) {e.printStackTrace();}}}}public static void main(String []args) throws IOException {Server server=new Server();System.out.println("服务器开启");server.startup();}}先启动服务端,监听端⼝,再使⽤客户端登录发送消息。
javaweb与websocket实现在线聊天功能总结技术从⼀开始ajax轮询后来改成websocket 碰到的⼀些问题的处理:websocket的pom依赖<dependency><groupId>org.springframework</groupId><artifactId>spring-websocket</artifactId><version>4.0.5.RELEASE</version></dependency>⾸先是配置处理器import javax.annotation.Resource;import ponent;import org.springframework.web.servlet.config.annotation.WebMvcConfigurerAdapter;import org.springframework.web.socket.config.annotation.EnableWebSocket;import org.springframework.web.socket.config.annotation.WebSocketConfigurer;import org.springframework.web.socket.config.annotation.WebSocketHandlerRegistry;/*** WebScoket配置处理器* @author Goofy* @Date 2015年6⽉11⽇下午1:15:09*/@Component@EnableWebSocketpublic class WebSocketConfig extends WebMvcConfigurerAdapter implements WebSocketConfigurer {@ResourceMyWebSocketHandler handler;public void registerWebSocketHandlers(WebSocketHandlerRegistry registry) {registry.addHandler(handler, "/ws").addInterceptors(new HandShake());registry.addHandler(handler, "/ws/sockjs").addInterceptors(new HandShake()).withSockJS();}}2.请求所经过的握⼿拦截器主要⽤来将ServerHttpRequest⾥的session的⽤户信息存放在attributes⾥到处理的handle中会⾃动存⼊websocketsession的attribute⾥import java.util.Map;import javax.servlet.http.HttpSession;import org.springframework.http.server.ServerHttpRequest;import org.springframework.http.server.ServerHttpResponse;import org.springframework.http.server.ServletServerHttpRequest;import org.springframework.web.socket.WebSocketHandler;import org.springframework.web.socket.server.HandshakeInterceptor;/*** Socket建⽴连接(握⼿)和断开** @author Goofy* @Date 2015年6⽉11⽇下午2:23:09*/public class HandShake implements HandshakeInterceptor {public boolean beforeHandshake(ServerHttpRequest request, ServerHttpResponse response, WebSocketHandler wsHandler, Map<String, Object> attributes) throws Exception { System.out.println("Websocket:⽤户[ID:" + ((ServletServerHttpRequest) request).getServletRequest().getSession(false).getAttribute("uid") + "]已经建⽴连接");if (request instanceof ServletServerHttpRequest) {ServletServerHttpRequest servletRequest = (ServletServerHttpRequest) request;HttpSession session = servletRequest.getServletRequest().getSession(false);// 标记⽤户Long uid = (Long) session.getAttribute("uid");if(uid!=null){attributes.put("uid", uid);}else{return false;}}return true;}public void afterHandshake(ServerHttpRequest request, ServerHttpResponse response, WebSocketHandler wsHandler, Exception exception) {}}Socket处理器处理连接通信或错误关闭等import java.io.IOException;import java.text.SimpleDateFormat;import java.util.Date;import java.util.HashMap;import java.util.Iterator;import java.util.Map;import java.util.Map.Entry;import ponent;import org.springframework.web.socket.CloseStatus;import org.springframework.web.socket.TextMessage;import org.springframework.web.socket.WebSocketHandler;import org.springframework.web.socket.WebSocketMessage;import org.springframework.web.socket.WebSocketSession;import org.xdemo.example.websocket.entity.Message;import com.google.gson.Gson;import com.google.gson.GsonBuilder;/*** Socket处理器** @author Goofy* @Date 2015年6⽉11⽇下午1:19:50*/@Componentpublic class MyWebSocketHandler implements WebSocketHandler {public static final Map<Long, WebSocketSession> userSocketSessionMap;static {userSocketSessionMap = new HashMap<Long, WebSocketSession>();}/*** 建⽴连接后*/public void afterConnectionEstablished(WebSocketSession session)throws Exception {Long uid = (Long) session.getAttributes().get("uid");if (userSocketSessionMap.get(uid) == null) {userSocketSessionMap.put(uid, session);}}/*** 消息处理,在客户端通过Websocket API发送的消息会经过这⾥,然后进⾏相应的处理*/public void handleMessage(WebSocketSession session, WebSocketMessage<?> message) throws Exception {if(message.getPayloadLength()==0)return;Message msg=new Gson().fromJson(message.getPayload().toString(),Message.class);msg.setDate(new Date());sendMessageToUser(msg.getTo(), new TextMessage(new GsonBuilder().setDateFormat("yyyy-MM-dd HH:mm:ss").create().toJson(msg))); }/*** 消息传输错误处理*/public void handleTransportError(WebSocketSession session,Throwable exception) throws Exception {if (session.isOpen()) {session.close();}Iterator<Entry<Long, WebSocketSession>> it = userSocketSessionMap.entrySet().iterator();// 移除Socket会话while (it.hasNext()) {Entry<Long, WebSocketSession> entry = it.next();if (entry.getValue().getId().equals(session.getId())) {userSocketSessionMap.remove(entry.getKey());System.out.println("Socket会话已经移除:⽤户ID" + entry.getKey());break;}}}/*** 关闭连接后*/public void afterConnectionClosed(WebSocketSession session,CloseStatus closeStatus) throws Exception {System.out.println("Websocket:" + session.getId() + "已经关闭");Iterator<Entry<Long, WebSocketSession>> it = userSocketSessionMap.entrySet().iterator();// 移除Socket会话if (entry.getValue().getId().equals(session.getId())) {userSocketSessionMap.remove(entry.getKey());System.out.println("Socket会话已经移除:⽤户ID" + entry.getKey());break;}}}public boolean supportsPartialMessages() {return false;}/*** 给所有在线⽤户发送消息** @param message* @throws IOException*/public void broadcast(final TextMessage message) throws IOException {Iterator<Entry<Long, WebSocketSession>> it = userSocketSessionMap.entrySet().iterator();// 多线程群发while (it.hasNext()) {final Entry<Long, WebSocketSession> entry = it.next();if (entry.getValue().isOpen()) {// entry.getValue().sendMessage(message);new Thread(new Runnable() {public void run() {try {if (entry.getValue().isOpen()) {entry.getValue().sendMessage(message);}} catch (IOException e) {e.printStackTrace();}}}).start();}}}/*** 给某个⽤户发送消息** @param userName* @param message* @throws IOException*/public void sendMessageToUser(Long uid, TextMessage message)throws IOException {WebSocketSession session = userSocketSessionMap.get(uid);if (session != null && session.isOpen()) {session.sendMessage(message);}}} 页⾯<%@ page language="java" import="java.util.*" pageEncoding="UTF-8"%><%String path = request.getContextPath();String basePath = request.getServerName() + ":"+ request.getServerPort() + path + "/";String basePath2 = request.getScheme() + "://"+ request.getServerName() + ":" + request.getServerPort()+ path + "/";%><!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01//EN""/TR/html4/strict.dtd"><html xmlns="/1999/xhtml" lang="en"><head><meta http-equiv="Content-Type" content="text/html; charset=utf-8" /><title></title><script type="text/javascript" src="<%=basePath2%>resources/jquery.js"></script> <style>textarea {height: 300px;width: 100%;outline: none;}input[type=button] {float: right;margin: 5px;width: 50px;height: 35px;border: none;color: white;font-weight: bold;outline: none;}.clear {background: red;}.send {background: green;}.clear:active {background: yellow;}.send:active {background: yellow;}.msg {width: 100%;height: 25px;outline: none;}#content {border: 1px solid gray;width: 100%;height: 400px;overflow-y: scroll;}.from {background-color: green;width: 80%;border-radius: 10px;height: 30px;line-height: 30px;margin: 5px;float: left;color: white;padding: 5px;font-size: 22px;}.to {background-color: gray;width: 80%;border-radius: 10px;height: 30px;line-height: 30px;margin: 5px;float: right;color: white;padding: 5px;font-size: 22px;}.name {color: gray;font-size: 12px;}.tmsg_text {color: white;background-color: rgb(47, 47, 47); font-size: 18px;border-radius: 5px;padding: 2px;}.fmsg_text {color: white;background-color: rgb(66, 138, 140); font-size: 18px;border-radius: 5px;}.sfmsg_text {color: white;background-color: rgb(148, 16, 16);font-size: 18px;border-radius: 5px;padding: 2px;}.tmsg {clear: both;float: right;width: 80%;text-align: right;}.fmsg {clear: both;float: left;width: 80%;}</style><script>var path = '<%=basePath%>';var uid=${uid eq null?-1:uid};if(uid==-1){location.href="<%=basePath2%>";}var from=uid;var fromName='${name}';var to=uid==1?2:1;var websocket;if ('WebSocket' in window) {websocket = new WebSocket("ws://" + path + "/ws?uid="+uid);} else if ('MozWebSocket' in window) {websocket = new MozWebSocket("ws://" + path + "/ws"+uid);} else {websocket = new SockJS("http://" + path + "/ws/sockjs"+uid);}websocket.onopen = function(event) {console.log("WebSocket:已连接");console.log(event);};websocket.onmessage = function(event) {var data=JSON.parse(event.data);console.log("WebSocket:收到⼀条消息",data);var textCss=data.from==-1?"sfmsg_text":"fmsg_text";$("#content").append("<div class='fmsg'><label class='name'>"+data.fromName+" "+data.date+"</label><div class='"+textCss+"'>"+data.text+"</div></div>");scrollToBottom();};websocket.onerror = function(event) {console.log("WebSocket:发⽣错误 ");console.log(event);};websocket.onclose = function(event) {console.log("WebSocket:已关闭");console.log(event);}function sendMsg(){var v=$("#msg").val();if(v==""){return;}else{var data={};data["from"]=from;data["fromName"]=fromName;data["to"]=to;data["text"]=v;websocket.send(JSON.stringify(data));$("#content").append("<div class='tmsg'><label class='name'>我 "+new Date().Format("yyyy-MM-dd hh:mm:ss")+"</label><div class='tmsg_text'>"+data.text+"</div></div>"); scrollToBottom();$("#msg").val("");}}function scrollToBottom(){var div = document.getElementById('content');div.scrollTop = div.scrollHeight;}Date.prototype.Format = function (fmt) { //author: meizzvar o = {"M+": this.getMonth() + 1, //⽉份"d+": this.getDate(), //⽇"h+": this.getHours(), //⼩时"s+": this.getSeconds(), //秒"q+": Math.floor((this.getMonth() + 3) / 3), //季度"S": this.getMilliseconds() //毫秒};if (/(y+)/.test(fmt)) fmt = fmt.replace(RegExp.$1, (this.getFullYear() + "").substr(4 - RegExp.$1.length));for (var k in o)if (new RegExp("(" + k + ")").test(fmt)) fmt = fmt.replace(RegExp.$1, (RegExp.$1.length == 1) ? (o[k]) : (("00" + o[k]).substr(("" + o[k]).length)));return fmt;}function send(event){var code;if(window.event){code = window.event.keyCode; // IE}else{code = e.which; // Firefox}if(code==13){sendMsg();}}function clearAll(){$("#content").empty();}</script></head><body>欢迎:${ }<div id="content"></div><input type="text" placeholder="请输⼊要发送的信息" id="msg" class="msg" onkeydown="send(event)"><input type="button" value="发送" class="send" onclick="sendMsg()" ><input type="button" value="清空" class="clear" onclick="clearAll()"></body></html> 碰到的⼀些问题以及处理的想法1.之前在拦截器的请求⾥⾯得不到session 使⽤从页⾯传进来的get⽅式得到参数uid(后来也没改什么调试的时候发现可以得到session)2.客服防⽌多处登陆的处理:每次登陆获取loginusermap 如果没有该⽤户新增有就更新然后替换map到session中登陆拦截器校验登陆的⽤户的sessionid和map⾥的是否⼀致不⼀致说明被挤重新跳转loginUserMap.put(user.getFid(), sessionId);request.getSession().getServletContext().setAttribute("loginUserMap", loginUserMap);3.按钮嵌在右侧的⼯具栏点击固定只弹出⼀个聊天⽹页var op;op = window.open("${webRoot}customerChat.json","newWin1");op.focus();4.兼容ios(看了请求头⾥的sec-websocket-extensions不⼀致就想的馊主意改头结果可⽤)if(request.getHeaders().get("sec-websocket-extensions").contains("x-webkit-deflate-frame")){List<String> list = new ArrayList<String>();list.add("permessage-deflate");request.getHeaders().remove("sec-websocket-extensions");request.getHeaders().put("sec-websocket-extensions",list);}兼容nginxupstream {server 127.0.0.1:8080;}server {listen 80;server_name localhost;location / {proxy_pass /;proxy_http_version 1.1;proxy_set_header Upgrade $http_upgrade;proxy_read_timeout 1000s;}}前端WebSocket 连接关闭(代码:1006) 还需配置proxy_read_timeout 1000s;哈哈第⼀次写博客有点菜鸡 websokcet部分也是从⽹上找的demo。
基于websocket和java的多人聊天室最新基于websocket与java的多人聊天室实现架构html5+websocket+javaEE7+tomcat8JavaEE7 最新的websocket1.0 APITomcat8开始支持websocket1.0 API【Tomcat implements the Java WebSocket 1.0 API defined by JSR-356】在编写代码之前你要导入javaEE7的jar包以便使用websocket API,将此项目部署到tomcat8里面。
具体代码如下:Java端:ChatAnnotation类;使用的是注解的方式。
package websocket.chat;import java.io.IOException;import java.util.Set;import java.util.concurrent.CopyOnWriteArraySet;import java.util.concurrent.atomic.AtomicInteger;import javax.websocket.OnClose;import javax.websocket.OnError;import javax.websocket.OnMessage;import javax.websocket.OnOpen;import javax.websocket.Session;import javax.websocket.server.ServerEndpoint;import org.apache.juli.logging.Log;import org.apache.juli.logging.LogFactory;import util.HTMLFilter;@ServerEndpoint(value = "/websocket/chat")public class ChatAnnotation {private static final Log log =LogFactory.getLog(ChatAnnotation.class);private static final String GUEST_PREFIX = "Guest";private static final AtomicInteger connectionIds = new AtomicInteger(0);private static final Set<ChatAnnotation> connections =new CopyOnWriteArraySet<>();private final String nickname;private Session session;public ChatAnnotation() {nickname = GUEST_PREFIX + connectionIds.getAndIncrement(); }@OnOpenpublic void start(Session session) {this.session = session;connections.add(this);String message = String.format("* %s %s", nickname, "has joined.");broadcast(message);}@OnClosepublic void end() {connections.remove(this);String message = String.format("* %s %s",nickname, "has disconnected.");broadcast(message);}@OnMessagepublic void incoming(String message) {// Never trust the clientString filteredMessage = String.format("%s: %s",nickname, HTMLFilter.filter(message.toString()));broadcast(filteredMessage);}@OnErrorpublic void onError(Throwable t) throws Throwable {log.error("Chat Error: " + t.toString(), t);}private static void broadcast(String msg) {for (ChatAnnotation client : connections) {try {synchronized (client) {client.session.getBasicRemote().sendText(msg);}} catch (IOException e) {log.debug("Chat Error: Failed to send message to client", e);connections.remove(client);try {client.session.close();} catch (IOException e1) {// Ignore}String message = String.format("* %s %s",client.nickname, "has been disconnected."); broadcast(message);}}}}里面的juli.jar包可以百度一下自行下载。
util.HTMLFilter类如下:HTMLFilter类。
package util;/*** HTML filter utility.** @author Craig R. McClanahan* @author Tim Tye*/public final class HTMLFilter {/*** Filter the specified message string for characters that are sensitive* in HTML. This avoids potential attacks caused by including JavaScript* codes in the request URL that is often reported in error messages. ** @param message The message string to be filtered*/public static String filter(String message) {if (message == null)return (null);char content[] = new char[message.length()];message.getChars(0, message.length(), content, 0);StringBuilder result = new StringBuilder(content.length + 50); for (int i = 0; i < content.length; i++) {switch (content[i]) {case '<':result.append("<");break;case '>':result.append(">");break;case '&':result.append("&");break;case '"':result.append(""");break;default:result.append(content[i]);}}return (result.toString());}}接下来是web端:Chat.xhtml<?xml version="1.0" encoding="UTF-8"?><html xmlns="/1999/xhtml" xml:lang="en"><head><title>Apache Tomcat WebSocket Examples: Chat</title><style type="text/css"><![CDATA[input#chat {width: 410px}#console-container {width: 400px;}#console {border: 1px solid #CCCCCC;border-right-color: #999999;border-bottom-color: #999999;height: 170px;overflow-y: scroll;padding: 5px;width: 100%;}#console p {padding: 0;margin: 0;}]]></style><script type="application/javascript"><![CDATA["use strict";var Chat = {};Chat.socket = null;Chat.connect = (function(host) {if ('WebSocket' in window) {Chat.socket = new WebSocket(host);} else if ('MozWebSocket' in window) {Chat.socket = new MozWebSocket(host);} else {Console.log('Error: WebSocket is not supported by this browser.');return;}Chat.socket.onopen = function () {Console.log('Info: WebSocket connection opened.');document.getElementById('chat').onkeydown =function(event) {if (event.keyCode == 13) {Chat.sendMessage();}};};Chat.socket.onclose = function () {document.getElementById('chat').onkeydown = null; Console.log('Info: WebSocket closed.');};Chat.socket.onmessage = function (message) {Console.log(message.data);};});Chat.initialize = function() {if (window.location.protocol == 'http:') {Chat.connect('ws://' + window.location.host +'/examples/websocket/chat');} else {Chat.connect('wss://' + window.location.host +'/examples/websocket/chat');}};Chat.sendMessage = (function() {var message = document.getElementById('chat').value; if (message != '') {Chat.socket.send(message);document.getElementById('chat').value = '';}});var Console = {};Console.log = (function(message) {var console = document.getElementById('console');var p = document.createElement('p');p.style.wordWrap = 'break-word';p.innerHTML = message;console.appendChild(p);while (console.childNodes.length > 25) {console.removeChild(console.firstChild);}console.scrollTop = console.scrollHeight;});Chat.initialize();document.addEventListener("DOMContentLoaded", function() {// Remove elements with "noscript" class - <noscript> is not allowed in XHTMLvar noscripts =document.getElementsByClassName("noscript");for (var i = 0; i < noscripts.length; i++) {noscripts[i].parentNode.removeChild(noscripts[i]);}}, false);]]></script></head><body><div class="noscript"><h2 style="color: #ff0000">Seems your browser doesn't support Javascript! Websockets rely on Javascript being enabled. Please enableJavascript and reload this page!</h2></div><div><p><input type="text" placeholder="type and press enter to chat" id="chat" /></p><div id="console-container"><div id="console"/></div></div></body></html>亲自测试:如果按照以上步骤部署项目,保证能顺利运行。