WebSocket实现android消息推送

WebSocket实现android消息推送

WebSocket是HTML5出的协议,基于TCP。它实现了浏览器与服务器全双工(full-duplex)通信——允许服务器主动发送信息给客户端。

WebSocket协议之前,双工通信是通过多个http链接来实现,这导致了效率低下。WebSocket解决了这个问题。

有了websocket协议,服务器就可以主动给客户端发送消息,而不是要等客户端的轮询。

实现一个简单的Websocket服务器


#-*- coding:utf8 -*-

import threading
import hashlib
import socket
import base64

global clients
clients = {}

#通知客户端
def notify(message):
    for connection in clients.values():
        connection.send('%c%c%s' % (0x81, len(message), message))

#客户端处理线程
class websocket_thread(threading.Thread):
    def __init__(self, connection, username):
        super(websocket_thread, self).__init__()
        self.connection = connection
        self.username = username

    def run(self):
        print 'new websocket client joined!'
        data = self.connection.recv(1024)
        headers = self.parse_headers(data)
        token = self.generate_token(headers['Sec-WebSocket-Key'])
        self.connection.send('\
HTTP/1.1 101 WebSocket Protocol Hybi-10\r\n\
Upgrade: WebSocket\r\n\
Connection: Upgrade\r\n\
Sec-WebSocket-Accept: %s\r\n\r\n' % token)
        while True:
            try:
                data = self.connection.recv(1024)
            except socket.error, e:
                print "unexpected error: ", e
                clients.pop(self.username)
                break
            data = self.parse_data(data)
            if len(data) == 0:
                continue
            message = "来自服务端的消息: " + data
            notify(message)

    def parse_data(self, msg):

        v = ord(msg[1]) & 0x7f
        if v == 0x7e:
            p = 4
        elif v == 0x7f:
            p = 10
        else:
            p = 2
        mask = msg[p:p+4]
        data = msg[p+4:]
        return ''.join([chr(ord(v) ^ ord(mask[k%4])) for k, v in enumerate(data)])

    def parse_headers(self, msg):
        headers = {}
        header, data = msg.split('\r\n\r\n', 1)
        for line in header.split('\r\n')[1:]:
            key, value = line.split(': ', 1)
            headers[key] = value
        headers['data'] = data
        return headers

    def generate_token(self, msg):
        key = msg + '258EAFA5-E914-47DA-95CA-C5AB0DC85B11'
        ser_key = hashlib.sha1(key).digest()
        return base64.b64encode(ser_key)

#服务端
class websocket_server(threading.Thread):
    def __init__(self, port):
        super(websocket_server, self).__init__()
        self.port = port

    def run(self):
        sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
        sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
        sock.bind(('0.0.0.0', self.port))
        sock.listen(5)
        print 'websocket server started!'
        while True:
            connection, address = sock.accept()
            try:
                username = "ID" + str(address[1])
                thread = websocket_thread(connection, username)
                thread.start()
                clients[username] = connection
            except socket.timeout:
                print 'websocket connection timeout!'

if __name__ == '__main__':
    server = websocket_server(9000) 
    server.start()

注意:这里服务器的ip设置最好设置为0.0.0.0,允许局域网其他计算机访问

网页端代码,用于向安卓端推送消息



