SpringBoot整合Socket实战案例,实现单点群发,
作者:小目标青年
来源:https:blog。csdn。netqq35387940本篇内容:
后端前端简单HTML页面功能场景点:群发,所有人都能收到局部群发,部分人群都能收到单点推送,指定某个人的页面
惯例,先看看本次实战示例项目结构:
可以看到内容不多,也就是说,springboot整合socket,跟着我学,轻轻松松。
不多说,开始:pom引入核心依赖dependenciesdependencygroupIdcom。alibabagroupIdfastjsonartifactIdversion1。2。75versiondependencydependencygroupIdcom。corundumstudio。socketiogroupIdnettysocketioartifactIdversion1。7。7versiondependencydependencygroupIdorg。springframework。bootgroupIdspringbootstarterwebartifactIddependencydependencygroupIdorg。springframework。bootgroupIdspringbootstartertestartifactIdscopetestscopedependencydependenciesyml加上配置项server:port:8089socketio:host:localhostport:8503maxFramePayloadLength:1048576maxHttpContentLength:1048576bossCount:1workCount:100allowCustomRequests:trueupgradeTimeout:10000pingTimeout:60000pingInterval:25000创建socket配置加载类MySocketConfig。javaimportcom。corundumstudio。socketio。SocketConfig;importcom。corundumstudio。socketio。SocketIOServer;importcom。corundumstudio。socketio。annotation。SpringAnnotationScanner;importorg。springframework。beans。factory。annotation。Value;importorg。springframework。context。annotation。Bean;importorg。springframework。context。annotation。Configuration;Author:JCcccDescription:Date:2022061321:50ConfigurationpublicclassMySocketConfig{Value({socketio。host})privateStringhost;Value({socketio。port})privateIntegerport;Value({socketio。bossCount})privateintbossCount;Value({socketio。workCount})privateintworkCount;Value({socketio。allowCustomRequests})privatebooleanallowCustomRequests;Value({socketio。upgradeTimeout})privateintupgradeTimeout;Value({socketio。pingTimeout})privateintpingTimeout;Value({socketio。pingInterval})privateintpingInterval;BeanpublicSocketIOServersocketIOServer(){SocketConfigsocketConfignewSocketConfig();socketConfig。setTcpNoDelay(true);socketConfig。setSoLinger(0);com。corundumstudio。socketio。Configurationconfignewcom。corundumstudio。socketio。Configuration();buildSocketConfig(socketConfig,config);returnnewSocketIOServer(config);}扫描nettysocketIo的注解(OnConnect、OnEvent等)BeanpublicSpringAnnotationScannerspringAnnotationScanner(){returnnewSpringAnnotationScanner(socketIOServer());}privatevoidbuildSocketConfig(SocketConfigsocketConfig,com。corundumstudio。socketio。Configurationconfig){config。setSocketConfig(socketConfig);config。setHostname(host);config。setPort(port);config。setBossThreads(bossCount);config。setWorkerThreads(workCount);config。setAllowCustomRequests(allowCustomRequests);config。setUpgradeTimeout(upgradeTimeout);config。setPingTimeout(pingTimeout);config。setPingInterval(pingInterval);}}创建消息实体MyMessage。javaAuthor:JCcccDate:202207239:05Description:publicclassMyMessage{privateStringtype;privateStringcontent;privateStringfrom;privateStringto;privateStringchannel;publicStringgetType(){returntype;}publicvoidsetType(Stringtype){this。typetype;}publicStringgetContent(){returncontent;}publicvoidsetContent(Stringcontent){this。contentcontent;}publicStringgetFrom(){returnfrom;}publicvoidsetFrom(Stringfrom){this。fromfrom;}publicStringgetTo(){returnto;}publicvoidsetTo(Stringto){this。toto;}publicStringgetChannel(){returnchannel;}publicvoidsetChannel(Stringchannel){this。channelchannel;}}
代码简析:
创建sockethandler负责记录客户端连接、下线
MySocketHandler。javaimportcom。corundumstudio。socketio。SocketIOClient;importcom。corundumstudio。socketio。SocketIOServer;importcom。corundumstudio。socketio。annotation。OnConnect;importcom。corundumstudio。socketio。annotation。OnDisconnect;importcom。socket。mysocket。util。SocketUtil;importorg。springframework。beans。factory。annotation。Autowired;importjavax。annotation。PostConstruct;importjavax。annotation。PreDestroy;importorg。slf4j。Logger;importorg。slf4j。LoggerFactory;importorg。springframework。stereotype。Component;Author:JCcccDescription:Date:202262321:21ComponentpublicclassMySocketHandler{privatefinalLoggerlogLoggerFactory。getLogger(this。getClass());AutowiredprivateSocketIOServersocketIoServer;PostConstructprivatevoidstart(){try{socketIoServer。start();}catch(Exceptione){e。printStackTrace();}}PreDestroyprivatevoiddestroy(){try{socketIoServer。stop();}catch(Exceptione){e。printStackTrace();}}OnConnectpublicvoidconnect(SocketIOClientclient){StringuserFlagclient。getHandshakeData()。getSingleUrlParam(userFlag);SocketUtil。connectMap。put(userFlag,client);log。info(客户端userFlag:userFlag已连接);}OnDisconnectpublicvoidonDisconnect(SocketIOClientclient){StringuserFlagclient。getHandshakeData()。getSingleUrlParam(userFlag);log。info(客户端userFlag:userFlag断开连接);SocketUtil。connectMap。remove(userFlag,client);}}
代码简析:
封装的socket小函数
SocketUtil。javaimportcom。alibaba。fastjson。JSON;importcom。alibaba。fastjson。JSONObject;importcom。corundumstudio。socketio。AckRequest;importcom。corundumstudio。socketio。SocketIOClient;importcom。corundumstudio。socketio。annotation。OnEvent;importorg。slf4j。Logger;importorg。slf4j。LoggerFactory;importorg。springframework。beans。factory。annotation。Autowired;importorg。springframework。stereotype。Component;importorg。springframework。util。StringUtils;importjava。util。Map;importjava。util。Objects;importjava。util。concurrent。ConcurrentHashMap;importjava。util。concurrent。ConcurrentMap;Author:JCcccDescription:Date:202262321:28ComponentpublicclassSocketUtil{privatefinalLoggerlogLoggerFactory。getLogger(this。getClass());暂且把用户客户端信息存在缓存publicstaticConcurrentMapString,SocketIOClientconnectMapnewConcurrentHashMap();OnEvent(valueCHANNELSYSTEM)publicvoidsystemDataListener(StringreceiveMsg){if(!StringUtils。hasLength(receiveMsg)){return;}JSONObjectmsgObject(JSONObject)JSON。parse(receiveMsg);StringuserFlagString。valueOf(msgObject。get(from));StringcontentString。valueOf(msgObject。get(content));log。info(收到用户:{}推送到系统频道的一条消息:{},userFlag,content);}publicvoidsendToAll(MapString,Objectmsg,StringsendChannel){if(connectMap。isEmpty()){return;}给在这个频道的每个客户端发消息for(Map。EntryString,SocketIOCliententry:connectMap。entrySet()){entry。getValue()。sendEvent(sendChannel,msg);}}publicvoidsendToOne(StringuserFlag,MapString,Objectmsg,StringsendChannel){拿出某个客户端信息SocketIOClientsocketClientgetSocketClient(userFlag);if(Objects。nonNull(socketClient)){单独给他发消息socketClient。sendEvent(sendChannel,msg);}}识别出客户端paramuserFlagreturnpublicSocketIOClientgetSocketClient(StringuserFlag){SocketIOClientclientnull;if(StringUtils。hasLength(userFlag)!connectMap。isEmpty()){for(Stringkey:connectMap。keySet()){if(userFlag。equals(key)){clientconnectMap。get(key);}}}returnclient;}}
代码简析:
写1个接口,模拟场景,前端页面调用后端接口,做消息推送
TestController。javaimportcom。socket。mysocket。dto。MyMessage;importcom。socket。mysocket。util。SocketUtil;importorg。springframework。beans。factory。annotation。Autowired;importorg。springframework。web。bind。annotation。;importjava。util。HashMap;importjava。util。Map;Author:JCcccDescription:Date:2022061321:50RestControllerpublicclassTestController{publicfinalstaticStringSENDTYPEALLALL;publicfinalstaticStringSENDTYPEALONEALONE;AutowiredSocketUtilsocketUtil;PostMapping(testSendMsg)publicStringtestSendMsg(RequestBodyMyMessagemyMessage){MapString,ObjectmapnewHashMap();map。put(msg,myMessage。getContent());群发if(SENDTYPEALL。equals(myMessage。getType())){socketUtil。sendToAll(map,myMessage。getChannel());returnsuccess;}指定单人if(SENDTYPEALONE。equals(myMessage。getType())){socketUtil。sendToOne(myMessage。getTo(),map,myMessage。getChannel());returnsuccess;}returnfail;}}
代码简析:
好了,7步了。一切已经就绪了。前端简单页面
接下来搞点前端HTML页面,玩一玩看看效果:
第一个页面:
TestClientStudentJC。html!DOCTYPEhtmlhtmlheadmetahttpequivContentTypecontenttexthtml;charsetutf8title我要连SOCKETtitlebasestylebody{padding:20px;}console{height:450px;overflow:auto;}。msgcolor{color:green;}styleheadbodylabal给系统推消息labalinputtypetextidcontentbuttonidbtnSendToSystemonclicksendSys();发送buttonbodyhtml
代码简析:
第二个页面,跟第一个基本一样,改一下用户唯一标识:
TestClientStudentPU。html!DOCTYPEhtmlhtmlheadmetahttpequivContentTypecontenttexthtml;charsetutf8title我要连SOCKETtitlebasestylebody{padding:20px;}console{height:450px;overflow:auto;}。msgcolor{color:green;}styleheadbodylabal给系统推消息labalinputtypetextidcontentbuttonidbtnSendToSystemonclicksendSys();发送buttonbodyhtml
OK,把项目跑起来,开始玩。
直接访问客户端页面模拟学生JC连接socket:
http:127。0。0。1:8089TestClientStudentJC。html
可以看到服务端有监测到:
这里监测的:
先试试客户端给系统推消息先:
可以看到服务端成功收到消息:
这种方式,其实是因为服务监听了相关的频道:
前端使用JS推到这个系统频道:
ps:其实前端给服务端推消息,其实调用接口就可以。
OK,进入核心应用场景1:群发,所有人都能收到
系统给连上的客户端都推送消息
{type:ALL,content:你们好,这是一条广播消息,全部人都能收到,channel:CHANNELSYSTEM}
看看效果:
然后是场景2,局部群发,部分人群都能收到
其实也就是通过HTML客户端监听主题做区分就好:
直接拉人口,升3:
模拟2个学生,1个老师都连接上了socket
当然,老师监听的是老师频道:
然后我们模拟推送一下消息到指定的老师频道:
{
type:ALL,
content:给老师们推一条消息!!!,
channel:CHANNELTEACHER
}
最后一个场景,也就是单点推送,指定某个人收到
模拟学生PU给学生JC推消息:
可以看到在学生频道的JC正常收到了PU的消息:
好了,该篇就到这吧。
为什么感觉越穷的家庭越不重视教育?1不是不重视,是没钱供孩子上学。2教育最后拼的是金钱是父母的格局。3教育很重要,谁都知道,但高等教育是需要钱的,越向上学,钱需要的越多。4初中三年拼中考,一半的学生上不了高中,拼的
想不通,任天堂掌机贵在哪?有那钱买台手机不香吗?题主的问题描述这么说,说明你并没有理解游戏的本质,老山内说过一句话,游戏的本质就是好玩,好玩这个属性和画面和游戏是否大型真的没有什么直接关联,任天堂的掌机能独树一帜,肯定有它的特点
拿到教师资格证,距离教师的编制还有多遥远?准确地说,拿到教师资格证,就相当于高三学生拿到了准考证,有了参加考编的资格。有的高三学生经过三天考试,一下子考上自己满意的学校。而有的学生却一而再,再而三地与自己想考的学校失之交臂
科学家怎么知道光线是从多少光年外的星体上发出的?科学家怎么知道光线是从多少光年外的星体上发出的?看了网上一些帖子和评论,有这种想法的网友很多,他们根本不相信那些遥远的星光来自几十几百甚至几十亿上百亿光年以外,认为科学家们也是随意
如何规划从西安去甘南九寨沟十天游的线路?我刚回来,我们走的路线是西安陇南九寨沟川主寺唐克朗木寺扎尕那夏河兰州西安。一路走来风景很美,不足之处就是应该直接从夏河到天水,再到西安,不用去兰州,因为兰州真的没啥好玩的,而且外地
为什么飞船去到火星这么远的距离我们人类还能遥控它?是什么原理?如果说这个和小朋友玩的遥控玩具汽车一样也不要奇怪,对于美国推进实验室的勇气号和机遇号火星探测器来说无线电是对其进行遥控的主要方式。看看我们购买的民用无人机产品,其设置中也包含自动返
为什么科学家费曼会说,世界上没有人真的懂得量子力学?因为我们至今都只能描述其现象,而无法了解其机制。连事物的成因都不明白,能说懂吗?而量子就是这么一群,我们还无法确定具体成因的事物,这就是费曼的原意。首先,没人懂量子。虽然1900年
考电工证难度大吗?低压的有前途,还是高压的有前途?从事电力工作的小鲁班这几天正好在培训电工证,我来说说我的看法考电工难度大不大要从报考条件及考试内容两个方面来看。一电工证报考条件及资料要求。根据前几天的电工考试培训,培训的老师讲了
怎样改掉小孩写作业慢的问题?孩子写作业慢可能有几方面原因1磨蹭孩子本身想去干别的,自己不愿意写作业,但是无奈家长催着写作业,万般无奈之下挪到书桌前,实际上还是身到心不到。这样的状态写作业效率很低,一般都是东摸
联想激起的民愤,究竟是因为什么?联想为什么激起民愤?我想这不是偶然的,是多方面的甚至是深层次的原因引起的民愤,并不是某些简单的问题。笔者根据网上网民有根有据的爆料,个人认为,主要在于这两个方面引起民众的共愤。第一
如何平衡美术教学中偏操作弱文化修养的问题?如何平衡美术教学中偏操作弱文化修养的问题?这个问题对我来讲,是个门外汉。我只能画一些简单的简笔画。偶尔,教孩子画简笔画蜡笔画,我也是依葫芦画瓢。以我的认知学美术的人,一定是爱好,并