e2

滴滴侠,fai抖

  博客园 :: 首页 :: 博问 :: 闪存 :: 新随笔 :: 联系 :: 订阅 订阅 :: 管理 ::

最近有一个这样的功能场景。用户操作完成后。服务器主动通知另一个客户端显示结果。

这里涉及一个服务器推的这么一个东西。需要实现这么一个功能,对比了几个实现方式。最终选择了socket.io。

1、commet,最初想到这个功能要求不高,想简单的通过commet方式实现就算了。但考虑到commet已经是比较老旧的做法了,其中有很多弊端,所以放弃了。

2、netty。netty是非常棒的java nio框架。但考虑到我需要实现的功能,有点杀鸡用牛刀的感觉,而且要兼顾小程序的实现,选择放弃

3、socket.io,非常适合。封装了socket的特性,用法简单。而且对不同语言的兼容性很好。

 

废话好说。直接看代码。

1、服务器端,使用的是java语言。

首先添加jar支持,java上服务器端用的是netty-socketio这个jar,是对netty做的二次封装。

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

java服务器端的实现:

  1.  
    /*
  2.  
    * 文件名:SocketIoServer.java 版权:Copyright by www.poly.com 描述: 修改人:gogym 修改时间:2018年10月23日 跟踪单号: 修改单号:
  3.  
    * 修改内容:
  4.  
    */
  5.  
     
  6.  
     
  7.  
    import java.util.HashMap;
  8.  
    import java.util.Map;
  9.  
     
  10.  
    import com.corundumstudio.socketio.AckCallback;
  11.  
    import com.corundumstudio.socketio.AckRequest;
  12.  
    import com.corundumstudio.socketio.AuthorizationListener;
  13.  
    import com.corundumstudio.socketio.Configuration;
  14.  
    import com.corundumstudio.socketio.HandshakeData;
  15.  
    import com.corundumstudio.socketio.SocketIOClient;
  16.  
    import com.corundumstudio.socketio.SocketIONamespace;
  17.  
    import com.corundumstudio.socketio.SocketIOServer;
  18.  
    import com.corundumstudio.socketio.listener.ConnectListener;
  19.  
    import com.corundumstudio.socketio.listener.DataListener;
  20.  
    import com.corundumstudio.socketio.listener.DisconnectListener;
  21.  
    import com.corundumstudio.socketio.listener.PingListener;
  22.  
     
  23.  
     
  24.  
    public class SocketIoServer
  25.  
    {
  26.  
     
  27.  
    public static void main(String[] args)
  28.  
    {
  29.  
     
  30.  
    // 启动服务
  31.  
    SocketIoServer sio = new SocketIoServer();
  32.  
    sio.initSocket();
  33.  
     
  34.  
    }
  35.  
     
  36.  
    // 保存session与端映射关系
  37.  
    public Map<String, SocketIOClient> clientMap = new HashMap<String, SocketIOClient>();
  38.  
     
  39.  
    /**
  40.  
    * Description: 初始化
  41.  
    *
  42.  
    * @see
  43.  
    */
  44.  
    public void initSocket()
  45.  
    {
  46.  
     
  47.  
    Configuration config = new Configuration();
  48.  
    // 一般不需要设置Hostname,设置localhost后,从其他IP连接会连不上来
  49.  
    // config.setHostname("localhost");
  50.  
    // 设置端口
  51.  
    config.setPort(10015);
  52.  
    // 往服务器写ping消息,用来检测客户端是否存活,如果设置时间内没有ping通,则清理掉客户端连接
  53.  
    config.setPingTimeout(3000);
  54.  
    // ping间隔时长
  55.  
    config.setPingInterval(30000);
  56.  
    // config.setBossThreads(5);
  57.  
    // config.setWorkerThreads(15);
  58.  
     
  59.  
    config.setAuthorizationListener(new AuthorizationListener()
  60.  
    {
  61.  
    @Override
  62.  
    public boolean isAuthorized(HandshakeData handshakedata)
  63.  
    {
  64.  
    // 这里可以拦截连接过来的URL。可以获取参数等。比如统一鉴权就可以在这里处理
  65.  
    System.out.println(handshakedata.getUrl());
  66.  
    return true;
  67.  
    }
  68.  
    });
  69.  
     
  70.  
    SocketIOServer server = new SocketIOServer(config);
  71.  
     
  72.  
    // 添加一个命名空间,命名空间可以用于区分不同的业务连接
  73.  
    SocketIONamespace namespace = server.addNamespace("/chat");
  74.  
     
  75.  
    server.start();
  76.  
    }
  77.  
     
  78.  
    /**
  79.  
    * Description: 添加监听程序
  80.  
    *
  81.  
    * @param namespace
  82.  
    * @see
  83.  
    */
  84.  
    public void addListener(SocketIONamespace namespace)
  85.  
    {
  86.  
     
  87.  
    // 添加连接监听
  88.  
    namespace.addConnectListener(new ConnectListener()
  89.  
    {
  90.  
    @Override
  91.  
    public void onConnect(SocketIOClient client)
  92.  
    {
  93.  
    System.out.println(client.getRemoteAddress() + "连接上了");
  94.  
    // 把连接上的客户端保存起来
  95.  
    clientMap.put(client.getSessionId().toString(), client);
  96.  
     
  97.  
    }
  98.  
    });
  99.  
     
  100.  
    // 添加连接断开监听
  101.  
    namespace.addDisconnectListener(new DisconnectListener()
  102.  
    {
  103.  
    @Override
  104.  
    public void onDisconnect(SocketIOClient client)
  105.  
    {
  106.  
    System.out.println(client.getRemoteAddress() + "离开了");
  107.  
    clientMap.remove(client.getSessionId().toString());
  108.  
    }
  109.  
    });
  110.  
     
  111.  
    // 添加ping监听
  112.  
    namespace.addPingListener(new PingListener()
  113.  
    {
  114.  
    @Override
  115.  
    public void onPing(SocketIOClient socketioclient)
  116.  
    {
  117.  
    // 往客户端写ping消息,用来检测客户端是否存活
  118.  
    System.out.println("ping:" + socketioclient.getRemoteAddress());
  119.  
    }
  120.  
    });
  121.  
     
  122.  
    // 添加自定义监听,例如:用户注册
  123.  
    namespace.addEventListener("register_event", String.class, new DataListener<String>()
  124.  
    {
  125.  
     
  126.  
    @Override
  127.  
    public void onData(SocketIOClient client, String str, AckRequest ack)
  128.  
    throws Exception
  129.  
    {
  130.  
    // 发送ack给客户端告知服务器端已经收到消息
  131.  
    ack.sendAckData("ok");
  132.  
    }
  133.  
    });
  134.  
     
  135.  
    }
  136.  
     
  137.  
    public void sendMsg(String sessionId, String msg)
  138.  
    {
  139.  
     
  140.  
    // 通过sessionid获取socket连接
  141.  
    SocketIOClient client = clientMap.get(sessionId);
  142.  
     
  143.  
    // 发条消息给客户端。客户端监听的是message
  144.  
    client.sendEvent("message", new AckCallback<Object>(Object.class, 5 /* 注意这里的单位是秒 */)
  145.  
    {
  146.  
    @Override
  147.  
    public void onSuccess(Object result)
  148.  
    {
  149.  
    // 接收客户端返回的ack。意味着客户端已经接收到消息了
  150.  
    System.out.println("ack from client: " + client.getSessionId() + " data: "
  151.  
    + result);
  152.  
    }
  153.  
     
  154.  
    @Override
  155.  
    public void onTimeout()
  156.  
    {
  157.  
    System.out.println("ACK超时");
  158.  
    }
  159.  
     
  160.  
    }, msg);
  161.  
     
  162.  
    }
  163.  
     
  164.  
    }

