利用spring boot开发websocket,spring boot环境要做好,没做好也没关系,本章节,将从零开始搭建。

话不多说,直接上代码。

一、对于有spring boot环境的开发步骤。没有环境的拉到最下面,有搭建环境的pom和application配置,至于怎么新建maven工程,应该不用说了吧,默认你们看本贴吧的都会了- -!:

1、创建WebSocketConfig

package com.bm.config;

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.socket.server.standard.ServerEndpointExporter;

/**
 * @author: zxy
 * @data: 2020年3月18日 下午5:07:24
 * @description: 类的描述
 **/
@Configuration
public class WebSocketConfig {
    @Bean
    public ServerEndpointExporter serverEndpointExporter() {
        return new ServerEndpointExporter();
    }

}

2、创建WebSocketServer

package com.bm.service;

import java.io.IOException;
import java.util.Collections;
import java.util.HashMap;
import java.util.Map;

import javax.websocket.OnClose;
import javax.websocket.OnError;
import javax.websocket.OnMessage;
import javax.websocket.OnOpen;
import javax.websocket.Session;
import javax.websocket.server.PathParam;
import javax.websocket.server.ServerEndpoint;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Component;
import org.springframework.util.StringUtils;

import com.bm.utils.AddOnlineCount;
import com.bm.utils.SubOnlineCount;

/**
 * @author: zxy
 * @data: 2020年3月18日 下午4:59:48
 * @description: 类的描述
 **/

@ServerEndpoint("/websocket/{userId}")
@Component
public class WebSocketServer {

	/**
	 * 日志对象
	 */
	protected Logger log = LoggerFactory.getLogger(getClass());

	// 静态变量,用来记录当前在线连接数。应该把它设计成线程安全的。
	public static int onlineCount;
	// 创建一个线程安全的map,正式上线,把消息记录放进缓存更好一些,当所有用户退出时,清除缓存
	private static Map<String, WebSocketServer> users = Collections.synchronizedMap(new HashMap());

	// 与某个客户端的连接会话,需要通过它来给客户端发送数据
	private Session session;
	// 放入map中的key,用来表示该连接对象
	private String username;

	/**
	 * 连接建立成功调用的方法
	 */
	@OnOpen
	public void onOpen(Session session, @PathParam("userId") String username) {
		this.session = session;
		this.username = username;
		users.put(username, this); // 加入map中,为了测试方便使用username做key

		AddOnlineCount addOnlineCount = new AddOnlineCount(session, username, onlineCount);
		Thread usernameThread = new Thread(addOnlineCount, username);
		usernameThread.start();
	}

	/**
	 * 连接关闭调用的方法
	 */
	@OnClose
	public void onClose() {
		users.remove(this.username); // 从set中删除
		SubOnlineCount subOnlineCount = new SubOnlineCount(username, onlineCount);
		Thread usernameThread = new Thread(subOnlineCount, username);
		usernameThread.start();
	}

	/**
	 * 收到客户端消息后触发的方法
	 * 
	 * @param message 客户端发送过来的消息
	 */
	@OnMessage
	public void onMessage(String message) {
		log.info("来自客户端的消息:" + message);
		// 群发消息
		try {
			if (StringUtils.isEmpty(message)) {
				return;
			}
			sendInfo(username + ": " + message);
		} catch (IOException e) {
			e.printStackTrace();
		}
	}

	@OnError
	public void onError(Session session, Throwable error) {
		log.error("发生错误 session: " + session);
		error.printStackTrace();
	}

	/**给特定人员发送消息
	 * @param username
	 * @param message
	 * @throws IOException
	 */
	public void sendMessageToSomeBody(String username, String message) throws IOException {
		if (users.get(username) == null) {
			return;
		}
		users.get(username).session.getBasicRemote().sendText(message);
		this.session.getBasicRemote().sendText(this.username + "@" + username + ": " + message);
	}

	/**
	 * 群发自定义消息
	 */
	public void sendInfo(String message) throws IOException {
		for (WebSocketServer item : users.values()) {
			try {
				item.session.getBasicRemote().sendText(message);
			} catch (IOException e) {
				continue;
			}
		}
	}

}

