上節課完成了netty的後端搭建,搞定了簡單的http請求響應,今天來結合前端websocket來完成羣聊功能。話不多說先上圖:
前端構建
- 不使用複雜構建工具直接靜態頁面走起
使用了zui樣式庫 http://zui.sexy/?#/,非常不錯,有好多模板。我使用的是聊天模板改造
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/zui/1.8.1/css/zui.min.css">
<link rel="stylesheet" href="zui-theme.css">
- 主體部分
<div class="container">
<h1>mike多人聊天室,等你來聊</h1>
<div class="comments">
<section class="comments-list" id="chatlist">
<div class="comment">
<a href="###" class="avatar">
<i class="icon-user icon-2x"></i>
</a>
<div class="content">
<div><strong>其他人</strong></div>
<div class="text">其他人的聊天內容</div>
</div>
</div>
<div class="comment">
<a href="###" class="avatar pull-right">
<i class="icon-user icon-2x"></i>
</a>
<div class="content pull-right">
<div><strong>我</strong></div>
<div class="text">我說話的內容</div>
</div>
</div>
</section>
<footer>
<div class="reply-form" id="commentReplyForm1">
<form class="form">
<div class="form-group">
<div class="input-control has-label-left">
<input id="userName" type="text" class="form-control" placeholder="">
<label for="inputAccountExample2" class="input-control-label-left">暱稱:</label>
</div>
</div>
</form>
<a href="###" class="avatar"><i class="icon-user icon-2x"></i></a>
<form class="form">
<div class="form-group">
<textarea id="inputMsg" class="form-control new-comment-text" rows="2" value="" placeholder="開始聊天... 輸入enter 發送消息"></textarea>
</div>
</form>
</div>
</footer>
</div>
</div>
- 引入依賴js
<!-- ZUI Javascript 依賴 jQuery -->
<script src="https://cdnjs.cloudflare.com/ajax/libs/zui/1.8.1/lib/jquery/jquery.js"></script>
<!-- ZUI 標準版壓縮後的 JavaScript 文件 -->
<script src="https://cdnjs.cloudflare.com/ajax/libs/zui/1.8.1/js/zui.min.js"></script>
- websocket的js代碼以及業務代碼
<script type="text/javascript">
window.CHAT = {
me: "",
WS:{},
init: function () {
if (window.WebSocket) {
this.WS = new WebSocket("ws://A156B7L58CCNY4B:8090/ws");
this.WS.onmessage = function(event) {
var data = event.data;
console.log("收到數據:" + data);
//顯示其他人的聊天信息
console.log(CHAT.me);
console.log(data.split(":")[0]);
if(CHAT.me != data.split(":")[0]) {
appendOtherchat(data);
}
},
this.WS.onclose = function(event) {
console.log("連接關閉");
},
this.WS.onopen = function(evt) {
console.log("Connection open ...");
},
this.WS.onerror = function(event) {
console.log("連接失敗....");
}
} else {
alert("您的瀏覽器不支持聊天,請更換瀏覽器");
}
},
chat:function (msg) {
this.WS.send(msg);
}
}
CHAT.init();
function Trim(str) {
return str.replace(/(^\s*)|(\s*$)/g, "");
}
function appendMy (msg) { //拼接自己的聊天內容
document.getElementById('chatlist').innerHTML+="<div class='comment'><a class='avatar pull-right'><i class='icon-user icon-2x'></i></a><div class='content pull-right'><div><strong>我</strong></div><div class='text'>"+msg+"</div></div></div>";
}
function appendOtherchat(msg) { //拼接別人的聊天信息到聊天室
var msgs = msg.split(":");
document.getElementById('chatlist').innerHTML+="<div class='comment'><a class='avatar'><i class='icon-user icon-2x'></i></a><div class='content'><div><strong>"+msgs[0]+"</strong></div><div class='text'>"+msgs[1]+"</div></div></div>";
}
document.getElementById('inputMsg').addEventListener('keyup', function(event) {
if (event.keyCode == "13") {
//回車執行查詢
var inputMsg = document.getElementById('inputMsg').value;
if (inputMsg == null || Trim(inputMsg) == "" ) {
alert("請輸入聊天消息");
} else {
var userName = document.getElementById('userName').value;
if (userName == null || userName == '') {
alert("請輸入聊天暱稱");
} else {
//發送消息 定義消息格式 用戶名:[消息]
CHAT.chat(userName+":"+inputMsg);
//記錄我的暱稱
CHAT.me = userName;
appendMy(inputMsg);
//發送完清空輸入
document.getElementById('inputMsg').focus();
document.getElementById('inputMsg').value="";
}
}
}
});
</script>
都有註釋就不解釋了自己看
後端服務改造
-
ChatHandler
改造,判斷websocket響應
/**
* 讀取客戶端發送的消息,並將信息轉發給其他客戶端的 Channel。
*/
@Override
protected void channelRead0(ChannelHandlerContext ctx, Object request) throws Exception {
if (request instanceof FullHttpRequest) { //是http請求
FullHttpResponse response = new DefaultFullHttpResponse(
HttpVersion.HTTP_1_1,HttpResponseStatus.OK , Unpooled.wrappedBuffer("Hello netty"
.getBytes()));
response.headers().set("Content-Type", "text/plain");
response.headers().set("Content-Length", response.content().readableBytes());
response.headers().set("connection", HttpHeaderValues.KEEP_ALIVE);
ctx.channel().writeAndFlush(response);
} else if (request instanceof TextWebSocketFrame) { // websocket請求
String userId = ctx.channel().id().asLongText();
System.out.println("收到客戶端"+userId+":"+((TextWebSocketFrame)request).text());
//發送消息給所有客戶端
channels.writeAndFlush(new TextWebSocketFrame(((TextWebSocketFrame)request).text()));
//發送給單個客戶端
//ctx.channel().writeAndFlush(new TextWebSocketFrame(((TextWebSocketFrame)request).text()));
}
}
* ChatServerInitializer
改造,加入WebSocket
@Override
protected void initChannel(SocketChannel ch) throws Exception {
ChannelPipeline pipeline = ch.pipeline();
//websocket協議本身是基於http協議的,所以這邊也要使用http解編碼器
pipeline.addLast(new HttpServerCodec());
//以塊的方式來寫的處理器
pipeline.addLast(new ChunkedWriteHandler());
//netty是基於分段請求的,HttpObjectAggregator的作用是將請求分段再聚合,參數是聚合字節的最大長度
pipeline.addLast(new HttpObjectAggregator(1024*1024*1024));
//ws://server:port/context_path
//ws://localhost:9999/ws
//參數指的是contex_path
pipeline.addLast(new WebSocketServerProtocolHandler("/ws",null,true,65535));
//自定義handler
pipeline.addLast(new ChatHandler());
System.out.println("ChatClient:"+ch.remoteAddress() +"連接上");
}
改造完成
啓動後端服務,訪問你的前端靜態頁面就可以和小夥伴聊天了。其實後端羣聊很簡單,就是把一個用戶的輸入消息,返回給所有在線客戶端,前端去負責篩選顯示。自己動手照着搞10分鐘就能完成。
實現功能
- 輸入聊天暱稱開始聊天
- 聊天消息不爲空才能發送
- 發送完自動清空輸入,且聚焦輸入框
- 自己的消息顯示在左側,其他人的消息在右側
別忘了關注我 mike啥都想搞
求關注啊。