参考:基于netty-socketio的web推送服务Spring Boot实战之netty-socketio实现简单聊天室(给指定用户推送消息)socket.io 中文文档

socketio简介
Socket.io是一个WebSocket库,包括了客户端的js和服务器端的nodejs,它的目标是构建可以在不同浏览器和移动设备上使用的实时应用。它会自动根据浏览器从WebSocket、AJAX长轮询、Iframe流等等各种方式中选择最佳的方式来实现网络实时应用,非常方便和人性化,而且支持的浏览器最低达IE5.5.

socket.io特点
实时分析:将数据推送到客户端,这些客户端会被表示为实时计数器,图表或日志客户。
实时通信和聊天:只需几行代码便可写成一个Socket.IO的”Hello,World”聊天应用。
二进制流传输:从1.0版本开始,Socket.IO支持任何形式的二进制文件传输,例如:图片,视频,音频等。
文档合并:允许多个用户同时编辑一个文档,并且能够看到每个用户做出的修改。

Demo简介

服务端使用netty-socketio,客户端使用socket.io.js。
本例完全来自上面的链接,这里只是测试效果以及做备忘。本例实现的功能是一个用户向另外一个用法发小消息。

maven中添加依赖

1
2
3
4
5
<dependency>
  <groupId>com.corundumstudio.socketio</groupId>
  <artifactId>netty-socketio</artifactId>
  <version>1.7.3</version>
</dependency>

服务端SocketServer

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
@Component
public class ChatServer implements InitializingBean {
    @Autowired
    private EventListenner eventListenner;
    @Override
    public void afterPropertiesSet() throws Exception {
        Configuration config = new Configuration();
        config.setPort(9098);
        SocketConfig socketConfig = new SocketConfig();
        socketConfig.setReuseAddress(true);
        socketConfig.setTcpNoDelay(true);
        socketConfig.setSoLinger(0);
        config.setSocketConfig(socketConfig);
        config.setHostname("localhost");
        SocketIOServer server = new SocketIOServer(config);
        server.addListeners(eventListenner);
        server.start();
        System.out.println("启动正常");
    }
}

缓存类,缓存客户端连接

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
@Component("clientCache")
public class SocketIOClientCache {
    //String:EventType类型
    private Map<String,SocketIOClient> clients=new ConcurrentHashMap<String,SocketIOClient>();
    //用户发送消息添加
    public void addClient(SocketIOClient client,MsgBean msgBean){
        clients.put(msgBean.getFrom(),client);
    }
    //用户退出时移除
    public void remove(MsgBean msgBean) {
        clients.remove(msgBean.getFrom());
    }
    //获取所有
    public  SocketIOClient getClient(String to) {
        return clients.get(to);
    }
}

消息发送的类

1
2
3
4
5
6
7
@Service("socketIOResponse")
public class SocketIOResponse {
    public void sendEvent(SocketIOClient client, MsgBean bean) {
        System.out.println("推送消息");
        client.sendEvent("OnMSG", bean);
    }
}

事件监听器

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
@Service("eventListenner")
public class EventListenner {
    @Resource(name = "clientCache")
    private SocketIOClientCache clientCache;
    @Resource(name = "socketIOResponse")
    private SocketIOResponse socketIOResponse;
    @OnConnect
    public void onConnect(SocketIOClient client) {
       System.out.println("建立连接");
    }
    @OnEvent("OnMSG")
    public void onSync(SocketIOClient client, MsgBean bean) {
        System.out.printf("收到消息-from: %s to:%s\n", bean.getFrom(), bean.getTo());
        clientCache.addClient(client, bean);
        SocketIOClient ioClients = clientCache.getClient(bean.getTo());
        System.out.println("clientCache");
        if (ioClients == null) {
            System.out.println("你发送消息的用户不在线");
            return;
        }
        socketIOResponse.sendEvent(ioClients,bean);
    }
    @OnDisconnect
    public void onDisconnect(SocketIOClient client) {
        System.out.println("关闭连接");
    }
}

消息bean

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
public class MsgBean { 
    private String from;
    private String to;
    private String content;
    public String getFrom() {
        return from;
    }
    public void setFrom(String from) {
        this.from = from;
    }
    public String getTo() {
        return to;
    }
    public void setTo(String to) {
        this.to = to;
    }
    public String getContent() {
        return content;
    }
    public void setContent(String content) {
        this.content = content;
    }
    @Override
    public String toString() {
        return "MsgBean [from=" + from + ", to=" + to + ", content=" + content + "]";
    }
}

页面

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
<html xmlns="http://www.w3.org/1999/xhtml" xmlns:th="http://www.thymeleaf.org">
<head>
    <title>socket.io demo</title>
    <meta http-equiv="content-type" content="text/html; charset=UTF-8" />
    <script type="text/javascript" th:src="@{/js/jquery.js}"></script>
    <script type="text/javascript" th:src="@{/js/socket.io.min.js}"></script>
    <style>
        body {
            padding: 20px;
        }
        #console {
            height: 400px;
            overflow: auto;
        }
        .username-msg {
            color: orange;
        }
        .connect-msg {
            color: green;
        }
        .disconnect-msg {
            color: red;
        }
        .send-msg {
            color: #888
        }
    </style>
</head>
<body>
<h1>Netty-socketio chat demo</h1>
<br />
<div id="console" class="well"></div>
<form class="well form-inline" onsubmit="return false;">
    <input id="from" class="input-xlarge" type="text" placeholder="from. . . " />
    <input id="to" class="input-xlarge" type="text" placeholder="to. . . " />
    <input id="content" class="input-xlarge" type="text" placeholder="content. . . " />
    <button type="button" onClick="sendMessage()" class="btn">Send</button>
    <button type="button" onClick="sendDisconnect()" class="btn">Disconnect</button>
</form>
</body>
<script type="text/javascript">
    var socket = io.connect('http://localhost:9098');
    socket.on('connect',function() {
        output('<span class="connect-msg">Client has connected to the server!</span>');
    });
    socket.on('OnMSG', function(data) {
        output('<span class="username-msg">' + data.content + ' : </span>');
    });
    socket.on('disconnect',function() {
        output('<span class="disconnect-msg">The client has disconnected! </span>');
    });
    function sendDisconnect() {
        socket.disconnect();
    }
    function sendMessage() {
        var from = $("#from").val();
        var to = $("#to").val();
        var content = $('#content').val();
        socket.emit('OnMSG', {
            from : from,
            to : to,
            content : content
        });
    }
    function output(message) {
        console.log(message)
        var currentTime = "<span>" + new Date() + "</span>";
        var element = $("<div>" + currentTime + " " + message + "</div><br/>");
        $('#console').prepend(element);
    }
</script>
</html>

说明:工程基于springboot,关于springboot相关的内容并未给出。

测试

浏览器2个标签页分页输入地址,进入到聊天页面。
标签页1

我们假定标签页1用户为admin,标签页2用户为test。
标签页1:admin向test发送1条消息,点击发送,切换到标签页2,发现并没有消息。因为这个时候缓存中没有test,所以没有发送成功。
标签页2:test向admin发送1条消息。

切换到标签页1,可以发现已经接受到test发送的消息

我们F12打开开发人员工具,可以看到多次发送消息并没有产生新的请求。

问题

1.中文乱码
尚未找到解决方案。

netty-socketio:https://github.com/mrniko/netty-socketio
netty-socketio-demo:https://github.com/mrniko/netty-socketio-demo