3、创建AddOnlineCount(增加在线人数)、SubOnlineCount(减少在线人数)、SpringContextUtil

package com.bm.utils;

import java.io.IOException;

import javax.websocket.Session;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import com.bm.service.WebSocketServer;

/**
 * @author: zxy
 * @data: 2020年3月19日 上午9:00:27
 * @description: 将在线人数变量设置为线程安全的,增加在线人数
 **/
public class AddOnlineCount extends Thread {

	/**
	 * 日志对象
	 */
	protected Logger log = LoggerFactory.getLogger(getClass());

	/**
	 * 与某个客户端的连接会话,需要通过它来给客户端发送数据
	 */
	private Session session;
	
	/**
	 * 放入map中的key,用来表示该连接对象
	 */
	private String username;
	
	/**
	 * 在线人数
	 */
	private int onlineCount = 0;
	
	public AddOnlineCount(Session session, String username, int onlineCount) {
		super();
		this.session = session;
		this.username = username;
		this.onlineCount = onlineCount;
	}


	synchronized public void run() {
		onlineCount++;
		WebSocketServer.onlineCount=onlineCount;
		log.info(username + "加入!当前在线人数为" + onlineCount);
		try {
			this.session.getBasicRemote().sendText(username + "进入群聊。在线人数:" + onlineCount);
		} catch (IOException e) {
			log.error("websocket IO异常");
		}
	}

}
package com.bm.utils;

import java.io.IOException;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import com.bm.service.WebSocketServer;

/**
 * @author: zxy
 * @data: 2020年3月19日 上午9:00:27
 * @description: 将在线人数变量设置为线程安全的,减少在线人数
 **/

public class SubOnlineCount extends Thread {

	/**
	 * 日志对象
	 */
	protected Logger log = LoggerFactory.getLogger(getClass());
	
	/**
	 * 放入map中的key,用来表示该连接对象
	 */
	private String username;
	
	/**
	 * 在线人数
	 */
	private int onlineCount = 0;
	
	public SubOnlineCount(String username, int onlineCount) {
		super();
		this.username = username;
		this.onlineCount = onlineCount;
	}

	synchronized public void run() {
		onlineCount--;
		WebSocketServer.onlineCount=onlineCount;
		try {
			WebSocketServer server = SpringContextUtil.getBean(WebSocketServer.class);
			server.sendInfo(username+" 离开了聊天室。在线人数:"+onlineCount);
		} catch (IOException e) {
			e.printStackTrace();
		}
		log.info(username+" 离开了聊天室。在线人数:"+onlineCount);
	}

}
package com.bm.utils;

import org.springframework.beans.BeansException;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
import org.springframework.stereotype.Component;

/**
 * @author: zxy
 * @data: 2020年3月19日 上午11:13:02
 * @description: 获取bean
 **/
@Component
public class SpringContextUtil implements ApplicationContextAware {
	
	private static ApplicationContext applicationContext;

	@Override
	public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
		SpringContextUtil.applicationContext=applicationContext;
	}
	
	/**
	 * 获取ApplicationContext
	 * @return
	 */
	public static ApplicationContext getApplicationContext() {
        return applicationContext;
    }
	
    /**
     * 通过name获取 Bean
     * @param name
     * @return
     */
    public static Object getBean(String name) {
        return getApplicationContext().getBean(name);
    }

    /**
     * 通过class获取Bean
     * @param clazz
     * @return
     */
    public static <T> T getBean(Class<T> clazz) {
        return getApplicationContext().getBean(clazz);
    }

    /**
     * 通过name,以及Clazz返回指定的Bean
     * @param name
     * @param clazz
     * @return
     */
    public static <T> T getBean(String name, Class<T> clazz) {
        return getApplicationContext().getBean(name, clazz);
    }

}

4、创建controller

package com.bm.controller;

import java.io.IOException;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseBody;

import com.bm.service.WebSocketServer;

/**
 * @author: zxy
 * @data: 2020年3月18日 下午5:07:24
 * @description: 类的描述
 **/
@Controller
@RequestMapping("/webSocket")
public  class WebSocketController {
    @Autowired
    WebSocketServer server;
    
