小型直播系統-java實現(四)

編程語言 Java JSON 技術 java探案 java探案 2017-09-28

websocket協議介紹

屁話不多說,簡單點說,就是一個保持全雙工,能夠保持長連接的協議,我們用它來進行發送彈幕和發視頻,圖片進行即時交流的工具。下面進行彈幕的技術實現。

@OnOpen

public void onOpen(Session session)

{session.setMaxTextMessageBufferSize((int) MAX_BIG_LONG);

addOnlineCount(); // 在線數加1--必須先加1---錯誤的時候會減1

this.session = session;

if (!parseQueryString(session)) // 如果未能取得用戶id和type,退出return;// 驗證賬號,防止偽造

this.chatUser = loginChatServer(chatUserId);

if (this.chatUser == null)

{

closeSession(session);return;

}

addChatUserToHashMap(roomId, chatUserId);

try

{// 發一個應答標記,表示已經成功登陸,沒有構造

sendMessage("SUCCESS");

}

catch (IOException e) {

// TODO Auto-generated catch blocke.printStackTrace();

}Constant.ONLINECOUNT = onlineCount.toString();

}

onopen標識客戶端與服務器進行通訊連接,在此處可進行業務邏輯的處理,將所需賬號提出進行保存。

@Component

@ServerEndpoint("/chatServer")

