1. 程式人生 > >socket.io 在java與微信小程式上的應用

socket.io 在java與微信小程式上的應用

最近有一個這樣的功能場景。使用者操作完成後。伺服器主動通知另一個客戶端顯示結果。

這裡涉及一個伺服器推的這麼一個東西。需要實現這麼一個功能,對比了幾個實現方式。最終選擇了socket.io。

1、commet,最初想到這個功能要求不高,想簡單的通過commet方式實現就算了。但考慮到commet已經是比較老舊的做法了,其中有很多弊端,所以放棄了。

2、netty。netty是非常棒的java nio框架。但考慮到我需要實現的功能,有點殺雞用牛刀的感覺,而且要兼顧小程式的實現,選擇放棄

3、socket.io,非常適合。封裝了socket的特性,用法簡單。而且對不同語言的相容性很好。

廢話好說。直接看程式碼。

1、伺服器端,使用的是java語言。

首先新增jar支援,java上伺服器端用的是netty-socketio這個jar,是對netty做的二次封裝。

        <dependency>
			<groupId>com.corundumstudio.socketio</groupId>
			<artifactId>netty-socketio</artifactId>
			<version>1.7.16</version>
		</dependency>

java伺服器端的實現:

/*
 * 檔名:SocketIoServer.java 版權:Copyright by www.poly.com 描述: 修改人:gogym 修改時間:2018年10月23日 跟蹤單號: 修改單號:
 * 修改內容:
 */


import java.util.HashMap;
import java.util.Map;

import com.corundumstudio.socketio.AckCallback;
import com.corundumstudio.socketio.AckRequest;
import com.corundumstudio.socketio.AuthorizationListener;
import com.corundumstudio.socketio.Configuration;
import com.corundumstudio.socketio.HandshakeData;
import com.corundumstudio.socketio.SocketIOClient;
import com.corundumstudio.socketio.SocketIONamespace;
import com.corundumstudio.socketio.SocketIOServer;
import com.corundumstudio.socketio.listener.ConnectListener;
import com.corundumstudio.socketio.listener.DataListener;
import com.corundumstudio.socketio.listener.DisconnectListener;
import com.corundumstudio.socketio.listener.PingListener;


public class SocketIoServer
{

    public static void main(String[] args)
    {

        // 啟動服務
        SocketIoServer sio = new SocketIoServer();
        sio.initSocket();

    }

    // 儲存session與端對映關係
    public Map<String, SocketIOClient> clientMap = new HashMap<String, SocketIOClient>();

    /**
     * Description: 初始化
     * 
     * @see
     */
    public void initSocket()
    {

        Configuration config = new Configuration();
        // 一般不需要設定Hostname,設定localhost後,從其他IP連線會連不上來
        // config.setHostname("localhost");
        // 設定埠
        config.setPort(10015);
        // 往伺服器寫ping訊息,用來檢測客戶端是否存活,如果設定時間內沒有ping通,則清理掉客戶端連線
        config.setPingTimeout(3000);
        // ping間隔時長
        config.setPingInterval(30000);
        // config.setBossThreads(5);
        // config.setWorkerThreads(15);

        config.setAuthorizationListener(new AuthorizationListener()
        {
            @Override
            public boolean isAuthorized(HandshakeData handshakedata)
            {
                // 這裡可以攔截連線過來的URL。可以獲取引數等。比如統一鑑權就可以在這裡處理
                System.out.println(handshakedata.getUrl());
                return true;
            }
        });

        SocketIOServer server = new SocketIOServer(config);

        // 新增一個名稱空間,名稱空間可以用於區分不同的業務連線
        SocketIONamespace namespace = server.addNamespace("/chat");

        server.start();
    }

    /**
     * Description: 新增監聽程式
     * 
     * @param namespace
     * @see
     */
    public void addListener(SocketIONamespace namespace)
    {

        // 新增連線監聽
        namespace.addConnectListener(new ConnectListener()
        {
            @Override
            public void onConnect(SocketIOClient client)
            {
                System.out.println(client.getRemoteAddress() + "連線上了");
                // 把連線上的客戶端儲存起來
                clientMap.put(client.getSessionId().toString(), client);

            }
        });

        // 新增連線斷開監聽
        namespace.addDisconnectListener(new DisconnectListener()
        {
            @Override
            public void onDisconnect(SocketIOClient client)
            {
                System.out.println(client.getRemoteAddress() + "離開了");
                clientMap.remove(client.getSessionId().toString());
            }
        });

        // 新增ping監聽
        namespace.addPingListener(new PingListener()
        {
            @Override
            public void onPing(SocketIOClient socketioclient)
            {
                // 往客戶端寫ping訊息,用來檢測客戶端是否存活
                System.out.println("ping:" + socketioclient.getRemoteAddress());
            }
        });

        // 新增自定義監聽,例如:使用者註冊
        namespace.addEventListener("register_event", String.class, new DataListener<String>()
        {

            @Override
            public void onData(SocketIOClient client, String str, AckRequest ack)
                throws Exception
            {
                // 傳送ack給客戶端告知伺服器端已經收到訊息
                ack.sendAckData("ok");
            }
        });

    }

