[Common] 聊天front-backend簡(jiǎn)單實(shí)現(xiàn)

這又是非serious系列的簡(jiǎn)單嘗試0.0 最近大概只有買買買能讓我內(nèi)心得到平靜... 這篇的主題其實(shí)是長(zhǎng)連接哈~ 不喜歡的朋友可以跳過(guò)啦~

1. 搭建服務(wù)器

第一步就是你需要先安裝nodejs稚补,然后安裝websocket:

npm install nodejs-websocket

然后你按照下面reference里面第一篇的方法就可以寫個(gè)server以及client html啦肋联,啟動(dòng)server就用node xxx.js即可蚓挤。client就直接雙擊打開(kāi)html他自己就會(huì)連接你啟動(dòng)的server(localhost:3000)寇窑。

那么 websocket 是啥呢杨何?
Websocket是一個(gè)持久化的協(xié)議灌闺,相對(duì)于HTTP這種非持久的協(xié)議來(lái)說(shuō)霎俩。

HTTP通常是有個(gè)request袭厂,然后立刻server就會(huì)給你response剑刑,但是server不能主動(dòng)發(fā)起給客戶端傳輸數(shù)據(jù)媳纬,必須要有request。即使是long poll 和 ajax輪詢 其實(shí)本質(zhì)還是需要client先發(fā)request施掏。

WebSocket 解決的第一個(gè)問(wèn)題是钮惠,通過(guò)第一個(gè) HTTP request 建立了 TCP 連接之后,之后的交換數(shù)據(jù)都不需要再發(fā) HTTP request了七芭,使得這個(gè)長(zhǎng)連接變成了一個(gè)真長(zhǎng)連接素挽。
在此基礎(chǔ)上 WebSocket 還是一個(gè)雙通道的連接,在同一個(gè) TCP 連接上既可以發(fā)也可以收信息狸驳。此外還有 multiplexing 功能毁菱,幾個(gè)不同的 URI 可以復(fù)用同一個(gè) WebSocket 連接米死。這些都是原來(lái)的 HTTP 不能做到的。

image.png

