目的:实现客户端与服务端的实时通讯,基于TCP协议

与keep-alive区别:keep-alive机制会连接一小段时间,最终会断开,ws协议不会断开

原理:通过一条特殊的http协议请求进行握手后,服务端支持ws协议,则进行协议升级,利用http创建的tcp连接,实现长连接。

步骤分解:

  • 连接服务器

    1
    
    final channel = IOWebSocketChannel.connect('ws://echo.websocket.org');
    
  • 关闭连接

    1
    
    channel.sink.close();
    
  • 监听服务器

    • StreamBuilder是一个组件,收到一个stream就刷新界面
    1
    2
    3
    4
    5
    6
    
    new StreamBuilder(
      stream: widget.channel.stream,
      builder: (context, snapshot) {
        return new Text(snapshot.hasData ? '${snapshot.data}' : '');
      },
    );
    
  • 发送消息

    1
    
    channel.sink.add('Hello!');
    

完整demo

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
import 'package:flutter/material.dart';
import 'package:web_socket_channel/io.dart';

class WebSocketRoute extends StatefulWidget {
  @override
  _WebSocketRouteState createState() => new _WebSocketRouteState();
}

class _WebSocketRouteState extends State<WebSocketRoute> {
  TextEditingController _controller = new TextEditingController();
  IOWebSocketChannel channel;
  String _text = "";


  @override
  void initState() {
    //创建websocket连接
    channel = new IOWebSocketChannel.connect('ws://echo.websocket.org');
  }

  @override
  Widget build(BuildContext context) {
    return new Scaffold(
      appBar: new AppBar(
        title: new Text("WebSocket(内容回显)"),
      ),
      body: new Padding(
        padding: const EdgeInsets.all(20.0),
        child: new Column(
          crossAxisAlignment: CrossAxisAlignment.start,
          children: <Widget>[
            new Form(
              child: new TextFormField(
                controller: _controller,
                decoration: new InputDecoration(labelText: 'Send a message'),
              ),
            ),
            new StreamBuilder(
              stream: channel.stream,
              builder: (context, snapshot) {
                //网络不通会走到这
                if (snapshot.hasError) {
                  _text = "网络不通...";
                } else if (snapshot.hasData) {
                  _text = "echo: "+snapshot.data;
                }
                return new Padding(
                  padding: const EdgeInsets.symmetric(vertical: 24.0),
                  child: new Text(_text),
                );
              },
            )
          ],
        ),
      ),
      floatingActionButton: new FloatingActionButton(
        onPressed: _sendMessage,
        tooltip: 'Send message',
        child: new Icon(Icons.send),
      ),
    );
  }

  void _sendMessage() {
    if (_controller.text.isNotEmpty) {
      channel.sink.add(_controller.text);
    }
  }

  @override
  void dispose() {
    channel.sink.close();
    super.dispose();
  }
}

数据类型:发送的过程中数据以frame形式存在,每一个frame都由opcode字段指定类型,客户端在收到消息时已经知道类型,会自动转换,streamBuilder.snapshot.data如果是文本,类型是string,二进制数据,类型是List,还有其他类型

参考:https://book.flutterchina.club/chapter11/websocket.html