JAVA客户端的实现

  1.  
    /*
  2.  
    * 文件名:SocketIoClient.java 版权:Copyright by www.poly.com 描述: 修改人:gogym 修改时间:2018年10月23日 跟踪单号: 修改单号:
  3.  
    * 修改内容:
  4.  
    */
  5.  
     
  6.  
    package com.poly.rbl.plugin.socketio;
  7.  
     
  8.  
     
  9.  
    import io.socket.client.Ack;
  10.  
    import io.socket.client.IO;
  11.  
    import io.socket.client.Socket;
  12.  
    import io.socket.emitter.Emitter;
  13.  
     
  14.  
    import com.poly.rbl.utils.DateTimeUtil;
  15.  
     
  16.  
     
  17.  
    public class SocketIoClient
  18.  
    {
  19.  
     
  20.  
    public static void main(String[] args)
  21.  
    {
  22.  
     
  23.  
    IO.Options options = new IO.Options();
  24.  
    // 设置协议为websocket
  25.  
    options.transports = new String[] {"websocket"};
  26.  
    // 失败重连次数
  27.  
    options.reconnectionAttempts = 5;
  28.  
    // 失败重连的时间间隔
  29.  
    options.reconnectionDelay = 3000;
  30.  
    // 连接超时时间(ms)
  31.  
    options.timeout = 3000;
  32.  
    // 开启重连
  33.  
    options.reconnection = true;
  34.  
    // 可以携带一些连接参数
  35.  
    // options.query="token=1234";
  36.  
     
  37.  
    try
  38.  
    {
  39.  
    final Socket socket = IO.socket("http://app.52rbl.com:8004/delivery", options);
  40.  
    // 连接服务器
  41.  
    socket.on(Socket.EVENT_CONNECT, new Emitter.Listener()
  42.  
    {
  43.  
    @Override
  44.  
    public void call(Object... args)
  45.  
    {
  46.  
    System.out.println(DateTimeUtil.getCurrentTime() + ":client connect! ");
  47.  
     
  48.  
    // 连接成功,马上向服务器发送信息,例如:用户注册
  49.  
    socket.emit("register_event", "发给服务器的消息", new Ack()
  50.  
    {
  51.  
    @Override
  52.  
    public void call(Object... aobj)
  53.  
    {
  54.  
    System.out.println(aobj[0]);
  55.  
    }
  56.  
    });
  57.  
    }
  58.  
    });
  59.  
     
  60.  
    // 监听断线
  61.  
    socket.on(Socket.EVENT_DISCONNECT, new Emitter.Listener()
  62.  
    {
  63.  
    @Override
  64.  
    public void call(Object... args)
  65.  
    {
  66.  
    System.out.println(DateTimeUtil.getCurrentTime() + ":client disconnect!");
  67.  
    }
  68.  
    });
  69.  
     
  70.  
    // 监听ping,其实这个监听没有多少意义
  71.  
    socket.on(Socket.EVENT_PING, new Emitter.Listener()
  72.  
    {
  73.  
    @Override
  74.  
    public void call(Object... arg0)
  75.  
    {
  76.  
    // 往服务器写ping
  77.  
    System.out.println("ping:" + arg0);
  78.  
    }
  79.  
    });
  80.  
     
  81.  
    // 监听pong,这个监听没有多少意义
  82.  
    socket.on(Socket.EVENT_PONG, new Emitter.Listener()
  83.  
    {
  84.  
    @Override
  85.  
    public void call(Object... arg0)
  86.  
    {
  87.  
    // ping了以后接收服务器的pong响应
  88.  
    System.out.println("pong:" + arg0[0]);
  89.  
    }
  90.  
    });
  91.  
     
  92.  
    // 监听服务器发送的消息,也可以自定义一个简单字符串
  93.  
    socket.on(Socket.EVENT_MESSAGE, new Emitter.Listener()
  94.  
    {
  95.  
    @Override
  96.  
    public void call(Object... args)
  97.  
    {
  98.  
    // 接收消息后,往服务器发送一个ack,告诉服务器消息收到了
  99.  
    Ack ack = (Ack)args[args.length - 1];
  100.  
    ack.call(args[0].hashCode());
  101.  
     
  102.  
    for (Object obj : args)
  103.  
    {
  104.  
    System.out.println(DateTimeUtil.getCurrentTime()
  105.  
    + ":receive server message=" + obj.toString());
  106.  
    }
  107.  
    }
  108.  
    });
  109.  
     
  110.  
    // 连接服务器
  111.  
    socket.connect();
  112.  
    }
  113.  
    catch (Exception e)
  114.  
    {
  115.  
    e.printStackTrace();
  116.  
    }
  117.  
    }
  118.  
     
  119.  
    }