public class ChatServer {

private static Log logger = LogFactory.getLog(ChatServer.class);

/** AtomicInteger:線程安全的整數對象 */

private static AtomicInteger onlineCount = new AtomicInteger(0);// 線程安全整數對象

private static long MAX_BIG_LONG = 1024 * 4 * 1024;

/** roomId與一個集合的哈希。集合中存儲當前房間的所有用戶 */

private static ConcurrentHashMap<String, CopyOnWriteArraySet<String>> roomToChatUserHashMap = new ConcurrentHashMap<String, CopyOnWriteArraySet<String>>();

/** 用戶與chatServer實例的哈希。 */

private static ConcurrentHashMap<String, ChatServer> chatUserToChatServer = new ConcurrentHashMap<String, ChatServer>();

/** token驗證 **/

private String token;

/** 房間號 **/

private String roomId;

/** chatUser 主鍵Id **/

private String chatUserId;

/** chatUserd對象 **/

private ChatUser chatUser;

/**

* concurrent包的線程安全Set,用來存放每個客戶端對應的MyWebSocket對象。若要實現服務端與單一客戶端通信的話,

* 可以使用Map來存放,其中Key可以為用戶標識·1

*/



聲明兩個類級別的ConcurrentHashMap存儲房間號和chatUser之間的對應關係,一個房間對應多個chatUser賬號,一個chatUser對應一個chatServer實例,發送消息時只需進行遍歷這倆個map找到相應的實例對象,調用它的發消息即可成功的發送

/**

* 一個房間對應的一個chatuser列表 發消息時候進行遍歷操作

*

* @param chatUserId

* @param chatUserId

* @return

*/

private boolean addChatUserToHashMap(String roomId, String chatUserId) {

try {

CopyOnWriteArraySet<String> chatUserIdSet = null;

if (roomToChatUserHashMap.containsKey(roomId)) {

chatUserIdSet = roomToChatUserHashMap.get(roomId);

} else {

chatUserIdSet = new CopyOnWriteArraySet<String>();

}

chatUserIdSet.add(chatUserId);

roomToChatUserHashMap.put(roomId, chatUserIdSet);

chatUserToChatServer.put(chatUserId, this);

return true;

} catch (Exception e) {

e.printStackTrace();

return false;

}

}

這是進入房間時,將自身賬號加入到房間聊天群組,用來發廣播消息時遍歷的。

/**

* 從哈希中移除已經斷開的連接

*

* @param chatUserId

* @param terminalUuid

* @return

*/

private boolean removeChatUserFromRoomHashMap(String roomId, String chatUserId) {

try {

CopyOnWriteArraySet<String> chatUserIdSet = null;

if (roomToChatUserHashMap.containsKey(roomId)) {// 如果存在

chatUserIdSet = roomToChatUserHashMap.get(chatUserId);// 取得chatUserId的集合

} else {

return true;

}

chatUserIdSet.remove(chatUserId);// 從集合中移除

if (chatUserIdSet.size() == 0) {// 如果已經沒有連接終端

roomToChatUserHashMap.remove(roomId);// 則清除

} else {

roomToChatUserHashMap.put(roomId, chatUserIdSet);// 更新哈希

}

ChatServer chatServer = chatUserToChatServer.get(chatUserId);

// 釋放資源,清空chatServer

chatServer = null;

chatUserToChatServer.remove(chatUserId);// 將chatServer的實例從哈希中移除。

return true;

} catch (Exception e) {

e.printStackTrace();

return false;

}

}

當斷開聊天室時,應該講其從聊天組移除掉。

/**

* 關閉websocket連接。

*

* @param session

* 要關閉的會話

*/

private void closeSession(Session session) {

try {

session.close();

} catch (IOException e) {

e.printStackTrace();

}

return;

}

/**

* 連接關閉調用的方法

*/

@OnClose

public void onClose(Session session) {

try {

removeChatUserFromRoomHashMap(this.roomId, this.chatUserId);

subOnlineCount(); // 在線數減1

} catch (Exception e) {

}

}

關閉調用的方法

/**

* 收到客戶端消息後調用的方法。 客戶端發送消息的方法是,發送到服務器,消息中指明要傳送給那個用戶,消息的類型。<br/>

* 此處只管發送,至於發送的模式(緊急通知,普通通知,訂單消息,聊天信息),這裡並進行解析處理,由客戶端自行處理。<br/>

*

*

* @param message

* 客戶端發送過來的消息

* @param session

* 可選的參數

*/

@OnMessage

public void onMessage(String message, Session session) {

if (StringUtils.isBlank(message)) // 收到的是空串

return;

if (StringUtils.equals(Constant.SUCCESS_RESPONSE, message)) {

return;

}

Gson gson = null;

try {// 解析json串

gson = new Gson();

ChatMessage chatMessage = gson.fromJson(message, ChatMessage.class);

// 解析json出錯

if (chatMessage == null)

return;

/**

* 目的為了以後對每個模塊進行拓展,所以分開寫

*

*/

// 如果是圖片類型的消息體

if (StringUtils.equals(chatMessage.getMessageType(), EnumMessageType.IMAGE.name())) {

try {

boolean dealImageResult = dealBinary(chatMessage);// 處理圖片結果

if (!dealImageResult) {

return;// 如果沒生成,則返回

}

} catch (Exception e) {

logger.error("處理圖片異常" + e.getMessage());

}

}

// 處理小視頻

else if ((StringUtils.equals(chatMessage.getMessageType(), EnumMessageType.VIDEO.name()))) {

try {

boolean dealVideoResult = dealBinary(chatMessage);

if (!dealVideoResult) {

return;

}

} catch (Exception e) {

logger.error("處理小視頻異常" + e.getMessage());

}

}

// 處理二進制文件

else if ((StringUtils.equals(chatMessage.getMessageType(), EnumMessageType.BINARY.name()))) {

try {

boolean dealBinaryResult = dealBinary(chatMessage);

if (!dealBinaryResult) {

return;

}

} catch (Exception e) {

// TODO: handle exception

logger.error("處理文件異常" + e.getMessage());

}

}

chatMessage.setImageBase64("");// 清空串

// 不用做NPE判斷,因為chatUser如果為空 則推出closeSession 所以不可能為空

chatMessage.setChatName(this.chatUser.getUsername());

sendMessageToEveryoneInRoom(chatMessage);

} catch (Exception e) {// 發生錯誤即退出

e.printStackTrace();

}

}

進行通訊的模塊。具體代碼私信我,

## 不足之處為考慮多個終端登錄的情況,比如一個用戶雙開瀏覽器,這時候這套方案顯然是不可行的,具體的解決方案找我諮詢

## 彈幕聊天室的實現

小型直播系統-java實現(四)

小型直播系統-java實現(四)

小型直播系統-java實現(四)

相關推薦

推薦中...