看起來(lái)是不是特別適合聊天場(chǎng)景贮庞!其實(shí)我本來(lái)想寫個(gè)直播的消息推送的峦筒,現(xiàn)在決定還是寫個(gè)聊天app吧~ (善變的女人...

2. 實(shí)現(xiàn)多播

首先先做一個(gè)簡(jiǎn)單地修改,讓多個(gè)客戶端(這里指網(wǎng)頁(yè)哈)可以連到server窗慎,然后一個(gè)客戶端的消息會(huì)廣播給所有客戶端物喷。

其實(shí)改動(dòng)很小哦,server端加一個(gè)廣播函數(shù)調(diào)用就可以啦:

function broadcast(server, msg) {
    //server.connections是一個(gè)數(shù)組遮斥,包含所有連接進(jìn)來(lái)的客戶端
    server.connections.forEach(function (conn) {
        //connection.sendText方法可以發(fā)送指定的內(nèi)容到客戶端峦失,傳入一個(gè)字符串
        //這里為遍歷每一個(gè)客戶端為其發(fā)送內(nèi)容
        conn.sendText(msg);
    })
}

var server = ws.createServer(function(conn){
    console.log('New connection')
    conn.on("text",function(str){
        console.log("Received"+str)
        // conn.sendText(str.toUpperCase()+"!!!") //大寫收到的數(shù)據(jù)
        // conn.sendText(str)  //收到直接發(fā)回去
        broadcast(server,str);
    })
    ……
}).listen(PORT)

客戶端其實(shí)不用改,跑起來(lái)就是這樣的:(它不讓我傳gif.. 傳了就鎖)


多播
  • server會(huì)有多個(gè)connection术吗,每個(gè)connect代表一個(gè)客戶端尉辑,如果你想實(shí)現(xiàn)點(diǎn)對(duì)點(diǎn)的消息傳輸,標(biāo)記住connection即可~

3. 標(biāo)識(shí)用戶1對(duì)1聊天

現(xiàn)在我們要加入login功能來(lái)實(shí)現(xiàn)一對(duì)一的聊天啦~
大概步驟是醬紫的:

  • client加login功能较屿,并且在server端用login msg里面的name標(biāo)識(shí)connection
  • client加send to who的功能隧魄,用戶填寫要發(fā)送消息給誰(shuí)
  • server端根據(jù)用戶傳入消息的send to who標(biāo)識(shí),從自己connections里面找到conn隘蝎,然后send msg

server代碼大概是醬紫的:

var server = ws.createServer(function(conn){
    console.log('New connection')
    conn.on('text',function(message){
        let info=JSON.parse(message);
        if (info.type==='login') {
            conn['user']=info.user;
            console.log("login: "+info.user)
        } else if (info.type==='message'){
            server.connections.forEach(function (conn) {
                console.log("has client: "+conn['user'])
                if(conn['user']===info.to){
                    conn.send(info.message)
                    console.log("send: "+info.message)
                }
            })
            console.log("msg to: "+info.to)
        }
    })
    ……
}).listen(PORT)

然后是client html的:

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <meta http-equiv="X-UA-Compatible" content="ie=edge">
    <title>WebSocket</title>
</head>
<body>
    <h1>Echo Test</h1>

    <input id="username" type="text"/>
    <button id="loginBtn">Login</button>
    <br/>

    <input id="toUsername" type="text" placeholder="send to" />
    <br/>

    <input id="sendTxt" type="text"/>
    <button id="sendBtn">發(fā)送</button>
    <div id="recv"></div>
    <script type="text/javascript">
        // //官方示例的服務(wù)器
        // var WebSocket = new WebSocket("ws://echo.websocket.org");
        // wsServer搭建的服務(wù)器
        var WebSocket = new WebSocket("ws://localhost:3000/");
        WebSocket.onopen = function(){
            console.log('websocket open');
            document.getElementById("recv").innerHTML = "Connected";
        }
        WebSocket.onclose = function(){
            console.log('websocket close');
        }
        WebSocket.onmessage = function(e){
            console.log(e.data);
            document.getElementById("recv").innerHTML = e.data;
        }
        document.getElementById("sendBtn").onclick = function(){
            var txt = document.getElementById("sendTxt").value;
            var toUser = document.getElementById("toUsername").value;

            WebSocket.send(
                JSON.stringify({
                    type:"message",
                    to:toUser,     // 需要發(fā)送給誰(shuí)
                    message:txt,
                })
            );
        }


        document.getElementById("loginBtn").onclick = function(){
            var username = document.getElementById("username").value;
            WebSocket.send(
                JSON.stringify({
                    type:"login",
                    user:username
                })
            );
        }
    </script>
</body>
</html>

然后是演示:(不讓我傳gif碎碎念again)


一對(duì)一聊天

!

4. 心跳

websocket是前后端交互的長(zhǎng)連接购啄,前后端也都可能因?yàn)橐恍┣闆r導(dǎo)致連接失效并且相互之間沒(méi)有反饋提醒。因此為了保證連接的可持續(xù)性和穩(wěn)定性嘱么,websocket心跳重連就應(yīng)運(yùn)而生狮含。

在使用原生websocket的時(shí)候,如果設(shè)備網(wǎng)絡(luò)斷開(kāi)曼振,不會(huì)立刻觸發(fā)websocket的任何事件几迄,前端也就無(wú)法得知當(dāng)前連接是否已經(jīng)斷開(kāi)。這個(gè)時(shí)候如果調(diào)用websocket.send方法冰评,瀏覽器才會(huì)發(fā)現(xiàn)鏈接斷開(kāi)了乓旗,便會(huì)立刻或者一定短時(shí)間后(不同瀏覽器或者瀏覽器版本可能表現(xiàn)不同)觸發(fā)onclose函數(shù)。

