参考案例
websocket是全双工通道,建立连接后可以双向发送消息。
Tomcat 7.0.5之后开始支持websocket,并实现了websocket规范。Java websocket应用由一系列WebSocketEndpoint组成。一个Endpoint是一个Java对象,代表WebSocket链接的一段端,对于服务端,可以视为处理WebSocket消息的接口,就像servlet处理http一样。(注:建立一个websocket链接就会新建一个Endpoint对象)
有两种方式定义Endpointt:
1、继承javax.websocket.Endpoint
类并实现其方法。
2、定义POJO 并添加@ServerEndpoint
注解
通过Session(和http中的session不同)添加MessageHandler消息处理器来接受消息,当采用注解方式定义Endpoint时,通过@OnMessage注解指定接受消息的方法。
发送消息是由RemoteEndpoint完成,其实例由Session维护,根据使用情况,我们可以通过Session.getBasicRemote获取同步消息发送实例,然后调用sendXxx()方法可以发送消息,可以通过Session.getAsyncRemote获取异步消息发送实例。
onOpen方法:建立链接之后自动调用
onClose方法:链接关闭自动调用
onError方法:链接中出现问题自动调用
@OnClose
@OnOpen
@OnError
示例代码
@ServerEndpoint("/robin")//后面是资源路径
public class ChatEndPoint{
private static Set<ChatEndPoint> webSocketSet = new HashSet<>();
private Session session;
@OnMessage
public void onMessage(String message,Session session)throws IOException{
//message 是接受到的客户端发送来的消息;session
System.out.println("get message"+message);
System.out.println(session);
//将消息发给其他用户
for(ChatEndPoint chat:webSocketSet){
if(chat!=this){
chat.session.getBasicRemote().setText(message);
}
}
}
@OnOpen
public void onOpen(Session session){
this.session = session;
webSocketSet.add(this);
}
@OnClose
public void onClose(Session session){
}
@OnError
public void onError(Session session,Throwable error){
}
}
webSocket依赖
<dependency>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-starter-websocketartifactId>
dependency>
Message类,客户端to服务端
@Data
public class Message{
private String toName;
private String message;
}
ResultMessage,服务端to客户端
@Data
public class ResultMessage{
private boolean isSystem;
private String fromName;
private Object message;//如果是系统消息则是数组
}
MessageUtils,消息工具类
public class MessageUtils {
public static String getMessage(boolean isSystemMessage,String fromName,Object message){
try {
ResultMessage result = new ResultMessage();
result.setSystem(isSystemMessage);
result.setMessage(message);
if (fromName!=null){
result.setFromName(fromName);
}
//把字符串转成json格式的字符串
ObjectMapper mapper = new ObjectMapper();
return mapper.writeValueAsString(result);
}catch (JsonProcessingException e){
e.printStackTrace();
}
return null;
}
}
@RestController
public class LoginController {
@RequestMapping("/toLogin")
public Result tologin(@RequestParam("user") String user,@RequestParam("pwd") String pwd, HttpSession session){
Result result = new Result();
if (user.equals("张三")&&pwd.equals("123")){
result.setFlag(true);
session.setAttribute("user",user);
}else if (user.equals("李四")&&pwd.equals("123")){
result.setFlag(true);
session.setAttribute("user",user);
}else if (user.equals("123")&&pwd.equals("123")){
result.setFlag(true);
session.setAttribute("user",user);
}
else if (user.equals("王五")&&pwd.equals("123")){
result.setFlag(true);
session.setAttribute("user",user);
}else {
result.setFlag(false);
result.setMessage("登录失败");
}
return result;
}
@RequestMapping("/getUsername")
public String getUsername(HttpSession session){
String username = (String) session.getAttribute("user");
return username;
}
}
@ServerEndpoint(value = "/chat",configurator = GetHttpSessionConfigurator.class)//注意这里
@Component
public class ChatEndpoint {
//用来存储每个用户客户端对象的ChatEndpoint对象
private static Map<String,ChatEndpoint> onlineUsers = new ConcurrentHashMap<>();
//声明session对象,通过对象可以发送消息给指定的用户
private Session session;
//声明HttpSession对象,我们之前在HttpSession对象中存储了用户名
private HttpSession httpSession;
//连接建立
@OnOpen
public void onOpen(Session session, EndpointConfig config){
this.session = session;
HttpSession httpSession = (HttpSession) config.getUserProperties().get(HttpSession.class.getName());
this.httpSession = httpSession;
//存储登陆的对象
String username = (String)httpSession.getAttribute("user");
onlineUsers.put(username,this);
//将当前在线用户的用户名推送给所有的客户端
//1 获取消息
String message = MessageUtils.getMessage(true, null, getNames());
//2 调用方法进行系统消息的推送
broadcastAllUsers(message);
}
private void broadcastAllUsers(String message){
try {
//将消息推送给所有的客户端
Set<String> names = onlineUsers.keySet();
for (String name : names) {
ChatEndpoint chatEndpoint = onlineUsers.get(name);
chatEndpoint.session.getBasicRemote().sendText(message);
}
}catch (Exception e){
e.printStackTrace();
}
}
//返回在线用户名
private Set<String> getNames(){
return onlineUsers.keySet();
}
//收到消息
@OnMessage
public void onMessage(String message,Session session){
//将数据转换成对象
try {
ObjectMapper mapper =new ObjectMapper();
Message mess = mapper.readValue(message, Message.class);
String toName = mess.getToName();
String data = mess.getMessage();
String username = (String) httpSession.getAttribute("user");
String resultMessage = MessageUtils.getMessage(false, username, data);
//发送数据
onlineUsers.get(toName).session.getBasicRemote().sendText(resultMessage);
} catch (Exception e) {
e.printStackTrace();
}
}
//关闭
@OnClose
public void onClose(Session session) {
String username = (String) httpSession.getAttribute("user");
//从容器中删除指定的用户
onlineUsers.remove(username);
MessageUtils.getMessage(true,null,getNames());
}
}
需要加了配置类 Spring才会管理。
@Configuration
public class WebSocketConfig {
@Bean
//注入ServerEndpointExporter bean对象,自动注册使用注解@ServerEndpoint的bean
public ServerEndpointExporter serverEndpointExporter(){
return new ServerEndpointExporter();
}
}
获取HttpSession的类
public class GetHttpSessionConfigurator extends ServerEndpointConfig.Configurator {
@Override
public void modifyHandshake(ServerEndpointConfig sec, HandshakeRequest request, HandshakeResponse response) {
//获取HttpSession对象
HttpSession httpSession = (HttpSession) request.getHttpSession();
sec.getUserProperties().put(HttpSession.class.getName(),httpSession);
}
}