制作背景為老師和學生交流,但是交流的範圍為班級的某一個任課老師和本班的學生,執行的操作是老師向學生詢問是否聽懂了,學生進行回複。這是一個項目中的一部分,主要用于線上教學時的學生回應。
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就是處理報錯資訊