html>
    <head>
        <meta charset="utf-8">
    head>
    <body>
        <h3>WebSocketTesth3>
        <div id="login">
            <div>
                <input id="serverIP" type="text" placeholder="服务器IP" value="127.0.0.1" autofocus="autofocus" />
                <input id="serverPort" type="text" placeholder="服务器端口" value="9000" />
                <input id="btnConnect" type="button" value="连接" onclick="connect()" />
            div>
            <div>
                <input id="sendText" type="text" placeholder="发送文本" value="I'm WebSocket Client!" />
                <input id="btnSend" type="button" value="发送" onclick="send()" />
            div>
            <div>
                <div>
                    来自服务端的消息
                div>
                <textarea id="txtContent" cols="50" rows="10" readonly="readonly">textarea>
            div>
        div>
    body>
    <script>
        var socket;

        function connect() {
            var host = "ws://" + $("serverIP").value + ":" + $("serverPort").value + "/"
            socket = new WebSocket(host);
            try {

                socket.onopen = function (msg) {
                    $("btnConnect").disabled = true;
                    alert("连接成功!");
                };

                socket.onmessage = function (msg) {
                    if (typeof msg.data == "string") {
                        displayContent(msg.data);
                    }
                    else {
                        alert("非文本消息");
                    }
                };

                socket.onclose = function (msg) { alert("socket closed!") };
            }
            catch (ex) {
                log(ex);
            }
        }

        function send() {
            var msg = $("sendText").value
            socket.send(msg);
        }

        window.onbeforeunload = function () {
            try {
                socket.close();
                socket = null;
            }
            catch (ex) {
            }
        };

        function $(id) { return document.getElementById(id); }

        Date.prototype.Format = function (fmt) { //author: meizz 
            var o = {
                "M+": this.getMonth() + 1, //月份 
                "d+": this.getDate(), //日 
                "h+": this.getHours(), //小时 
                "m+": this.getMinutes(), //分 
                "s+": this.getSeconds(), //秒 
                "q+": Math.floor((this.getMonth() + 3) / 3), //季度 
                "S": this.getMilliseconds() //毫秒 
            };
            if (/(y+)/.test(fmt)) fmt = fmt.replace(RegExp.$1, (this.getFullYear() + "").substr(4 - RegExp.$1.length));
            for (var k in o)
                if (new RegExp("(" + k + ")").test(fmt)) fmt = fmt.replace(RegExp.$1, (RegExp.$1.length == 1) ? (o[k]) : (("00" + o[k]).substr(("" + o[k]).length)));
            return fmt;
        }

        function displayContent(msg) {
            $("txtContent").value += "\r\n" +new Date().Format("yyyy/MM/dd hh:mm:ss")+ ":  " + msg;
        }
        function onkey(event) { if (event.keyCode == 13) { send(); } }
    script>
html>

安卓端实现

WebSocketClient地址 https://github.com/TooTallNate/Java-WebSocket

导入步骤

  1. 在gradle的repository中添加

    mavenCentral()

  2. 在build中添加

    compile “org.java-websocket:Java-WebSocket:1.3.4”

3.运行gradle同步

利用WebSocket自己手搓一个工具类,放到Service中启动

package com.halfopen.h.cislsign;

import android.util.Log;

import org.java_websocket.client.WebSocketClient;
import org.java_websocket.handshake.ServerHandshake;

import java.net.URI;
import java.net.URISyntaxException;

import static cn.jpush.android.api.JPushInterface.a.s;

/**
 * Created by h on 2017/7/23.
 */

public class MyWebSocketClient {

    //WebSocketClient 和 address
    private WebSocketClient mWebSocketClient;
    private String address = "ws://10.222.108.43:9000";


//初始化WebSocketClient
    /**
     *
     * @throws URISyntaxException
     */
    private void initSocketClient() throws URISyntaxException {
        if(mWebSocketClient == null) {
            mWebSocketClient = new WebSocketClient(new URI(address)) {
                @Override
                public void onOpen(ServerHandshake serverHandshake) {
                    Log.d("flag--","onOpen(WebSocketClient.java:32)-->>"+"成功建立websocket连接");
                }


                @Override
                public void onMessage(String s) {
                    //服务端消息
                    Log.d("flag--","onMessage(WebSocketClient.java:39)-->>"+"收到来自服务器的消息"+s);
                }


                @Override
                public void onClose(int i, String s, boolean remote) {
                    //连接断开,remote判定是客户端断开还是服务端断开
                    Log.d("flag--","onClose(WebSocketClient.java:46)-->>"+"Connection closed by " + ( remote ? "remote peer" : "us" ) + ", info=" + s);
                    closeConnect();
                }


                @Override
                public void onError(Exception e) {
                    Log.d("flag--","onError(WebSocketClient.java:53)-->>"+s);
                }
            };
        }
    }


    //连接
    private void connect() {
        new Thread(){
            @Override
            public void run() {
                mWebSocketClient.connect();
            }
        }.start();
    }


    //断开连接
    private void closeConnect() {
        try {
            mWebSocketClient.close();
        }
        catch(Exception e) {
            e.printStackTrace();
        }
        finally {
            mWebSocketClient = null;
        }
    }

    /**
     *发送消息
     */
    private void sendMsg(String msg) {
        mWebSocketClient.send(msg);
    }

    //
    public void start(){
        try {

            initSocketClient();
            connect();
        } catch (URISyntaxException e) {
            e.printStackTrace();
        }

    }
}

注意:安卓客户端要和websocekt服务器互通,并且地址和端口要向对应。

WebSocket实现android消息推送_第1张图片

WebSocket实现android消息推送_第2张图片

WebSocket实现android消息推送_第3张图片

参考链接

https://www.zhihu.com/question/20215561
http://blog.csdn.net/icechenbing/article/details/7407588

你可能感兴趣的:(android)