后端websocket服務(wù)也可能出現(xiàn)異常集索,造成連接斷開(kāi)屿愚,這時(shí)前端也并沒(méi)有收到斷開(kāi)通知,因此需要前端定時(shí)發(fā)送心跳消息ping务荆,后端收到ping類型的消息妆距,立馬返回pong消息,告知前端連接正常函匕。如果一定時(shí)間沒(méi)收到pong消息娱据,就說(shuō)明連接不正常,前端便會(huì)執(zhí)行重連盅惜。

為了解決以上兩個(gè)問(wèn)題中剩,以前端作為主動(dòng)方忌穿,定時(shí)發(fā)送ping消息,用于檢測(cè)網(wǎng)絡(luò)和前后端連接問(wèn)題结啼。一旦發(fā)現(xiàn)異常掠剑,前端持續(xù)執(zhí)行重連邏輯,直到重連成功郊愧。

也就是說(shuō)前端需要做:

  • onclose/onerror的時(shí)候重連
  • 設(shè)置定時(shí)器朴译,定時(shí)給server發(fā)消息
  • 如果超時(shí)沒(méi)有拿到response,會(huì)自動(dòng)觸發(fā)onclose引發(fā)重連

服務(wù)端需要做:

  • 收到client的心跳信號(hào)以后回復(fù)

5. 客戶端接入socket

首先你需要一個(gè) pod 庫(kù)SocketRocket以及一個(gè)工具類

雖然你可能需要改一下上面那個(gè)工具類属铁,因?yàn)橛行┙涌诓挥昧嗣呤伲琤ut大多都是可以用的,現(xiàn)在你只要手機(jī)端搭建一個(gè)簡(jiǎn)單頁(yè)面就可以啦:

頁(yè)面
#import "SocketViewController.h"
#import "SocketRocketUtility.h"
#import <YYKit/NSDictionary+YYAdd.h>

@interface SocketViewController ()

@property (weak, nonatomic) IBOutlet UITextField *username;
@property (weak, nonatomic) IBOutlet UITextField *toName;
@property (weak, nonatomic) IBOutlet UITextField *msg;

@end

@implementation SocketViewController

- (void)viewDidLoad {
    [super viewDidLoad];
    // Do any additional setup after loading the view from its nib.
    [[SocketRocketUtility instance] SRWebSocketOpenWithURLString:@"ws://192.168.1.107:3000/"];
}

- (IBAction)loginClicked:(id)sender {
    NSString *username = self.username.text;
    NSDictionary *dict = @{
        @"type" : @"login",
        @"user" : username,
    };
    NSString *jsonStr = [dict jsonStringEncoded];
    [[SocketRocketUtility instance] sendString:jsonStr];
}

- (IBAction)sendClicked:(id)sender {
    NSString *toName = self.toName.text;
    NSString *msg = self.msg.text;
    NSDictionary *dict = @{
        @"type" : @"message",
        @"to" : toName,
        @"message" : msg
    };
    NSString *jsonStr = [dict jsonStringEncoded];
    [[SocketRocketUtility instance] sendString:jsonStr];
}

@end

然后現(xiàn)在打開(kāi)server焦蘑,在網(wǎng)頁(yè)和手機(jī)端都打開(kāi)client盯拱,然后這兩個(gè)都可以互相通信了。


手機(jī)發(fā)給網(wǎng)頁(yè)

好啦這周末的work比較無(wú)聊啦例嘱,只是玩兒一下websocket~ 下周可能會(huì)搞iOS or 看看書吧~

