[Common] 聊天front-backend简单实现

这又是非serious系列的简单尝试0.0 最近大概只有买买买能让我内心得到平静... 这篇的主题其实是长连接哈~ 不喜欢的朋友可以跳过啦~

1. 搭建服务器

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

npm install nodejs-websocket

然后你按照下面reference里面第一篇的方法就可以写个server以及client html啦,启动server就用node xxx.js即可。client就直接双击打开html他自己就会连接你启动的server(localhost:3000)。

那么 websocket 是啥呢?
Websocket是一个持久化的协议,相对于HTTP这种非持久的协议来说。

HTTP通常是有个request,然后立刻server就会给你response,但是server不能主动发起给客户端传输数据,必须要有request。即使是long poll 和 ajax轮询 其实本质还是需要client先发request。

WebSocket 解决的第一个问题是,通过第一个 HTTP request 建立了 TCP 连接之后,之后的交换数据都不需要再发 HTTP request了,使得这个长连接变成了一个真长连接
在此基础上 WebSocket 还是一个双通道的连接,在同一个 TCP 连接上既可以发也可以收信息。此外还有 multiplexing 功能,几个不同的 URI 可以复用同一个 WebSocket 连接。这些都是原来的 HTTP 不能做到的。

image.png

看起来是不是特别适合聊天场景!其实我本来想写个直播的消息推送的,现在决定还是写个聊天app吧~ (善变的女人...

2. 实现多播

首先先做一个简单地修改,让多个客户端(这里指网页哈)可以连到server,然后一个客户端的消息会广播给所有客户端。

其实改动很小哦,server端加一个广播函数调用就可以啦:

function broadcast(server, msg) {
    //server.connections是一个数组,包含所有连接进来的客户端
    server.connections.forEach(function (conn) {
        //connection.sendText方法可以发送指定的内容到客户端,传入一个字符串
        //这里为遍历每一个客户端为其发送内容
        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()+"!!!") //大写收到的数据
        // conn.sendText(str)  //收到直接发回去
        broadcast(server,str);
    })
    ……
}).listen(PORT)

客户端其实不用改,跑起来就是这样的:(它不让我传gif.. 传了就锁)


多播
  • server会有多个connection,每个connect代表一个客户端,如果你想实现点对点的消息传输,标记住connection即可~

3. 标识用户1对1聊天

现在我们要加入login功能来实现一对一的聊天啦~
大概步骤是酱紫的:

  • client加login功能,并且在server端用login msg里面的name标识connection
  • client加send to who的功能,用户填写要发送消息给谁
  • server端根据用户传入消息的send to who标识,从自己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的:




    
    
    
    WebSocket


    

Echo Test



然后是演示:(不让我传gif碎碎念again)


一对一聊天

!

4. 心跳

websocket是前后端交互的长连接,前后端也都可能因为一些情况导致连接失效并且相互之间没有反馈提醒。因此为了保证连接的可持续性和稳定性,websocket心跳重连就应运而生。

在使用原生websocket的时候,如果设备网络断开,不会立刻触发websocket的任何事件,前端也就无法得知当前连接是否已经断开。这个时候如果调用websocket.send方法,浏览器才会发现链接断开了,便会立刻或者一定短时间后(不同浏览器或者浏览器版本可能表现不同)触发onclose函数。

后端websocket服务也可能出现异常,造成连接断开,这时前端也并没有收到断开通知,因此需要前端定时发送心跳消息ping,后端收到ping类型的消息,立马返回pong消息,告知前端连接正常。如果一定时间没收到pong消息,就说明连接不正常,前端便会执行重连

为了解决以上两个问题,以前端作为主动方,定时发送ping消息,用于检测网络和前后端连接问题。一旦发现异常,前端持续执行重连逻辑,直到重连成功。

也就是说前端需要做:

  • onclose/onerror的时候重连
  • 设置定时器,定时给server发消息
  • 如果超时没有拿到response,会自动触发onclose引发重连

服务端需要做:

  • 收到client的心跳信号以后回复

5. 客户端接入socket

首先你需要一个 pod 库SocketRocket以及一个工具类

虽然你可能需要改一下上面那个工具类,因为有些接口不用了,but大多都是可以用的,现在你只要手机端搭建一个简单页面就可以啦:

页面
#import "SocketViewController.h"
#import "SocketRocketUtility.h"
#import 

@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

然后现在打开server,在网页和手机端都打开client,然后这两个都可以互相通信了。


手机发给网页

好啦这周末的work比较无聊啦,只是玩儿一下websocket~ 下周可能会搞iOS or 看看书吧~

Reference:
https://blog.csdn.net/qq_20367813/article/details/78020930
websocket是啥:参考知乎https://www.zhihu.com/question/20215561
推荐关于websocket的一篇:https://blog.csdn.net/asd051377305/article/details/108066378
http 长短连接和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:https://www.jianshu.com/p/821b777555d3

你可能感兴趣的:([Common] 聊天front-backend简单实现)