    public void sendMsg(String sessionId, String msg)
    {

        // 通過sessionid獲取socket連線
        SocketIOClient client = clientMap.get(sessionId);

        // 發條訊息給客戶端。客戶端監聽的是message
        client.sendEvent("message", new AckCallback<Object>(Object.class, 5 /* 注意這裡的單位是秒 */)
        {
            @Override
            public void onSuccess(Object result)
            {
                // 接收客戶端返回的ack。意味著客戶端已經接收到訊息了
                System.out.println("ack from client: " + client.getSessionId() + " data: "
                                   + result);
            }

            @Override
            public void onTimeout()
            {
                System.out.println("ACK超時");
            }

        }, msg);

    }

}

JAVA客戶端的實現

/*
 * 檔名:SocketIoClient.java 版權:Copyright by www.poly.com 描述: 修改人:gogym 修改時間:2018年10月23日 跟蹤單號: 修改單號:
 * 修改內容:
 */

package com.poly.rbl.plugin.socketio;


import io.socket.client.Ack;
import io.socket.client.IO;
import io.socket.client.Socket;
import io.socket.emitter.Emitter;

import com.poly.rbl.utils.DateTimeUtil;


public class SocketIoClient
{

    public static void main(String[] args)
    {

        IO.Options options = new IO.Options();
        // 設定協議為websocket
        options.transports = new String[] {"websocket"};
        // 失敗重連次數
        options.reconnectionAttempts = 5;
        // 失敗重連的時間間隔
        options.reconnectionDelay = 3000;
        // 連線超時時間(ms)
        options.timeout = 3000;
        // 開啟重連
        options.reconnection = true;
        // 可以攜帶一些連線引數
        // options.query="token=1234";

        try
        {
            final Socket socket = IO.socket("http://app.52rbl.com:8004/delivery", options);
            // 連線伺服器
            socket.on(Socket.EVENT_CONNECT, new Emitter.Listener()
            {
                @Override
                public void call(Object... args)
                {
                    System.out.println(DateTimeUtil.getCurrentTime() + ":client connect! ");

                    // 連線成功,馬上向伺服器傳送資訊,例如:使用者註冊
                    socket.emit("register_event", "發給伺服器的訊息", new Ack()
                    {
                        @Override
                        public void call(Object... aobj)
                        {
                            System.out.println(aobj[0]);
                        }
                    });
                }
            });

            // 監聽斷線
            socket.on(Socket.EVENT_DISCONNECT, new Emitter.Listener()
            {
                @Override
                public void call(Object... args)
                {
                    System.out.println(DateTimeUtil.getCurrentTime() + ":client disconnect!");
                }
            });

            // 監聽ping,其實這個監聽沒有多少意義
            socket.on(Socket.EVENT_PING, new Emitter.Listener()
            {
                @Override
                public void call(Object... arg0)
                {
                    // 往伺服器寫ping
                    System.out.println("ping:" + arg0);
                }
            });

            // 監聽pong,這個監聽沒有多少意義
            socket.on(Socket.EVENT_PONG, new Emitter.Listener()
            {
                @Override
                public void call(Object... arg0)
                {
                    // ping了以後接收伺服器的pong響應
                    System.out.println("pong:" + arg0[0]);
                }
            });

            // 監聽伺服器傳送的訊息,也可以自定義一個簡單字串
            socket.on(Socket.EVENT_MESSAGE, new Emitter.Listener()
            {
                @Override
                public void call(Object... args)
                {
                    // 接收訊息後,往伺服器傳送一個ack,告訴伺服器訊息收到了
                    Ack ack = (Ack)args[args.length - 1];
                    ack.call(args[0].hashCode());

                    for (Object obj : args)
                    {
                        System.out.println(DateTimeUtil.getCurrentTime()
                                           + ":receive server message=" + obj.toString());
                    }
                }
            });

            // 連線伺服器
            socket.connect();
        }
        catch (Exception e)
        {
            e.printStackTrace();
        }
    }

}
 var socket = io('http://192.168.1.11:8003/', {
      transports: ['websocket']
    });

    //連線監聽
    socket.on('connect', () => {
      console.log("成功")
        
      //向伺服器傳送註冊資訊
      socket.emit('register_event', params, (data) => {
        console.log(data)
       
      });

    })

    socket.on('connect_error', d => {
      console.log("connect_error")
    })

    socket.on('connect_timeout', d => {
      console.log("connect_timeout")
    })

    socket.on('disconnect', reason => {
      console.log("disconnect")
    })

    socket.on('reconnect', attemptNumber => {
      console.log("reconnect")
    })

    socket.on('reconnect_attempt', () => {
      socket.io.opts.transports = ['polling', 'websocket'];
    });

    //監聽訊息
    socket.on('message', (data,cb) => {
      console.log(data);
     //返回ACK給伺服器
     cb("ok")
    });