VUE+SpringBoot+Websocket实现前后端通信案例分享
PS→无奈:不为模糊不清的未来担忧,只为清清楚楚的现在努力。
版权声明:本文为博主原创文章,未经博主允许不得转载。
醒来无事就把先前做过的一个websocket实现扫码登录的案例记录一下,以免以后再次用到而毫无头绪。
一.在接到这个需求的时候,我们首先应该做的就是好好缕清自己的思路,并且如果之前对websocket没有了解过的朋友们可以先自行了解一下,不用了解的很深刻,大致了解它的使用方法以及使用途径。并且根据你自身的业务,在脑子里或者在草稿纸上绘画一张流程图,这样可以更清楚直观的知道每一步的逻辑目的。
二.我使用websocket就是为了使用app实现扫二维码成功登陆网页版的需求。我的思路是这样的:
1).首先在浏览器输入网页版项目路径的时候跳转至网页版登陆页面,此时使用websocket与后台进行连接。
2).登陆页的登录选项卡我们项目默认的是账号密码登陆选项卡,在点击扫码登录选项卡的时候触发一个函数跳转至后台生成一个跳转至app端验证的url二维码图片和一个唯一标识并且返回给前端。
3).用户用已经登录好的app扫描该二维码,跳转至app验证确认登录页面,app点击确认之后回调我们后端扫码登录的接口,是否成功,返回相应code给app端。
三.然而使用的过程中碰到很多坑,时间过了很久,大致的也往的差不多了,有连接始终连接不上的问题(后台项目使用shiro拦截掉了websocket的链接请求),也有消息发送失败的问题(后台无法获取到socket里面的Session),还有最后很搞的一个bug,就是多个电脑同时在登录页使用二维码登陆的话,用app扫描其中任何一个二维码,两个网页版项目都会登录。——O(∩_∩)O哈哈~(这个问题是因为后台发送消息的时候没有指定向哪个前端发送消息,而是广播式的发送消息给所有前端浏览器),这是三个大的问题,其余的也碰到一些小的问题,此处就不做详细的描述了。
直接上我们的主角(代码)上台表演吧!!!(相信大家最期待的就是这段表演了~~~~O(∩_∩)O哈哈~ 我也一样)
vue前端效果图及代码:效果图不咋的,因为我们公司没有前端所有前端都是我们后端自己搞的~~~尴尬(*^▽^*)
<el-col id="login-box" :xs="{span: 22,offset: 1}" :sm="{span: 20,offset: 2}" :md="{span: 7, offset: 1}">
<el-menu :default-active="activeIndex" v-model="menuIndex" class="el-menu-demo" mode="horizontal"
@select="handleSelect" active-text-color="#FECE00">
<el-menu-item index="1" @click="saoma" class="menu-item">扫码登录</el-menu-item>
<el-menu-item index="2" class="menu-item">蘑菇丁账号登录</el-menu-item>
</el-menu>
<div id="login-box-context">
<el-form id="login-form" ref="loginForm" :model="form" v-show="menuIndex==2" :rules="rules">
<el-form-item prop="username">
<el-input v-model="form.username" placeholder="请输入账号"></el-input>
</el-form-item>
<el-form-item prop="password">
<el-input v-model="form.password" @keyup.enter.native="doLogin" placeholder="请输入密码" type="password"></el-input>
</el-form-item>
<el-form-item prop="captcha">
<el-row :gutter="20">
<el-col :span="14">
<el-input v-model="form.captcha" placeholder="请输入验证码" @keyup.enter.native="doLogin">
</el-input>
</el-col>
<el-col :span="10" class="login-captcha">
<img :src="captchaPath" @click="getCaptcha()" alt="">
</el-col>
</el-row>
</el-form-item>
<el-form-item>
<el-button @click="onSubmit" type="primary" :disabled="config.disabled" style="background-color: #FECE00;border-color: #FECE00;">
登录
</el-button>
</el-form-item>
</el-form>
<div id="qc-login" v-show="menuIndex==1">
<div id="qrcode">
<img alt="" src="" id="pic"/>
<div id="gone" style="display: none;position: absolute;height: 200px;
width: 200px;color: rgb(255, 255, 255); z-index: 1;
line-height: 54px;overflow: hidden;bottom: 80px;
background: rgba(0, 0, 0, 0.7) none repeat scroll 0% 0%;">
<span style="font-size: 20px; font-weight: bolder;
margin-left: 40px;position: absolute;top: 50px;">二维码已失效</span><br/>
<button id="shua" @click="shuaxin" style="background-color: #f9c616;
width: 90px;height: 35px; font-size: 17px;font-family: 黑体;
margin-left: 55px;position: relative;top: 50px;line-height: 1;
border: 1px solid rgb(220, 223, 230);">
刷新
</button>
</div>
</div>
<!--<p style="font-size: 75px;color: white; text-align: center;margin: 0;padding: 0;">敬请期待</p>-->
<p>请使用<a href="#/appDownLoad">蘑菇丁</a>扫描二维码登录</p>
</div>
<a id="register" href="/Register" target="_blank">企业注册</a>
</div>
</el-col>
websocket相关js代码:
import header from '../../components/Header.vue'
import footer from '../../components/Footer.vue'
import 'element-ui/lib/theme-chalk/display.css'
import jQuery from 'jquery'
import { getUUID } from '@/utils'
import LocalStorageUtil from '@/utils/LocalStorageUtil'
import md5 from 'js-md5'
import AuthService from '@/service/AuthService'
export default {
components: {
"v-header": header,
"v-footer": footer
},
data() {
return {
config: {
disabled: false
},
captchaPath: '',
menuIndex: "2",
activeIndex: "2",
code: null,
websocket:null,
uuid:null,
token :null,
userName:null,
form: {
username: '',
password: '',
yzid:'',
captcha: ''
},
rules: {
username: [
{required: true, message: '请输入账号', trigger: 'blur'},
],
password: [
{required: true, message: '请输入密码', trigger: 'blur'},
{min: 6, message: '请输入密码', trigger: 'blur'}
],
verificationCode: [
{required: true, message: '请正确输入验证码', trigger: 'blur'}
]
}
}
},
created() { //进入到当前页面加载的方法
this.getCaptcha(); //获取验证码
this.websocet(); //连接websocket
},
methods: {
// 获取验证码
getCaptcha () {
this.form.yzid = getUUID()
this.captchaPath = this.$http.adornUrl(`/captcha.jpg?uuid=${this.form.yzid}`)
},
onSubmit(e) { //账号密码登录时候的验证
this.$refs["loginForm"].validate((valid) => {
if (valid) {
this.$http({
url: this.$http.adornUrl('/sys/login'),
method: 'post',
data: this.$http.adornData({
'username': this.form.username,
'password': md5(this.form.password),
'uuid': this.form.yzid,
'captcha': this.form.captcha
})
}).then(({data}) => {
if (data && data.code === 0) {
if (data.roleType=="enterprise") { // 如果是企业角色的话
LocalStorageUtil.clear();
LocalStorageUtil.set('authToken', data.token);
LocalStorageUtil.set('roleType', data.roleType);
LocalStorageUtil.set("username",data.userName);
LocalStorageUtil.set("auditStatus",data.auditStatus);
LocalStorageUtil.set("organizationCode",data.organizationCode);
LocalStorageUtil.set("organizationName",data.organizationName);
LocalStorageUtil.set("nickName",data.nickName);
LocalStorageUtil.set("avatar",data.avatar !="" ? data.avatar:'headDefault.png' );
}else{ // 如果是管理员、教师、学生的话
LocalStorageUtil.clear();
LocalStorageUtil.set('roleGroup', data.roleGroup);
LocalStorageUtil.set('roleType', data.roleType);
LocalStorageUtil.set('authToken', data.token);
LocalStorageUtil.set("username",data.userName);
LocalStorageUtil.set("nickName",data.nickName);
LocalStorageUtil.set("avatar",data.avatar !="" ? data.avatar:'headDefault.png');
LocalStorageUtil.set("Organization",JSON.stringify(data.Organization)); // 用户的组织机构
LocalStorageUtil.set("gpmsSchoolYear",data.gpmsSchoolYear); // 当前学年
}
this.$router.push('/home')
} else {
this.getCaptcha()
this.$message.error(data.msg)
this.form.captcha=''
}
})
}
});
},
handleSelect(key, keyPath) { //切换选项卡
this.menuIndex = key;
},
saoma(){ //自定义点击事件,点击扫码登录选项卡的时候
var uid = this.uuid;
document.getElementById("pic").src=this.$http.adornUrl('/auth/proCode?uuid='+uid);
},
shuaxin(e){ //二维码过期,点击刷新按钮的点击事件
document.getElementById("gone").style.display="none";
var uid = this.uuid;
document.getElementById("pic").src=this.$http.adornUrl("/auth/proCode?uuid="+uid);
},
websocet(){ // 主角儿websocket上场方法
var surl = this.$http.adornUrl('/websocket');
var wsurl = surl.replace("http", "ws");
let websocket = new WebSocket(wsurl);
// console.log(websocket)
websocket.onopen = () => {
// console.log('连接成功...');
// Web Socket 已连接上,使用 send() 方法发送数据
};
websocket.onmessage = (evt) => {
var obj = JSON.parse(eval('(' + evt.data + ')'));
if (obj.type=="connect") { //websocket连接成功的话,后台会向前端发送数据type为connet的标志位
this.uuid = obj.uid;
// console.log("唯一标识"+this.uuid)
}else if (obj.type=="overdue") { //生成二维码与唯一标识uuid,后台会向前端发送数据type为overdue的标志位
this.token = obj.uid;
// console.log(this.token);
//console.log("二维码过期倒计时")
setTimeout(() =>{ // 设置二维码过期时间
this.testqurtz();
websocket.close();
this.websocet();
},120000)
}else if(obj.type=="loginOk"&&obj.uid==this.token){ //如果websocket发送到前端的标志位是loginOk的话就表示二维码登录验证成功,允许登录
if (obj.roleType=="enterprise") { // 如果是企业角色的话
LocalStorageUtil.clear();
LocalStorageUtil.set('authToken', obj.authToken);
LocalStorageUtil.set('roleType', obj.roleType);
LocalStorageUtil.set("username",obj.userName);
LocalStorageUtil.set("auditStatus",obj.auditStatus);
LocalStorageUtil.set("organizationCode",obj.organizationCode);
LocalStorageUtil.set("organizationName",obj.organizationName);
LocalStorageUtil.set("nickName",obj.nickName);
LocalStorageUtil.set("avatar",obj.avatar !="" ? obj.avatar:'headDefault.png' );
}else { // 如果是管理员、教师、学生的话
LocalStorageUtil.clear();
LocalStorageUtil.set('roleGroup', obj.roleGroup);
LocalStorageUtil.set('roleType', obj.roleType);
LocalStorageUtil.set('authToken', obj.authToken);
LocalStorageUtil.set("username",obj.userName);
LocalStorageUtil.set("nickName",obj.nickName);
LocalStorageUtil.set("avatar",obj.avatar !="" ? obj.avatar:'headDefault.png');
LocalStorageUtil.set("Organization",obj.organization); // 用户的组织机构
LocalStorageUtil.set("gpmsSchoolYear",obj.gpmsSchoolYear); // 当前学年
}
this.$router.replace({ name: 'home' })
}
};
websocket.onclose = () => {
// 关闭 websocket
// console.log('连接已关闭...');
};
websocket.onerror = function(evt) {
// console.log('连接出现错误...');
};
// 路由跳转时结束websocket链接
this.$router.afterEach(function () {
websocket.close()
})
},
testqurtz() { //测试定时函数
//console.log('2分钟之后的函数。。。')
document.getElementById("gone").style.display="";
},
//你按回车键了
doLogin:function(ev){
if(ev.keyCode == 13){
this.onSubmit();
}
},
},
}
后台生成二维码图片代码(参考):
@Autowired
private MyWebSocket myWebSocket;
//存储二维码维一标识
public static Set<String> tokes = new HashSet<>();
//存储toke绑定的用户
public static Map<String,String> users = new HashMap<>();
/**
* @Author: Qzli
* @Description: 生成二维码
* @Date: Create by 2018/6/24 0027 17:20
*/
@GetMapping("/proCode")
@ApiOperation("生成二维码")
public String proCode() throws Exception{
HttpServletRequest request=((ServletRequestAttributes) RequestContextHolder.getRequestAttributes()).getRequest();
HttpServletResponse response=((ServletRequestAttributes)RequestContextHolder.getRequestAttributes()).getResponse();
System.out.println("生成二维码!");
//生成唯一ID
String uuid = request.getParameter("uuid");
tokes.add(uuid);
//二维码内容
String url = urlConfig.getAuthurl ();
String encoderContent=url+uuid+"?type=1";
System.out.println(encoderContent);
ByteArrayOutputStream jpegOutputStream = new ByteArrayOutputStream();
BufferedImage twoDimensionImg = new TwoDimensionCode().qRCodeCommon(encoderContent, "jpg", 12);
ImageIO.write(twoDimensionImg, "jpg", jpegOutputStream);
response.addHeader("Content-Disposition", "attachment;filename=" + new String((uuid+".jpg").getBytes()));
byte[] bys = jpegOutputStream.toByteArray();
response.addHeader("Content-Length", "" + bys.length);
ServletOutputStream responseOutputStream = response.getOutputStream();
response.setContentType("application/octet-stream");
responseOutputStream.write(bys);
responseOutputStream.flush();
responseOutputStream.close();
websocketModel wm = new websocketModel();
wm.setType("overdue");
wm.setUid(uuid);
myWebSocket.sendInfo(wm,uuid);//向前端发送消息
return null;
}
app回调后台的扫码登录接口(参考):
/**
* @Author: Qzli
* @Description: app扫码后将token(uuid)与用户绑定
* @Date: Create by 2018/6/24 0027 20:19
*/
@PostMapping("/qrcodelogin")
@ApiOperation("扫码登录")
public R qrcodelogin (@Valid UserDto.Applogin applogin) throws IOException {
HttpServletRequest request=((ServletRequestAttributes)RequestContextHolder.getRequestAttributes()).getRequest();
String jwttoken = request.getHeader("authenticationToken");
String pfid = applogin.getPfid(); //用户id
String uid = applogin.getUid(); //唯一标识
System.out.println ("扫码登录:"+uid);
if (jwtTokenUtil.validateToken(jwttoken)) { //验证token
for (String t : tokes) {
if (t.equals(uid)) {
MetaPfprofileEntity user = metaPfprofileService.selectById(pfid); //根据用户id查找用户信息
if (null == user) { //判断用户存不存在
throw new VerifyError(SystemErrorCode.LOGIN_FAILURE.getMessage());//账号或密码错误
}
users.put(uid, user.getUserName()); //存在的话讲用户的用户名存入Map集合
websocketModel wm = new websocketModel();
wm.setType("loginOk");
wm.setNickName (user.getNickname()); //用户昵称
wm.setUsername(user.getUserName());
wm.setAvatar (user.getHeadImage());
wm.setAuthToken(jwtTokenUtil.generateToken(user.getId()));
wm.setRoleType(user.getRoleType());
wm.setUid(uid);
System.out.println (user.getRoleType());
if (user.getRoleType().equals("enterprise")) { //如果是企业角色的话
wm.setOrganizationName(user.getOrganizationName());
wm.setOrganizationCode(user.getOrganizationCode());
GxyUserEnterpriseEntity usere = gxyUserEnterpriseService.selectOne(new EntityWrapper<GxyUserEnterpriseEntity>().eq ("ENTERPRISE_CODE",user.getOrganizationCode()));
if (usere!=null){
wm.setAuditStatus(usere.getGxyAuditStatus());
}
}
else if (user.getRoleType().equals("student")||user.getRoleType().equals("teacher")){
String url= urlConfig.getUrl()+"/customize/control/getUserDepCode";
Map<String, String> params = new HashMap<>();
params.put("currentId",user.getId());
params.put("pfid",user.getId());
params.put("roleType",user.getRoleType());
// 根据用户id获取用户的组织机构
String token = JWTUtil.sign(user.getId());
boolean s = JWTUtil.verify(token,user.getId()); //检验token
String json= HttpUtils.getInstance().sendHttpPost(url,params, token); //调取接口
String status = JSONObject.fromObject(json).getString ("status");
if (status.equals ("FALSE")){
throw new VerifyError(JSONObject.fromObject(json).getString ("message"));//
}
// 用户的组织机构
JSONObject myJsonObject = JSONObject.fromObject(JSONObject.fromObject(json).get("data"));
// 根据学院id,state=1(当前学年) 查找唯一的学年(数据字典的id)
SysConfigEntity sysConfigEntity = sysConfigService.selectOne(new EntityWrapper<SysConfigEntity>()
.eq("MODIFIED_BY",myJsonObject.get("schoolId"))
.eq("STATE","1")
.eq("PARAM_KEY","gpmsSchoolYearTypeDate"));
if (sysConfigEntity!=null){
wm.setGpmsSchoolYear (sysConfigEntity.getId());
}else {
wm.setGpmsSchoolYear (null);
}
wm.setOrganization (myJsonObject.toString ());
wm.setRoleGroup (myJsonObject.getString ("roleGroup"));
UserOrganizationDto udo = (UserOrganizationDto) JSONObject.toBean (myJsonObject, UserOrganizationDto.class);
String rg = StringUtils.join(udo.getRoleGroup(),",");
sysRoleRedis.saveOrUpdate(user.getId(),rg);
sysUserOrgRedis.saveOrUpdate (user.getId(),myJsonObject.toString ());
}
System.out.println (wm.toString ());
myWebSocket.sendInfo(wm,uid);//向前端发送登录成功的消息
UserDto.ResponseMessage responseMessage = new UserDto.ResponseMessage();
responseMessage.setRetCode(0);
responseMessage.setMsg("登录成功");
responseMessage.setData(new JSONObject ());
return R.ok().put("responseMessage",responseMessage); //返回到app后端
}
}
}
throw new VerifyError(SystemErrorCode.PERSON_AUTH_FAILURE.getMessage());//认证信息不一致
}
生成二维码的相关两个类(可co过去直接复用):
package com.zhangtao.common.twodimension;
import com.swetake.util.Qrcode;
import jp.sourceforge.qrcode.QRCodeDecoder;
import jp.sourceforge.qrcode.exception.DecodingFailedException;
import javax.imageio.ImageIO;
import java.awt.*;
import java.awt.image.BufferedImage;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
public class TwoDimensionCode {
/**
* 生成二维码(QRCode)图片
* @param content 存储内容
* @param imgPath 图片路径
*/
public void encoderQRCode(String content, String imgPath) {
this.encoderQRCode(content, imgPath, "png", 7);
}
/**
* 生成二维码(QRCode)图片
* @param content 存储内容
* @param output 输出流
*/
public void encoderQRCode(String content, OutputStream output) {
this.encoderQRCode(content, output, "png", 7);
}
/**
* 生成二维码(QRCode)图片
* @param content 存储内容
* @param imgPath 图片路径
* @param imgType 图片类型
*/
public void encoderQRCode(String content, String imgPath, String imgType) {
this.encoderQRCode(content, imgPath, imgType, 7);
}
/**
* 生成二维码(QRCode)图片
* @param content 存储内容
* @param output 输出流
* @param imgType 图片类型
*/
public void encoderQRCode(String content, OutputStream output, String imgType) {
this.encoderQRCode(content, output, imgType, 7);
}
/**
* 生成二维码(QRCode)图片
* @param content 存储内容
* @param imgPath 图片路径
* @param imgType 图片类型
* @param size 二维码尺寸
*/
public void encoderQRCode(String content, String imgPath, String imgType, int size) {
try {
BufferedImage bufImg = this.qRCodeCommon(content, imgType, size);
File imgFile = new File(imgPath);
// 生成二维码QRCode图片
ImageIO.write(bufImg, imgType, imgFile);
} catch (Exception e) {
e.printStackTrace();
}
}
/**
* 生成二维码(QRCode)图片
* @param content 存储内容
* @param output 输出流
* @param imgType 图片类型
* @param size 二维码尺寸
*/
public void encoderQRCode(String content, OutputStream output, String imgType, int size) {
try {
BufferedImage bufImg = this.qRCodeCommon(content, imgType, size);
// 生成二维码QRCode图片
ImageIO.write(bufImg, imgType, output);
} catch (Exception e) {
e.printStackTrace();
}
}
/**
* 生成二维码(QRCode)图片的公共方法
* @param content 存储内容
* @param imgType 图片类型
* @param size 二维码尺寸
* @return
*/
public BufferedImage qRCodeCommon(String content, String imgType, int size) {
BufferedImage bufImg = null;
try {
Qrcode qrcodeHandler = new Qrcode();
// 设置二维码排错率,可选L(7%)、M(15%)、Q(25%)、H(30%),排错率越高可存储的信息越少,但对二维码清晰度的要求越小
qrcodeHandler.setQrcodeErrorCorrect('M');
qrcodeHandler.setQrcodeEncodeMode('B');
// 设置设置二维码尺寸,取值范围1-40,值越大尺寸越大,可存储的信息越大
qrcodeHandler.setQrcodeVersion(size);
// 获得内容的字节数组,设置编码格式
byte[] contentBytes = content.getBytes("utf-8");
// 图片尺寸
int imgSize = 67 + 12 * (size - 1);
bufImg = new BufferedImage(imgSize, imgSize, BufferedImage.TYPE_INT_RGB);
Graphics2D gs = bufImg.createGraphics();
// 设置背景颜色
gs.setBackground(Color.white);
gs.clearRect(0, 0, imgSize, imgSize);
// 设定图像颜色> BLACK
gs.setColor(Color.BLACK);
// 设置偏移量,不设置可能导致解析出错
int pixoff = 2;
// 输出内容> 二维码
if (contentBytes.length > 0 && contentBytes.length < 800) {
boolean[][] codeOut = qrcodeHandler.calQrcode(contentBytes);
for (int i = 0; i < codeOut.length; i++) {
for (int j = 0; j < codeOut.length; j++) {
if (codeOut[j][i]) {
gs.fillRect(j * 3 + pixoff, i * 3 + pixoff, 3, 3);
}
}
}
} else {
throw new Exception("QRCode content bytes length = " + contentBytes.length + " not in [0, 800].");
}
gs.dispose();
bufImg.flush();
} catch (Exception e) {
e.printStackTrace();
}
return bufImg;
}
/**
* 解析二维码(QRCode)
* @param imgPath 图片路径
* @return
*/
public String decoderQRCode(String imgPath) {
// QRCode 二维码图片的文件
File imageFile = new File(imgPath);
BufferedImage bufImg = null;
String content = null;
try {
bufImg = ImageIO.read(imageFile);
QRCodeDecoder decoder = new QRCodeDecoder();
content = new String(decoder.decode(new TwoDimensionCodeImage(bufImg)), "utf-8");
} catch (IOException e) {
System.out.println("Error: " + e.getMessage());
e.printStackTrace();
} catch (DecodingFailedException dfe) {
System.out.println("Error: " + dfe.getMessage());
dfe.printStackTrace();
}
return content;
}
/**
* 解析二维码(QRCode)
* @param input 输入流
* @return
*/
public String decoderQRCode(InputStream input) {
BufferedImage bufImg = null;
String content = null;
try {
bufImg = ImageIO.read(input);
QRCodeDecoder decoder = new QRCodeDecoder();
content = new String(decoder.decode(new TwoDimensionCodeImage(bufImg)), "utf-8");
} catch (IOException e) {
System.out.println("Error: " + e.getMessage());
e.printStackTrace();
} catch (DecodingFailedException dfe) {
System.out.println("Error: " + dfe.getMessage());
dfe.printStackTrace();
}
return content;
}
public static void main(String[] args) {
/*
String imgPath = "G:/twoDim/Michael_QRCode.png";
// String encoderContent = "Hello 大大、小小,welcome to QRCode!" + "\nMyblog [ http://sjsky.iteye.com ]" + "\nEMail [ [email protected] ]";
String encoderContent = "http://localhost:8080/";
TwoDimensionCode handler = new TwoDimensionCode();
handler.encoderQRCode(encoderContent, imgPath, "png");
// try {
// OutputStream output = new FileOutputStream(imgPath);
// handler.encoderQRCode(content, output);
// } catch (Exception e) {
// e.printStackTrace();
// }
System.out.println("========encoder success");
String decoderContent = handler.decoderQRCode(imgPath);
System.out.println("解析结果如下:");
System.out.println(decoderContent);
System.out.println("========decoder success!!!");
*/
}
}
package com.zhangtao.common.twodimension;
import jp.sourceforge.qrcode.data.QRCodeImage;
import java.awt.image.BufferedImage;
public class TwoDimensionCodeImage implements QRCodeImage {
BufferedImage bufImg;
public TwoDimensionCodeImage(BufferedImage bufImg) {
this.bufImg = bufImg;
}
@Override
public int getHeight() {
return bufImg.getHeight();
}
@Override
public int getPixel(int x, int y) {
return bufImg.getRGB(x, y);
}
@Override
public int getWidth() {
return bufImg.getWidth();
}
}
最终的的是websocket的几个相关类:myWebsocke、myWebsocketConfig、websocketModel
package com.zhangtao.common.websocket;
import com.zhangtao.common.utils.JsonUtil;
import org.springframework.stereotype.Component;
import javax.websocket.*;
import javax.websocket.server.ServerEndpoint;
import java.io.IOException;
import java.util.HashMap;
import java.util.Map;
import java.util.UUID;
import java.util.concurrent.CopyOnWriteArraySet;
/**
* @Author: Qzli
* @Description:
* @Date: Create by 2018/6/25 0023 10:54
*/
@ServerEndpoint("/websocket")
@Component
public class MyWebSocket {
//静态变量,用来记录当前在线连接数。应该把它设计成线程安全的。
private static int onlineCount = 0;
//concurrent包的线程安全Set,用来存放每个客户端对应的MyWebSocket对象。
private static CopyOnWriteArraySet<MyWebSocket> webSocketSet = new CopyOnWriteArraySet<MyWebSocket>();
//存储二维码维一标识
public static Map<String,Object> sessions = new HashMap<>();
//与某个客户端的连接会话,需要通过它来给客户端发送数据
private Session session;
/**
* 连接建立成功调用的方法*/
@OnOpen
public void onOpen(Session session)throws Exception {
System.out.println("连接成功;");
this.session = session;
//生成唯一ID
String uuid = String.valueOf(UUID.randomUUID());
sessions.put(uuid,this.session); //把唯一标识跟客户端绑定
webSocketSet.add(this); //加入set中
addOnlineCount(); //在线数加1
System.out.println("有新连接加入!当前在线人数为" + getOnlineCount());
try {
// Thread.sleep(60000);
websocketModel wm = new websocketModel();
wm.setType("connect");
wm.setUid(uuid);
sendInfo(wm,uuid);
} catch (IOException e) {
System.out.println("IO异常");
}
}
/**
* 连接关闭调用的方法
*/
@OnClose
public void onClose() {
webSocketSet.remove(this); //从set中删除
subOnlineCount(); //在线数减1
System.out.println("有一连接关闭!当前在线人数为" + getOnlineCount());
System.out.println(webSocketSet);
}
/**
* 收到客户端消息后调用的方法
*
* @param message 客户端发送过来的消息*/
@OnMessage
public void onMessage(String message, Session session) {
System.out.println("来自客户端的消息:" + message);
try {
session.getBasicRemote().sendText(message);
} catch (IOException e) {
e.printStackTrace();
}
}
/**
* 发生错误时调用
*
* @param session
* @param error
*/
@OnError
public void onError(Session session, Throwable error) {
System.out.println("发生错误");
error.printStackTrace();
}
public void sendMessage(String message,Session session) throws IOException {
System.out.println(session);
session.getBasicRemote().sendText(JsonUtil.toJson(message));
//this.session.getAsyncRemote().sendText(message);
}
/** Qzli 2018/06/25
* 自定义发送消息(解决广播式发送消息的问题)
* */
public void sendInfo(Object message,String uid) throws IOException {
System.out.println(webSocketSet);
try {
sendMessage(JsonUtil.toJson(message),(Session)sessions.get(uid));
} catch (IOException e) {
e.printStackTrace();
}
}
public static synchronized int getOnlineCount() {
return onlineCount;
}
public static synchronized void addOnlineCount() {
MyWebSocket.onlineCount++;
}
public static synchronized void subOnlineCount() {
MyWebSocket.onlineCount--;
}
}
package com.zhangtao.common.websocket;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.socket.server.standard.ServerEndpointExporter;
/**
* @Author: Qzli
* @Description:
* @Date: Create by 2018/6/25 0025 15:01
* @Nodify By:
*/
@Configuration
public class myWebSocketConfig {
@Bean
public ServerEndpointExporter serverEndpointExporter() {
return new ServerEndpointExporter();
}
}
package com.zhangtao.common.websocket;
import lombok.Data;
/**
* @Author: Qzli
* @Description:
* @Date: Create by 2018/6/27 0027 9:13
* @Nodify By:
*/
@Data
public class websocketModel {
//想前端发送哪些消息的实体类
private String type;
private String username;
private String nickName;
private String password;
private String avatar;
private String uid;
private String authToken;
private String roleType;
private String organizationName;
private String organizationCode;
private String auditStatus;
private String gpmsSchoolYear;
private String organization;
private String roleGroup;
}
至此所有业务逻辑代码已全部上演完毕,请检阅。如有不懂可私信或留言,如有不良或错误请指正,如有更好的方法可共同探讨改进,相互学习!