天天看點

webSocket簡單應用(三、帶房間的聊天室編寫)

制作背景為老師和學生交流,但是交流的範圍為班級的某一個任課老師和本班的學生,執行的操作是老師向學生詢問是否聽懂了,學生進行回複。這是一個項目中的一部分,主要用于線上教學時的學生回應。

WebSocketRoom配置類

@Slf4j
public class WebSocketRoom {
    public static Map<String, ConcurrentHashMap<String, Session>> rooms = new ConcurrentHashMap();
    public static void close(String roomId,String sessionId) throws IOException {//sessionId就是account
        log.info("{}從{}房間斷開了連結",sessionId,roomId);
        Map<String, Session> sessions=rooms.get(roomId);//擷取目前房間的資訊
        for (String account : sessions.keySet()) {
            Session session = sessions.get(account);//擷取目前hashmap中的value值
            if (session.getId().equals(sessionId)) {
                sessions.remove(account);//如果目前池中有key對應的value值,則進行清除操作
                break;
            }
        }
    }

    public static void sendMessage(String roomId,String account, String message) {//一對一發送,用于學生回複老師是否聽懂
        log.info("在房間{}中,{}發送消息{}",roomId,account,message);
        rooms.get(roomId).get(roomId).getAsyncRemote().sendText(message);//擷取需要發送的使用者,并發送資訊
    }

    public static void sendMessage(String roomId,String message) {//對目前所有的連結進行發送,用于老師向學生提問是否聽懂
        for (String sessionId : rooms.get(roomId).keySet()) {
            log.info("已經向"+sessionId+"發送消息"+message);
            rooms.get(roomId).get(sessionId).getAsyncRemote().sendText(message);//擷取需要發送的使用者,并發送資訊
        }
    }
    public static String sendMessage(String roomId) {//擷取目前房間所有的使用者
        String allMember="";
        for (String account : rooms.get(roomId).keySet()) {
            if(allMember=="")
                allMember=account;
            else
            allMember=allMember+","+account;//擷取使用者賬号并拼接
        }
        return  allMember;
    }
}
           

其實和第一個wesocket配置類相似,隻不過是在操作前先要找到對應房間号的map,之後再進行其他的操作,配備上房間内的群發和一對一發送。這裡面需要注意ConcurrentHashMap要聲明為靜态變量。每個使用者的發送資訊的前提是知道接收者的session位址是什麼,這就需要一個公共的存儲區域,友善去尋找。是以聲明為static類型(靜态變量也稱為類變量,屬于類對象所有,位于方法區,為所有對象共享,共享一份記憶體,一旦值被修改,則其他對象均對修改可見)這要就可以保證能夠拿到正确的資料了,同時方法也聲明為static類型,用于操作連結池。

這裡面定了不同入參的sendMessage用于群發和一對一發送

WebSocketServiceImp類的編寫

@Slf4j
@Service
@Component
@ServerEndpoint("/webSocketRoom/{account}/{roomId}")
@DependsOn("springContextUtils")
public class WebSocketRoomServiceImpl {
    /** 記錄目前線上連接配接數 */
//    private static AtomicInteger onlineCount = new AtomicInteger(0);
    private Session session;
   

    //為這個變量添加set方法
    public static void setApplicationContext(ApplicationContext applicationContext) {
        WebSocketRoomServiceImpl.applicationContext = applicationContext;
    }
    @OnOpen
    public void onOpen(Session session, @PathParam("account") String account,@PathParam("roomId") String roomId) throws IOException {
        if(WebSocketRoom.rooms.get(roomId)==null&&account.equals(roomId)){//如果房間号和賬号時相同,說明是老師賬号,則建立房間
            ConcurrentHashMap<String, Session> sessions = new ConcurrentHashMap<>();//建立新的連接配接池
            WebSocketRoom.rooms.put(roomId,sessions);
        }else if(WebSocketRoom.rooms.get(roomId)==null&&!account.equals(roomId))//如果房間号不存在且賬号和房間号不一緻則關閉連接配接
        {
            log.info("{}加入的{}房間不存在",account,roomId);
            this.onClose(session,account,roomId);
            return;
        }

        WebSocketRoom.rooms.get(roomId).put(account, session);//加入使用者到連接配接池中
        log.info("有賬号加入:{},目前線上人數為:{}", account, WebSocketRoom.rooms.get(roomId).size());
//        String nowNumber=roomId+"目前線上人數"+WebSocketRoom.rooms.get(roomId).size();
//        String nowNumber= WebSocketRoom.sendMessage(roomId);
        log.info("目前所有的使用者:"+WebSocketRoom.sendMessage(roomId));
        this.onMessage(roomId,account,WebSocketRoom.sendMessage(roomId));//發送目前加入的賬号
    }

    @OnClose
    public void onClose(Session session,@PathParam("account") String account,@PathParam("roomId") String roomId) throws IOException {
        if(WebSocketRoom.rooms.get(roomId)==null){
            session.close();//關閉目前使用者連結
            return;
        }
        log.info("有賬号退出:{},目前線上人數為:{}", account, WebSocketRoom.rooms.get(roomId).size());
        WebSocketRoom.close(roomId,session.getId());//從連結池中删除使用者資訊
        session.close();//關閉目前使用者連結
        log.info("目前所有的使用者:"+WebSocketRoom.sendMessage(roomId));
        this.onMessage(roomId,account,WebSocketRoom.sendMessage(roomId));//發送目前加入的賬号
        if(WebSocketRoom.rooms.get(roomId).size()==0)
        {
            log.info("{}房間已經銷毀",roomId);
            WebSocketRoom.rooms.remove(roomId);
        }
    }
    @OnError
    public void onError(Throwable t) throws Throwable {
        log.error("webScoketRoom報錯: " + t.toString());
    }


    @OnMessage
    public void onMessage(@PathParam("roomId") String roomId,@PathParam("account") String account,String message){
        message=account+":"+message;
        if(roomId.equals(account)){
            log.info("{}執行了一對多發送操作{}",account,roomId);
            WebSocketRoom.sendMessage(roomId,message);//發送消息群發
        }
        else{
            log.info("{}執行了一對一發送操作{}",account,roomId);
            WebSocketRoom.sendMessage(roomId,account,message);//學生向老師發送消息
        }
    }
    public void send(Session toSession,String message){
        try {
            log.info("服務端給用戶端[{}]發送消息{}", toSession.getId(), message);
            toSession.getBasicRemote().sendText(message);
        } catch (Exception e) {
            log.error("服務端發送消息給用戶端失敗:{}", e);
        }
    }

}
           

首先是@OnOpen注解,他的意思是在連結建立的時候調用的方法。我們在這個方法中對使用者資訊進行操作,因為環境是老師和學生,那麼能夠建立班級聊天室的的應該是老師。先判斷房間号在連接配接池中有沒有,如果有則加入。如果沒有則判斷房間号和使用者賬号是否一緻,如果一緻則認為是老師賬号,先将房間加入到連結池中,在将老師的賬号加入到連結池中。

之後是@OnMessage,這個注解是對接受到的消息進行處理,因為操作要求的原因,我們需要判斷目前賬号是否為老師賬号,如果是則執行群發操作對班級中所有的學生都發消息,如果是學生則執行一對一操作用于學生回答老師的詢問

@OnClose用于關閉連接配接,這裡需要根據自己的需要做處理,例如我這裡需要老師斷線從進入後還有學生的線上資訊,是以我房間關閉的設定是所有人都不在房間内。

@OnError就是處理報錯資訊

繼續閱讀