    @RequestMapping("/page")
    public String page() {
    	System.out.println("dasda");
    	return "page/page";
    }
    
    @PostMapping("/login")
    @ResponseBody
    public String login(String username,String password) throws IOException {
        server.sendInfo(username + "进入了群聊");
        return username;
    }
}

5、创建访问页面html,路径:src/main/resources/templates/page/page.html,js就自己准备吧。路径放在:src/main/resources/static下面。没有templates和static文件夹的,自己手动新建出来。

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<body>

<form id="loginForm"  >
    用户名: <input name="username">
    <br>
    <input type="button" value="登录" onclick="login()" />
</form>

<textarea id="msg" placeholder=""  style="width: 500px;height: 50px" ></textarea>
<input type="button" onclick="send()" value="发送消息"  >
<br>
<textarea id="history"   style="width: 500px;height: 200px ; max-lines: 10" >
</textarea>

<script src="/modules/jquery/jquery-2.1.1.min.js"></script>
<script type="application/javascript">
        var socket ;
        //登录过后初始化socket连接
        function initSocket(userId) {
            if(typeof(WebSocket) == "undefined") {
                console.log("您的浏览器不支持WebSocket");
            }else {
                console.log("您的浏览器支持WebSocket/websocket");
            }
            //socket连接地址: 注意是ws协议
            socket = new WebSocket("ws://192.168.11.112:8085/websocket/"+userId);
            socket.onopen = function() {
                console.log("Socket 已打开");
            };
            //获得消息事件
            socket.onmessage = function(msg) {
                var histroy = $("#history").val();
                $("#history").val(histroy+"\r\n"+msg.data);
                console.log($(msg));
            };
            //关闭事件
            socket.onclose = function() {
                console.log("Socket已关闭");
            };
            //错误事件
            socket.onerror = function() {
                alert("Socket发生了错误");
            }
            $(window).unload(function(){
                socket.close();
            });
        }
        //点击按钮发送消息
        function send() {
            console.log("发送消息: "+$("#msg").val());
            socket.send($("#msg").val());
        }
        //登录
        function login() {
            $.ajax({
                url: "/webSocket/login",
                data: $("#loginForm").serialize(),
                type: "POST",
                success: function (userId) {
                    if ( userId){
                        console.log("登录成功!");
                        initSocket(userId);
                    }
                }
            });
        }
</script>
</body>
</html>

二、没有spring boot环境的,搭建环境

1、pom

<project xmlns="http://maven.apache.org/POM/4.0.0"
	xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
	xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
	<modelVersion>4.0.0</modelVersion>
	<groupId>com</groupId>
	<artifactId>bmSystem</artifactId>
	<version>0.0.1-SNAPSHOT</version>
	<packaging>jar</packaging>

	<!-- Spingboot相关jar包版本 -->
	<parent>
		<groupId>org.springframework.boot</groupId>
		<artifactId>spring-boot-starter-parent</artifactId>
		<version>2.0.5.RELEASE</version>
	</parent>

	<!-- 相关jar包 -->
	<dependencies>
		<!-- Springboot核心jar包 -->
		<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-starter</artifactId>
		</dependency>
		<!-- web开发包:包含Tomcat和Springmvc -->
		<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-starter-web</artifactId>
		</dependency>
		<!-- Junit测试jar包 -->
		<dependency>
			<groupId>junit</groupId>
			<artifactId>junit</artifactId>
			<scope>test</scope>
		</dependency>
		<!-- spring-boot热部署 -->
		<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-devtools</artifactId>
		</dependency>
		<!-- thymeleaf -->
		<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-starter-thymeleaf</artifactId>
		</dependency>
	    <!--websocket-->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-websocket</artifactId>
        </dependency>
	
	
	</dependencies>
</project>

2、application.ym,路径不用说了吧。

#服务器配置
server:
  port: 8085

#spring相关配置
spring:
  thymeleaf:
    prefix: classpath:/templates/
    suffix: .html
    mode: HTML5
    encoding: UTF-8
    servlet: 
      content-type: text/html
    cache: false

 

代码写到这里就可以启动服务了。页面如下:

springboot使用socket通信 springboot启动socket_java