小程序上也可以使用,虽然小程序有提供websocket的支持,但并不支持socket.io。如果想要在小程序上使用,需要添加一个小程序上的专用库:weapp.socket.io    https://github.com/10cella/weapp.socket.io

  1.  
    var socket = io('http://192.168.1.11:8003/', {
  2.  
    transports: ['websocket']
  3.  
    });
  4.  
     
  5.  
    //连接监听
  6.  
    socket.on('connect', () => {
  7.  
    console.log("成功")
  8.  
     
  9.  
    //向服务器发送注册信息
  10.  
    socket.emit('register_event', params, (data) => {
  11.  
    console.log(data)
  12.  
     
  13.  
    });
  14.  
     
  15.  
    })
  16.  
     
  17.  
    socket.on('connect_error', d => {
  18.  
    console.log("connect_error")
  19.  
    })
  20.  
     
  21.  
    socket.on('connect_timeout', d => {
  22.  
    console.log("connect_timeout")
  23.  
    })
  24.  
     
  25.  
    socket.on('disconnect', reason => {
  26.  
    console.log("disconnect")
  27.  
    })
  28.  
     
  29.  
    socket.on('reconnect', attemptNumber => {
  30.  
    console.log("reconnect")
  31.  
    })
  32.  
     
  33.  
    socket.on('reconnect_attempt', () => {
  34.  
    socket.io.opts.transports = ['polling', 'websocket'];
  35.  
    });
  36.  
     
  37.  
    //监听消息
  38.  
    socket.on('message', (data,cb) => {
  39.  
    console.log(data);
  40.  
    //返回ACK给服务器
  41.  
    cb("ok")
  42.  
    });

 

posted on 2020-05-24 20:29  纯黑Se丶  阅读(1115)  评论(0编辑  收藏  举报