Warning: error_log(/data/www/wwwroot/hmttv.cn/caches/error_log.php): failed to open stream: Permission denied in /data/www/wwwroot/hmttv.cn/phpcms/libs/functions/global.func.php on line 537 Warning: error_log(/data/www/wwwroot/hmttv.cn/caches/error_log.php): failed to open stream: Permission denied in /data/www/wwwroot/hmttv.cn/phpcms/libs/functions/global.func.php on line 537 日本免费看视频,精品在线一区二区,aaa级精品久久久国产片

          整合營銷服務商

          電腦端+手機端+微信端=數據同步管理

          免費咨詢熱線:

          基于 SpringBoot + Vue 框架開發的網頁版聊天室項目(有視頻教程)

          依是一套全部開源的快速開發平臺,毫無保留給個人及企業免費使用。

          • 前端采用Vue、Element UI。
          • 后端采用Spring Boot、Spring Security、Redis & Jwt。
          • 權限認證使用Jwt,支持多終端認證系統。
          • 支持加載動態權限菜單,多方式輕松權限控制。
          • 高效率開發,使用代碼生成器可以一鍵生成前后端代碼。

          內置功能

          • 用戶管理:用戶是系統操作者,該功能主要完成系統用戶配置。
          • 部門管理: 配置系統組織機構(公司、部門、小組),樹結構展現支持數據權限。關注Java項目分享
          • 崗位管理: 配置系統用戶所屬擔任職務。
          • 菜單管理: 配置系統菜單,操作權限,按鈕權限標識等。
          • 角色管理: 角色菜單權限分配、設置角色按機構進行數據范圍權限劃分。
          • 字典管理: 對系統中經常使用的一些較為固定的數據進行維護。
          • 參數管理: 對系統動態配置常用參數。
          • 通知公告: 系統通知公告信息發布維護。
          • 操作日志: 系統正常操作日志記錄和查詢;系統異常信息日志記錄和查詢。
          • 登錄日志: 系統登錄日志記錄查詢包含登錄異常。
          • 在線用戶: 當前系統中活躍用戶狀態監控。
          • 定時任務: 在線(添加、修改、刪除)任務調度包含執行結果日志。
          • 代碼生成: 前后端代碼的生成(java、html、xml、sql)支持CRUD下載 。
          • 系統接口: 根據業務代碼自動生成相關的api接口文檔。
          • 服務監控: 監視當前系統CPU、內存、磁盤、堆棧等相關信息。
          • 緩存監控: 對系統的緩存信息查詢,命令統計等。
          • 在線構建器: 拖動表單元素生成相應的HTML代碼。
          • 連接池監視: 監視當前系統數據庫連接池狀態,可進行分析SQL找出系統性能瓶頸。

          項目介紹

          微言聊天室是基于前后端分離,采用SpringBoot+Vue框架開發的網頁版聊天室。使用了Spring Security安全框架進行密碼的加密存儲和登錄登出等邏輯的處理,以WebSocket+Socket.js+Stomp.js實現消息的發送與接收,監聽。搭建FastDFS文件服務器用于保存圖片,使用EasyExcel導出數據,使用Vue.js結合Element UI進行顯示彈窗和數據表格分頁等功能,以及整個系統的界面進行UI設計,并且使用MyBatis結合數據庫MySQL進行開發。最后使用了Nginx進行部署前后端分離項目。

          功能實現:群聊,單聊,郵件發送,emoji表情發送,圖片發送,用戶管理,群聊記錄管理,Excel的導出。關注Java項目分享

          項目技術棧

          后端技術棧

          1. Spring Boot
          2. Spring Security
          3. MyBatis
          4. MySQL
          5. WebSocket
          6. RabbitMQ
          7. Redis

          前端技術棧

          1. Vue
          2. ElementUI
          3. axios
          4. vue-router
          5. Vuex
          6. WebSocket
          7. vue-cli4 ...

          項目預覽圖

          客戶端界面-群聊主界面

          客戶端界面-私聊界面

          管理端界面-用戶管理

          管理端界面-群聊消息管理

          部署流程

          1. clone 項目到本地
          2. 在本地 MySQL 中創建一個空的數據庫 subtlechat,在該數據庫中運行提供的數據庫腳本subtlechat.sql,完成表的創建和數據的導入。
          3. 提前準備好Redis,在項目中的mail模塊的 application.yml 文件中,將 Redis 配置改為自己的。關注Java項目分享
          4. 提前準備好RabbitMQ,在項目中的mail模塊的 application.yml 文件中和web模塊中的 application-dev.properties,將 RabbitMQ 的配置改為自己的。
          5. 注冊郵箱的授權碼,在項目中的mail模塊的 application.yml 文件中填入

          1. 搭建fastdfs服務器,fastdfs-client.properties文件改成自己的。
          2. 在 IntelliJ IDEA 中打開subtlechat項目,先啟動 mail模塊,再啟動web模塊。
          3. 啟動vue項目。

          源碼和操作視頻

          點贊轉發本文后私信【0724】四個數字即可獲取前后端代碼和操作教學視頻

          前端代碼

          后端代碼

          操作視頻

          文由作者“大白菜”分享,有較多修訂和改動。注意:本系列是給IM初學者的文章,IM老油條們還望海涵,勿噴!

          1、引言

          接上兩篇《IM系統設計篇》、《編碼實踐篇(單聊功能)》,本篇主要講解的是通過實戰編碼實現IM的群聊功能,內容涉及群聊技術實現原理、編碼實踐等知識。

          2、寫在前面

          建議你在閱讀本文之前,務必先讀本系列的前兩篇《IM系統設計篇》、《編碼實踐篇(單聊功能)》,在著重理解IM系統的理論設計思路之后,再來閱讀實戰代碼則效果更好。

          最后,在開始本文之前,請您務必提前了解Netty的相關基礎知識,可從本系列首篇《IM系統設計篇》中的“知識準備”一章開始。

          3、系列文章

          本文是系列文章的第3篇,以下是系列目錄:

          • 《基于Netty,從零開發IM(一):IM系統設計篇》
          • 《基于Netty,從零開發IM(二):編碼實踐篇(單聊功能)》
          • 《基于Netty,從零開發IM(三):編碼實踐篇(群聊功能)》(* 本文)
          • 《基于Netty,從零開發IM(四):編碼實踐篇(系統優化)》(稍后發布.. )

          4、本篇概述

          在上篇《編碼實踐篇(單聊功能)》中,我們主要實現了IM的單聊功能,本節主要是實現IM群聊功能。

          本篇涉及的群聊核心功能,大致如下所示:

          • 1)登錄:每個客戶端連接服務端的時候,都需要輸入自己的賬號信息,以便和連接通道進行綁定;
          • 2)創建群組:輸入群組 ID 和群組名稱進行創建群組。需要先根據群組 ID 進行校驗,判斷是否已經存在了;
          • 3)查看群組:查看目前已經創建的群組列表;
          • 4)加入群組:主要參數是群組 ID 和用戶 ID,用戶 ID 只需從 Channel 的綁定屬性里面獲取即。主要是判斷群組 ID 是否存在,如果存在還需要判斷該用戶 ID 是否已經在群組里面了;
          • 5)退出群組:主要是判斷群組 ID 是否存在,如果存在則刪除相應的關系;
          • 6)查看組成員:根據群組 ID 去查詢對應的成員列表;
          • 7)群發消息:選擇某個群進行消息發送,該群下的成員都能收到信息。主要判斷群組 ID 是否存在,如果存在再去獲取其對應的成員列表。

          5、群聊原理

          其實群聊和單聊,整體上原理是一樣的,只是做了一下細節上的升級。

          在首篇《IM系統設計篇》的“6、IM群聊思路設計”設計部分也做了詳細的說明了。

          群聊的大概流程就是:根據群組 ID 查找到所有的成員集合,然后再遍歷找到每個成員對應的連接通道。

          具體的群聊架構思路如下圖:

          • 1)群聊和單聊整體上的思路一致:需要保存每個用戶和通道的對應關系,方便后期通過用戶 ID 去查找到對應的通道,再跟進通道推送消息;
          • 2)群聊把消息發送給群員的原理:其實很簡單,服務端再保存另外一份映射關系,那就是聊天室和成員的映射關系。發送消息時,首先根據聊天室 ID 找到對應的所有成員,然后再跟進各個成員的 ID 去查找到對應的通道,最后由每個通道進行消息的發送;
          • 3)群成員加入某個群聊聊的時候:往映射表新增一條記錄,如果成員退群的時候則刪除對應的映射記錄。

          6、運行效果

          補充說明:因為本系列文章主要目的是引導IM初學者在基于Netty的情況下,如何一步一步從零寫出IM的邏輯和思維能力,因而為了簡化編碼實現,本篇中編碼實現的客戶端都是基于控制臺實現的(希望不要被嫌棄),因為理解技術的本質顯然比炫酷的外在表現形式更為重要。

          用戶登錄效果圖:

          7、實體定義實戰

          7.1 服務端實體

          服務端映射關系的管理,分別是:

          • 1)登錄信息(用戶 ID 和通道);
          • 2)群組信息(群組 ID 和群組成員關系)。

          主要通過兩個 Map 去維護,具體如下:

          public class ServerChatGroupHandler extends ChannelInboundHandlerAdapter {

          private static Map<Integer, Channel> map=new HashMap<Integer, Channel>();

          private static Map<Integer, Group> groups=new HashMap<Integer, Group>();

          }

          //組和成員列表關系實體

          @Data

          public class Group implements Serializable {

          private String groupName;

          private List<GroupMember> members=new ArrayList<GroupMember>();

          }

          //成員和連接通道的關系實體

          public class GroupMember implements Serializable {

          private Integer userid;

          private Channel channel;

          }

          7.2 實體和指令關系

          我們準備好相應的實體,以及實體和指令的映射關系,具體如下所示:

          private static Map<Byte, Class<? extends BaseBean>> map=new HashMap<Byte,Class<? extends BaseBean>>();

          static{

          //登錄的請求和響應實體

          map.put(1, LoginReqBean.class);

          map.put(2, LoginResBean.class);

          //創建群組的請求和響應實體

          map.put(3, GroupCreateReqBean.class);

          map.put(4, GroupCreateResBean.class);

          //查看群組的請求和響應實體

          map.put(5, GroupListReqBean.class);

          map.put(6, GroupListResBean.class);

          //加入群組的請求和響應實體

          map.put(7,GroupAddReqBean.class);

          map.put(8,GroupAddResBean.class);

          //退出群組的請求和響應實體

          map.put(9,GroupQuitReqBean.class);

          map.put(10,GroupQuitResBean.class);

          //查看成員列表的請求和響應實體

          map.put(11,GroupMemberReqBean.class);

          map.put(12,GroupMemberResBean.class);

          //發送響應的實體(發送消息、發送響應、接受消息)

          map.put(13,GroupSendMsgReqBean.class);

          map.put(14,GroupSendMsgResBean.class);

          map.put(15,GroupRecMsgBean.class);

          }

          通過下面這張圖,能看的更清晰一些:

          8、Handler定義實戰

          IM群聊功能的實現,我們需要兩個兩個業務 Handler:

          • 1)分別是客戶端(ClientChatGroupHandler);
          • 2)服務端(ServerChatGroupHandler)。

          8.1 客戶端 Handler

          客戶端 Handler,主要是通過判斷實體類型來做不同的業務操作,當然也可以使用 SimpleChannelInboundHandler 去進行 Handler 拆分。

          public class ClientChatGroupHandler extends ChannelInboundHandlerAdapter {

          @Override

          public void channelActive(ChannelHandlerContext ctx) throws Exception {

          //在鏈接就緒時登錄

          login(ctx.channel());

          }

          //主要是“接受服務端”的響應信息

          @Override

          public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {

          if(msg instanceof LoginResBean){

          LoginResBean res=(LoginResBean) msg;

          System.out.println("登錄響應:"+res.getMsg());

          if(res.getStatus()==0){

          //登錄成功

          //1.給通道綁定身份

          ctx.channel().attr(AttributeKey.valueOf("userid")).set(res.getUserid());

          //2.顯示操作類型【請看下面】

          deal(ctx.channel());

          }else{

          //登錄失敗,繼續登錄

          login(ctx.channel());

          }

          }else if(msg instanceof GroupCreateResBean){

          GroupCreateResBean res=(GroupCreateResBean)msg;

          System.out.println("創建響應群組:"+res.getMsg());

          }else if(msg instanceofGroupListResBean){

          GroupListResBean res=(GroupListResBean)msg;

          System.out.println("查看群組列表:"+res.getLists());

          }elseif(msg instanceofGroupAddResBean){

          GroupAddResBean res=(GroupAddResBean)msg;

          System.out.println("加入群組響應:"+res.getMsg());

          }elseif(msg instanceof GroupQuitResBean){

          GroupQuitResBean res=(GroupQuitResBean)msg;

          System.out.println("退群群組響應:"+res.getMsg());

          }else if(msg instanceof GroupMemberResBean){

          GroupMemberResBean res=(GroupMemberResBean)msg;

          if(res.getCode()==1){

          System.out.println("查看成員列表:"+res.getMsg());

          }else{

          System.out.println("查看成員列表:"+res.getLists());

          }

          }else if(msg instanceof GroupSendMsgResBean){

          GroupSendMsgResBean res=(GroupSendMsgResBean)msg;

          System.out.println("群發消息響應:"+res.getMsg());

          }else if(msg instanceof GroupRecMsgBean){

          GroupRecMsgBean res=(GroupRecMsgBean)msg;

          System.out.println("收到消息fromuserid="+

          res.getFromuserid()+

          ",msg="+res.getMsg());

          }

          }

          }

          通過子線程循環向輸出控制臺輸出操作類型的方法,以下方法目前都是空方法,下面將詳細講解。

          private void deal(final Channel channel){

          final Scanner scanner=new Scanner(System.in);

          new Thread(new Runnable() {

          public void run() {

          while(true){

          System.out.println("請選擇類型:0創建群組,1查看群組,2加入群組,3退出群組,4查看群成員,5群發消息");

          int type=scanner.nextInt();

          switch(type){

          case 0:

          createGroup(scanner,channel);

          break;

          case 1:

          listGroup(scanner,channel);

          break;

          case 2:

          addGroup(scanner,channel);

          break;

          case 3:

          quitGroup(scanner,channel);

          break;

          case 4:

          listMembers(scanner,channel);

          break;

          case 5:

          sendMsgToGroup(scanner,channel);

          break;

          default:

          System.out.println("輸入的類型不存在!");

          }

          }

          }

          }).start();

          }

          8.2 服務端 Handler

          服務端 Handler,主要是通過判斷實體類型來做不同的業務操作,當然也可以使用 SimpleChannelInboundHandler 去進行 Handler 拆分。

          以下方法目前都是空方法,下面將詳細講解。

          public class ServerChatGroupHandler extends ChannelInboundHandlerAdapter {

          private static Map<Integer, Channel> map=new HashMap<Integer, Channel>();

          private static Map<Integer, Group> groups=new HashMap<Integer, Group>();

          @Override

          public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {

          if(msg instanceof LoginReqBean) {

          //登錄

          login((LoginReqBean) msg, ctx.channel());

          }else if(msg instanceof GroupCreateReqBean){

          //創建群組

          createGroup((GroupCreateReqBean)msg,ctx.channel());

          }else if(msg instanceof GroupListReqBean){

          //查看群組列表

          listGroup((GroupListReqBean)msg,ctx.channel());

          }else if(msg instanceof GroupAddReqBean){

          //加入群組

          addGroup((GroupAddReqBean)msg,ctx.channel());

          }else if(msg instanceof GroupQuitReqBean){

          //退出群組

          quitGroup((GroupQuitReqBean)msg,ctx.channel());

          }else if(msg instanceof GroupMemberReqBean){

          //查看成員列表

          listMember((GroupMemberReqBean)msg,ctx.channel());

          }else if(msg instanceof GroupSendMsgReqBean){

          //消息發送

          sendMsg((GroupSendMsgReqBean) msg,ctx.channel());

          }

          }

          }

          9、具體功能編碼實戰

          9.1 創建群組

          客戶端請求:

          private void createGroup(Scanner scanner,Channel channel){

          System.out.println("請輸入群組ID");

          Integer groupId=scanner.nextInt();

          System.out.println("請輸入群組名稱");

          String groupName=scanner.next();

          GroupCreateReqBean bean=new GroupCreateReqBean();

          bean.setGroupId(groupId);

          bean.setGroupName(groupName);

          channel.writeAndFlush(bean);

          }

          服務端處理:

          public class ServerChatGroupHandler extends ChannelInboundHandlerAdapter {

          private static Map<Integer, Channel> map=new HashMap<Integer, Channel>();

          private static Map<Integer, Group> groups=new HashMap<Integer, Group>();

          private void createGroup(GroupCreateReqBean bean,Channel channel){

          //定義一個響應實體

          GroupCreateResBean res=new GroupCreateResBean();

          //查詢groups是否已經存在

          Group group=groups.get(bean.getGroupId());

          //判斷是否已經存在

          if(group==null){

          //定義群組實體

          Group g=new Group();

          //定義一個集合,專門存儲成員

          List<GroupMember> members=new ArrayList<GroupMember>();

          //屬性賦值

          g.setGroupName(bean.getGroupName());

          g.setMembers(members);

          //添加到Map里面

          groups.put(bean.getGroupId(),g);

          //響應信息

          res.setCode(0);

          res.setMsg("創建群組成功");

          }else{

          res.setCode(1);

          res.setMsg("該群組已經存在!");

          }

          channel.writeAndFlush(res);

          }

          }

          9.2 查看群組

          客戶端請求:

          private void listGroup(Scanner scanner,Channel channel){

          GroupListReqBean bean=new GroupListReqBean();

          bean.setType("list");

          channel.writeAndFlush(bean);

          }

          服務端處理:

          public class ServerChatGroupHandler extends ChannelInboundHandlerAdapter {

          private static Map<Integer, Channel> map=new HashMap<Integer, Channel>();

          private static Map<Integer, Group> groups=new HashMap<Integer, Group>();

          private void listGroup(GroupListReqBean bean,Channel channel){

          if("list".equals(bean.getType())){

          //定義一個響應實體

          GroupListResBean res=new GroupListResBean();

          //定義一個集合

          List<GroupInfo> lists=new ArrayList<GroupInfo>();

          //變量groups Map集合

          for(Map.Entry<Integer, Group> entry : groups.entrySet()){

          Integer mapKey = entry.getKey();

          Group mapValue = entry.getValue();

          GroupInfo gi=new GroupInfo();

          gi.setGroupId(mapKey);

          gi.setGroupName(mapValue.getGroupName());

          lists.add(gi);

          }

          //把集合添加到響應實體里面

          res.setLists(lists);

          //開始寫到客戶端

          channel.writeAndFlush(res);

          }

          }

          }

          9.3 加入群組

          客戶端請求:

          private void addGroup(Scanner scanner,Channel channel){

          System.out.println("請輸入加入的群組ID");

          int groupId=scanner.nextInt();

          Integer userId=(Integer) channel.attr(AttributeKey.valueOf("userid")).get();

          GroupAddReqBean bean=new GroupAddReqBean();

          bean.setUserId(userId);

          bean.setGroupId(groupId);

          channel.writeAndFlush(bean);

          }

          服務端處理:

          public class ServerChatGroupHandler extends ChannelInboundHandlerAdapter {

          private static Map<Integer, Channel> map=new HashMap<Integer, Channel>();

          private static Map<Integer, Group> groups=new HashMap<Integer, Group>();

          private void addGroup(GroupAddReqBean bean,Channel channel){

          GroupAddResBean res=new GroupAddResBean();

          //1.根據“群組ID”獲取對應的“組信息”

          Group group=groups.get(bean.getGroupId());

          //2.“群組”不存在

          if(group==null){

          res.setCode(1);

          res.setMsg("groupId="+bean.getGroupId()+",不存在!");

          channel.writeAndFlush(res);

          return;

          }

          //3.“群組”存在,則獲取其底下的“成員集合”

          List<GroupMember> members=group.getMembers();

          boolean flag=false;

          //4.遍歷集合,判斷“用戶”是否已經存在了

          for(GroupMember gm:members){

          if(gm.getUserid()==bean.getUserId()){

          flag=true;

          break;

          }

          }

          if(flag){

          res.setCode(1);

          res.setMsg("已經在群組里面,無法再次加入!");

          }else{

          //1.用戶信息

          GroupMember gm=new GroupMember();

          gm.setUserid(bean.getUserId());

          gm.setChannel(channel);

          //2.添加到集合里面

          members.add(gm);

          //3.給“群組”重新賦值

          group.setMembers(members);

          res.setCode(0);

          res.setMsg("加入群組成功");

          }

          channel.writeAndFlush(res);

          }

          }

          9.4 退出群組

          客戶端請求:

          private void quitGroup(Scanner scanner,Channel channel){

          System.out.println("請輸入退出的群組ID");

          int groupId=scanner.nextInt();

          Integer userId=(Integer) channel.attr(AttributeKey.valueOf("userid")).get();

          GroupQuitReqBean bean=new GroupQuitReqBean();

          bean.setUserId(userId);

          bean.setGroupId(groupId);

          channel.writeAndFlush(bean);

          }

          服務端處理:

          public class ServerChatGroupHandler extends ChannelInboundHandlerAdapter {

          private static Map<Integer, Channel> map=new HashMap<Integer, Channel>();

          private static Map<Integer, Group> groups=new HashMap<Integer, Group>();

          private void quitGroup(GroupQuitReqBean bean,Channel channel){

          GroupQuitResBean res=new GroupQuitResBean();

          //1.根據“群組ID”獲取對應的“組信息”

          Group group=groups.get(bean.getGroupId());

          if(group==null){

          //2.群組不存在

          res.setCode(1);

          res.setMsg("groupId="+bean.getGroupId()+",不存在!");

          channel.writeAndFlush(res);

          return;

          }

          //3.群組存在,則獲取其底下“成員集合”

          List<GroupMember> members=group.getMembers();

          //4.遍歷集合,找到“當前用戶”在集合的序號

          int index=-1;

          for(inti=0;i<members.size();i++){

          if(members.get(i).getUserid()==bean.getUserId()){

          index=i;

          break;

          }

          }

          //5.如果序號等于-1,則表示“當前用戶”不存在集合里面

          if(index==-1){

          res.setCode(1);

          res.setMsg("userid="+bean.getUserId()+",不存在該群組里面!");

          channel.writeAndFlush(res);

          return;

          }

          //6.從集合里面刪除“當前用戶”

          members.remove(index);

          //7.給“群組”的“成員列表”重新賦值

          group.setMembers(members);

          res.setCode(0);

          res.setMsg("退出群組成功");

          channel.writeAndFlush(res);

          }

          }

          9.5 查看群組成員

          客戶端請求:

          private void listMembers(Scanner scanner,Channel channel){

          System.out.println("請輸入群組ID:");

          int groupId=scanner.nextInt();

          GroupMemberReqBean bean=new GroupMemberReqBean();

          bean.setGroupId(groupId);

          channel.writeAndFlush(bean);

          }

          服務端處理:

          public class ServerChatGroupHandler extends ChannelInboundHandlerAdapter {

          private static Map<Integer, Channel> map=new HashMap<Integer, Channel>();

          private static Map<Integer, Group> groups=new HashMap<Integer, Group>();

          private void listMember(GroupMemberReqBean bean,Channel channel){

          GroupMemberResBean res=new GroupMemberResBean();

          List<Integer> lists=new ArrayList<Integer>();

          //1.根據“群組ID”獲取對應的“組信息”

          Group group=groups.get(bean.getGroupId());

          if(group==null){

          //2.查詢的群組不存在

          res.setCode(1);

          res.setMsg("groupId="+bean.getGroupId()+",不存在!");

          channel.writeAndFlush(res);

          }else{

          //3.群組存在,則變量其底層的成員

          for(Map.Entry<Integer, Group> entry : groups.entrySet()){

          Group g = entry.getValue();

          List<GroupMember> members=g.getMembers();

          for(GroupMember gm:members){

          lists.add(gm.getUserid());

          }

          }

          res.setCode(0);

          res.setMsg("查詢成功");

          res.setLists(lists);

          channel.writeAndFlush(res);

          }

          }

          }

          9.6 群發消息

          客戶端請求:

          private void sendMsgToGroup(Scanner scanner,Channel channel){

          System.out.println("請輸入群組ID:");

          int groupId=scanner.nextInt();

          System.out.println("請輸入發送消息內容:");

          String msg=scanner.next();

          Integer userId=(Integer) channel.attr(AttributeKey.valueOf("userid")).get();

          GroupSendMsgReqBean bean=new GroupSendMsgReqBean();

          bean.setFromuserid(userId);

          bean.setTogroupid(groupId);

          bean.setMsg(msg);

          channel.writeAndFlush(bean);

          }

          服務端處理:

          public class ServerChatGroupHandler extends ChannelInboundHandlerAdapter {

          private static Map<Integer, Channel> map=new HashMap<Integer, Channel>();

          private static Map<Integer, Group> groups=new HashMap<Integer, Group>();

          privatevoidsendMsg(GroupSendMsgReqBean bean,Channel channel){

          GroupSendMsgResBean res=new GroupSendMsgResBean();

          //1.根據“群組ID”獲取對應的“組信息”

          Group group=groups.get(bean.getTogroupid());

          //2.給“發送人”響應,通知其發送的消息是否成功

          if(group==null){

          res.setCode(1);

          res.setMsg("groupId="+bean.getTogroupid()+",不存在!");

          channel.writeAndFlush(res);

          return;

          }else{

          res.setCode(0);

          res.setMsg("群發消息成功");

          channel.writeAndFlush(res);

          }

          //3.根據“組”下面的“成員”,變量并且逐個推送消息

          List<GroupMember> members=group.getMembers();

          for(GroupMember gm:members){

          GroupRecMsgBean rec=new GroupRecMsgBean();

          rec.setFromuserid(bean.getFromuserid());

          rec.setMsg(bean.getMsg());

          gm.getChannel().writeAndFlush(rec);

          }

          }

          }

          10、本篇小結

          本篇中涉及的功能點稍微有點多,主要是實現了群聊的幾個核心功能,分別是:創建群組、查看群組列表、加入群組、退出群組、查看成員列表、群發消息。

          這些功能經過拆解,看起來就不是那么復雜了,希望大家都可以親自動手實現一遍,加深理解,提高學習效果。

          實際上,真正的產品級IM中,群聊涉及的技術細節是非常多的,有興趣可以詳讀下面這幾篇:

          • IM群聊消息如此復雜,如何保證不丟不重?
          • 移動端IM中大規模群消息的推送如何保證效率、實時性?
          • 關于IM即時通訊群聊消息的亂序問題討論
          • IM群聊消息究竟是存1份(即擴散讀)還是存多份(即擴散寫)?
          • 一套高可用、易伸縮、高并發的IM群聊、單聊架構方案設計實踐
          • 網易云信技術分享:IM中的萬人群聊技術方案實踐總結
          • 阿里電商IM消息平臺,在群聊、直播場景下的技術實踐
          • 企業微信的IM架構設計揭秘:消息模型、萬人群、已讀回執、消息撤回等
          • 融云IM技術分享:萬人群聊消息投遞方案的思考和實踐

          11、參考資料

          [1] 手把手教你用Netty實現心跳機制、斷線重連機制

          [2] 自已開發IM很難?手把手教你擼一個Andriod版IM

          [3] 基于Netty,從零開發一個IM服務端

          [4] 拿起鍵盤就是干,教你徒手開發一套分布式IM系統

          [5] 正確理解IM長連接、心跳及重連機制,并動手實現

          [6] 手把手教你用Go快速搭建高性能、可擴展的IM系統

          [7] 手把手教你用WebSocket打造Web端IM聊天

          [8] 萬字長文,手把手教你用Netty打造IM聊天

          [9] 基于Netty實現一套分布式IM系統

          [10] 基于Netty,搭建高性能IM集群(含技術思路+源碼)

          [11] SpringBoot集成開源IM框架MobileIMSDK,實現即時通訊IM聊天功能

          學習交流:

          - 移動端IM開發入門文章:《新手入門一篇就夠:從零開發移動端IM》

          - 開源IM框架源碼:https://github.com/JackJiang2011/MobileIMSDK(備用地址點此)

          本文已同步發布于:http://www.52im.net/thread-3981-1-1.html)

          TML實現文件夾的上傳和下載,前端如何用HTML5實現分片上傳GB級大文件,網頁中實現文件上傳下載的三種解決方案(推薦),HTML5實現文件批量上傳組件,JQUERY 實現文件夾上傳(保留目錄結構),B/S大文件上傳支持斷點上傳,WebService 大文件上傳,斷點續傳分片,HTML+AJAX實現上傳大文件問題,用HTML實現本地文件的上傳,HTML5實現大文件上傳,HTML5實現大文件分片上傳思路,利用HTML5分片上傳超大文件思路,

          WEBUPLOADER 支持 超大上G,多附件上傳,JS 大文件分割/分片上傳,

          百度WEBUPLOADER上傳視頻等大文件,WEBUPLOAD組件實現文件上傳功能和下載功能,JS大文件切片上傳,斷點續傳實現DEMO,前端上傳大文件的解決方案,前端上傳大文件處理(切片、斷點續傳),前端大文件上傳優化方案——分片上傳,vue大文件上傳解決方案,vue大文件上傳解決方案10G,vue大文件上傳解決方案50G,vue大文件上傳解決方案100G,html5如何實現大文件斷點續傳、秒傳,

          java如何實現大文件斷點續傳、秒傳,SpringBoot如何實現大文件斷點續傳、秒傳,SpringMVC如何實現大文件斷點續傳、秒傳,SpringCloud如何實現大文件斷點續傳、秒傳,

          webuploader如何實現大文件斷點續傳、秒傳,百度webuploader如何實現大文件斷點續傳、秒傳,html5實現大文件斷點續傳、秒傳,vue如何實現大文件斷點續傳、秒傳,前端如何實現大文件斷點續傳、秒傳,JavaScript如何實現大文件斷點續傳、秒傳,

          html5大文件斷點續傳、秒傳解決方案,html5大文件斷點續傳、加密上傳解決方案,html5大文件斷點續傳、加密存儲解決方案,html5大文件斷點續傳分片解決方案,html5大文件斷點續傳分塊解決方案,html5大文件斷點續傳分割解決方案,html5大文件斷點續傳切割解決方案,

          后端我們公司項目組選的是JAVA,因為公司有自己的產品,所以直接使用就行了,針對客戶需求這塊是進行擴展。

          客戶這邊實際上要傳的文件單個大小就有50G左右,所以需要支持斷點續傳和分片上傳,并且要支持多線程上傳,能夠充分利用帶寬資源。

          之前在網上找過相關的資料,論壇里面也有網友交流過,還加過很多QQ群和微信群,但是結果都不太令人滿意。

          技術選型的話用的是jquery,也是一個企業內網系統,用的是之前公司的框架,只是進行功能擴展

          分片網上討論的很多,基本上全部都是用的HTML5的API,這個方案也不是不能用,但是在IE下面就不行了,兼容性差了點,并且也不能進行擴展,不能進行二次開發,限制性非常大,我們技術同事提的要求是需要提供產品完整源代碼,

          網上的文章全部都沒有提供文件夾上傳和續傳的代碼,也沒有提供數據庫操作的代碼,

          另外這塊我們是要求必須提供產品完整源代碼,因為后面我們需要自已來維護,同時是要求能夠自主可控的

          研發部門的同事調研過百度的webuploader這個組件,發現他實際上就是對Flash和HTML5進行了一個封裝,本質還是調的HTML5的API,之前在項目中也用過,嘗試過,但是最終都不太滿意,一個是兼容性非常差,說的是兼容IE,但是在IE用的是Flash,在很多用戶的電腦上用不了,卡頓崩潰發生的太頻繁,文件上傳的數量一多比如幾千個,前端頁面就開始卡了,用戶體驗非常差。這些問題研發部的同事都向百度反應過,但是百度webuploader那邊一直沒人回,基本上沒人管,領導說要求付費提供技術支持,那邊也是沒人回,聯系不上他們任何人。

          webuploader這邊連個開發人員都聯系不到,這個是怎么回事?

          用戶上傳的文件比較大,有20G左右,直接用HTML傳的話容易失敗,服務器也容易出錯,需要分片,分塊,分割上傳。也就是將一個大的文件分成若干個小文件塊來上傳,另外就是需要實現秒傳功能和防重復功能,秒傳就是用戶如果上傳過這個文件,那么直接在數據庫中查找記錄就行了,不用再上傳一次,節省時間,實現的思路是對文件做MD5計算,將MD5值保存到數據庫,算法可以用MD5,或者CRC,或者SHA1,這個隨便哪個算法都行。

          切片的話還有一點就是在服務器上合并,一個文件的所有分片數據上傳完后需要在服務器端進行合并操作。

          視頻教程:https://www.ixigua.com/7227314770696012322

          導入項目:
          導入到Eclipse:http://www.ncmem.com/doc/view.aspx?id=9da9c7c2b91b40b7b09768eeb282e647
          導入到IDEA:http://www.ncmem.com/doc/view.aspx?id=9fee385dfc0742448b56679420f22162
          springboot統一配置:http://www.ncmem.com/doc/view.aspx?id=7768eec9284b48e3abe08f032f554ea2

          下載示例:

          https://gitee.com/xproer/up6-jsp-eclipse/tree/6.5.40/

          工程

          NOSQL

          NOSQL示例不需要任何配置,可以直接訪問測試

          創建數據表

          選擇對應的數據表腳本,這里以SQL為例

          修改數據庫連接信息

          訪問頁面進行測試

          文件存儲路徑

          up6/upload/年/月/日/guid/filename

          相關問題:
          1.javax.servlet.http.HttpServlet錯誤
          2.項目無法發布到tomcat
          3.md5計算完畢后卡住
          4.服務器找不到config.json文件

          相關參考:

          文件保存位置

          源碼工程文檔:https://drive.weixin.qq.com/s?k=ACoAYgezAAw1dWofra

          源碼報價單:https://drive.weixin.qq.com/s?k=ACoAYgezAAwoiul8gl

          OEM版報價單:https://drive.weixin.qq.com/s?k=ACoAYgezAAwuzp4W0a

          產品源代碼:https://drive.weixin.qq.com/s?k=ACoAYgezAAwbdKCskc
          授權生成器:https://drive.weixin.qq.com/s?k=ACoAYgezAAwTIcFph1


          主站蜘蛛池模板: 国产一区在线视频观看| 国产精品久久久久久一区二区三区| 怡红院AV一区二区三区| 亚洲午夜精品一区二区| 无码av免费一区二区三区| 在线精品动漫一区二区无广告| 国产亚洲欧洲Aⅴ综合一区| 好湿好大硬得深一点动态图91精品福利一区二区 | 久久高清一区二区三区| 日韩国产精品无码一区二区三区| 麻豆一区二区三区精品视频| 一色一伦一区二区三区| 爱爱帝国亚洲一区二区三区| 精品人妻一区二区三区四区在线| 美女福利视频一区二区| 亚洲AV美女一区二区三区| 久久精品动漫一区二区三区| 免费播放一区二区三区| 亚洲综合av一区二区三区| 八戒久久精品一区二区三区| 国产在线一区二区在线视频 | 国产亚洲综合一区柠檬导航 | 综合久久一区二区三区| 3d动漫精品啪啪一区二区中文 | 天天躁日日躁狠狠躁一区| 久久精品人妻一区二区三区 | 无码人妻精品一区二| 精品一区二区三区免费| 91精品国产一区| 色视频综合无码一区二区三区| 国产波霸爆乳一区二区| 亚洲AV日韩综合一区尤物| 国产一区二区四区在线观看| 亚洲一区二区三区免费观看| 视频一区视频二区制服丝袜 | 无码日韩精品一区二区免费| 国产美女视频一区| 精品深夜AV无码一区二区老年 | 一区一区三区产品乱码| 国产成人精品一区二区三区| bt7086福利一区国产|