Reference:
https://blog.csdn.net/qq_20367813/article/details/78020930
websocket是啥:參考知乎https://www.zhihu.com/question/20215561
推薦關(guān)于websocket的一篇:https://blog.csdn.net/asd051377305/article/details/108066378
http 長(zhǎng)短連接和websocket:http://caibaojian.com/http-connection-and-websocket.html
聊天參考:https://blog.csdn.net/qq_41097495/article/details/105835100
心跳:https://www.cnblogs.com/1wen/p/5808276.html
iOS接入socket:http://www.reibang.com/p/821b777555d3

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末狡逢,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子蝶防,更是在濱河造成了極大的恐慌甚侣,老刑警劉巖明吩,帶你破解...
    沈念sama閱讀 206,126評(píng)論 6 481
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件间学,死亡現(xiàn)場(chǎng)離奇詭異,居然都是意外死亡印荔,警方通過(guò)查閱死者的電腦和手機(jī)低葫,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,254評(píng)論 2 382
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)仍律,“玉大人嘿悬,你說(shuō)我怎么就攤上這事∷” “怎么了善涨?”我有些...
    開(kāi)封第一講書人閱讀 152,445評(píng)論 0 341
  • 文/不壞的土叔 我叫張陵,是天一觀的道長(zhǎng)草则。 經(jīng)常有香客問(wèn)我钢拧,道長(zhǎng),這世上最難降的妖魔是什么炕横? 我笑而不...
    開(kāi)封第一講書人閱讀 55,185評(píng)論 1 278
  • 正文 為了忘掉前任源内,我火速辦了婚禮,結(jié)果婚禮上份殿,老公的妹妹穿的比我還像新娘膜钓。我一直安慰自己嗽交,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 64,178評(píng)論 5 371
  • 文/花漫 我一把揭開(kāi)白布颂斜。 她就那樣靜靜地躺著夫壁,像睡著了一般。 火紅的嫁衣襯著肌膚如雪焚鲜。 梳的紋絲不亂的頭發(fā)上掌唾,一...
    開(kāi)封第一講書人閱讀 48,970評(píng)論 1 284
  • 那天,我揣著相機(jī)與錄音忿磅,去河邊找鬼糯彬。 笑死,一個(gè)胖子當(dāng)著我的面吹牛葱她,可吹牛的內(nèi)容都是我干的撩扒。 我是一名探鬼主播,決...
    沈念sama閱讀 38,276評(píng)論 3 399
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼吨些,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼搓谆!你這毒婦竟也來(lái)了?” 一聲冷哼從身側(cè)響起豪墅,我...
    開(kāi)封第一講書人閱讀 36,927評(píng)論 0 259
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤泉手,失蹤者是張志新(化名)和其女友劉穎,沒(méi)想到半個(gè)月后偶器,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體斩萌,經(jīng)...
    沈念sama閱讀 43,400評(píng)論 1 300
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 35,883評(píng)論 2 323
  • 正文 我和宋清朗相戀三年屏轰,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了颊郎。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 37,997評(píng)論 1 333
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡霎苗,死狀恐怖姆吭,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情唁盏,我是刑警寧澤内狸,帶...
    沈念sama閱讀 33,646評(píng)論 4 322
  • 正文 年R本政府宣布,位于F島的核電站厘擂,受9級(jí)特大地震影響昆淡,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜驴党,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,213評(píng)論 3 307
  • 文/蒙蒙 一瘪撇、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦倔既、人聲如沸恕曲。這莊子的主人今日做“春日...
    開(kāi)封第一講書人閱讀 30,204評(píng)論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)佩谣。三九已至,卻和暖如春实蓬,著一層夾襖步出監(jiān)牢的瞬間茸俭,已是汗流浹背。 一陣腳步聲響...
    開(kāi)封第一講書人閱讀 31,423評(píng)論 1 260
  • 我被黑心中介騙來(lái)泰國(guó)打工安皱, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留调鬓,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 45,423評(píng)論 2 352
  • 正文 我出身青樓酌伊,卻偏偏與公主長(zhǎng)得像腾窝,于是被迫代替她去往敵國(guó)和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子居砖,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 42,722評(píng)論 2 345

推薦閱讀更多精彩內(nèi)容