Socket.io

Socket.IO(官网)介绍

是一个跨平台的聊天框架,可以实现 web 端和移动端的实时聊天,简单说就是用来做聊天和消息推送的。最初以为项目做消息推送会直接使用第三方的,比如小米、极光啥的,但是,架构师说我们不用第三方的,自己要搭建消息推送,Socket.io技术很成熟了。所以就这样开始了Socket.io的学习之路。第一次接触Socket.io以为很难做,因为不了解,加上能找的资源有限(上网查找的都是英文资料,有关于Android端的Socke.io更是少之又少,除非你去stackoverflow里面去找,英语还得好),所以写篇文章记录下实现该功能流程以及遇到的问题。
官方DemoGithub的Demo,这两个 demo 都是一个聊天室,可以在里面聊天,刚开始弄这个的时候发现有好多哥们在那里,还找几个一起在做聊天功能的小伙伴,快快下载试试吧。

本文不说服务端的搭建和web端的实现,只是来说说 Android 端如何使用Socket.io实现消息推送功能。

(1)导包

Android Studio 导包,一共有两种情况(PS: Eclipse 用户也不要哭,下面会教你怎样获取到 JAR

  1. 第一种情况 compile 'com.github.nkzawa:socket.io-client:0.3.0'

  2. 第二种情况

     compile ('io.socket:socket.io-client:0.7.0') {
       // excluding org.json which is provided by Android
       exclude group: 'org.json', module: 'json'
     }
    

注意:两个包的区别,如果你的项目没有用到 Https ,那么你可以使用两个当中的一个。如果有用到 Https ,那么你就要用二个包,不然你会连接不上 Https 的,具体的连接方式,以下会介绍。

第一种情况的导包获取到的 JAR 形式

 
 

第二种情况的导包获取到的 JAR 形式

 
 

PSAndroid Studio 用户直接跳过(如果你想看看你的Studio下载的 JAR 放在那里也可以看看)。Eclipse 用户获取 Jar,如果你会去远程仓库下载 Jar,那么你就去吧,也就是几个 JAR 而已,不然的话你还是得借助 Android Studio 来获取 Jar

用Studio导入包后找到你的 External Libraries ,选中你的 JAR ,如 engine.io-client-0.7.0 点击右键,点击 Library Properties ,会弹出一个对话框,Copy 这个URL,打开我的电脑,粘贴到导航栏点击确定就可以看到你的 JAR了,但是,这个 JAR 是一个资源文件的 JAR ,里面有源码的,我们不需要这个,点击back后退,一般会有三个文件夹,其中的一个就是你需要的 JAR了。流程图如下:

 
 

 
 

 
 

(2)代码使用

导完包剩下的就是代码的使用了。由于 Socket.io 封装得很好,所以我们能用到的类和方法不多,也就几个而已。

  • 获取 Socket 和设置 url : mSocket = IO.socket( "http://192.168.205.125:10443" );
  • 连接 mSocket.mSocket.connect();
  • 发送消息 mSocket.emit( "newMessage", data );,这里需要注意的是: data 是 JSONObject 的类。
  • 消息监听 mSocket.on( Socket.EVENT_CONNECT, onConnect );// 连接成功
  • 断开连接 mSocket.disconnect();
  • 断开消息监听 mSocket.off( Socket.EVENT_CONNECT, onConnect );

就是这么几个方法就可以实现消息推送或者实现聊天了。由于本文主要是实现消息推送的功能,所以把主要的代码放在了 Service,顺便把流程写一下,方便初学者学习。

    import android.app.Service;
    import android.content.Intent;
    import android.os.IBinder;
    import android.support.annotation.Nullable;
    import android.util.Log;
    
    import org.json.JSONException;
    import org.json.JSONObject;
    
    import java.net.URISyntaxException;
    import java.security.KeyManagementException;
    import java.security.NoSuchAlgorithmException;
    import java.security.cert.CertificateException;
    import java.security.cert.X509Certificate;
    
    import javax.net.ssl.HostnameVerifier;
    import javax.net.ssl.SSLContext;
    import javax.net.ssl.SSLSession;
    import javax.net.ssl.TrustManager;
    import javax.net.ssl.X509TrustManager;
    
    import io.socket.client.IO;
    import io.socket.client.Socket;
    import io.socket.emitter.Emitter;
    
    public class MessagePushService extends Service {
        
        private static final String TAG = MessagePushService.class.getSimpleName();
        
        private Socket mSocket;
        
        private boolean isConnected;
        
        /**
         * 初始化Socket,Https的连接方式
         */
        private void initSocketHttps() {
            SSLContext sc = null;
            TrustManager[] trustCerts = new TrustManager[] { new X509TrustManager() {
                
                @Override
                public X509Certificate[] getAcceptedIssuers() {
                    return null;
                }
                
                @Override
                public void checkServerTrusted( X509Certificate[] chain, String authType )
                    throws CertificateException {
                }
                
                @Override
                public void checkClientTrusted( X509Certificate[] chain, String authType )
                    throws CertificateException {
                    
                }
            } };
            try {
                sc = SSLContext.getInstance( "TLS" );
                sc.init( null, trustCerts, null );
                IO.Options opts = new IO.Options();
                opts.sslContext = sc;
                opts.hostnameVerifier = new HostnameVerifier() {
                    @Override
                    public boolean verify( String s, SSLSession sslSession ) {
                        return true;
                    }
                };
                mSocket = IO.socket( "https://192.168.205.125:10443", opts );
            } catch ( NoSuchAlgorithmException e ) {
                e.printStackTrace();
            } catch ( KeyManagementException e ) {
                e.printStackTrace();
            } catch ( URISyntaxException e ) {
                e.printStackTrace();
            }
        }
        
        /**
         * 初始化Socket,Http的连接方式
         */
        private void initSocketHttp() {
            try {
                mSocket = IO.socket( "http://192.168.205.125:10443" ); // 初始化Socket
            } catch ( URISyntaxException e ) {
                e.printStackTrace();
            }
        }
        
        private void connectSocket() {
            try {
                mSocket.connect();
                JSONObject jsonObject = new JSONObject();
                jsonObject.put( "userName", "小王" ); // 这里一般是设置登录名
                mSocket.emit( "loginName", jsonObject ); // 发送登录人
            } catch ( JSONException e ) {
                e.printStackTrace();
            }
            
            mSocket.on( Socket.EVENT_CONNECT, onConnect );// 连接成功
            mSocket.on( Socket.EVENT_DISCONNECT, onDisconnect );// 断开连接
            mSocket.on( Socket.EVENT_CONNECT_ERROR, onConnectError );// 连接异常
            mSocket.on( Socket.EVENT_CONNECT_TIMEOUT, onConnectTimeoutError );// 连接超时
            mSocket.on( "newMessage", onConnectMsg );// 监听消息事件回调
        }
        
        private void disConnectSocket() {
            mSocket.disconnect();
            
            mSocket.off( Socket.EVENT_CONNECT, onConnect );// 连接成功
            mSocket.off( Socket.EVENT_DISCONNECT, onDisconnect );// 断开连接
            mSocket.off( Socket.EVENT_CONNECT_ERROR, onConnectError );// 连接异常
            mSocket.off( Socket.EVENT_CONNECT_TIMEOUT, onConnectTimeoutError );// 连接超时
            mSocket.off( "newMessage", onConnectMsg );// 监听消息事件回调
        }
        
        private Emitter.Listener onConnectMsg = new Emitter.Listener() {
            @Override
            public void call( final Object... args ) {
                // 在这里处理你的消息
                Log.e( TAG, "服务器返回来的消息 : " + args[0] );
            }
        };
        
        /**
         * 实现消息回调接口
         */
        private Emitter.Listener onConnect = new Emitter.Listener() {
            @Override
            public void call( final Object... args ) {
                Log.e( TAG, "连接成功 " + args[0] );
                if (!isConnected) { // 如果已经断开,重新发送
                    try {
                        JSONObject jsonObject = new JSONObject();
                        jsonObject.put( "userName", "小王" ); // 这里一般是设置登录名
                        mSocket.emit( "loginName", jsonObject ); // 发送登录人
                    } catch ( JSONException e ) {
                        e.printStackTrace();
                    }
                    isConnected = true;
                }
            }
        };
        
        private Emitter.Listener onDisconnect = new Emitter.Listener() {
            @Override
            public void call( Object... args ) {
                Log.e( TAG, "断开连接 " + args[0] );
                isConnected = false;
            }
        };
        
        private Emitter.Listener onConnectError = new Emitter.Listener() {
            @Override
            public void call( final Object... args ) {
                Log.e( TAG, "连接 失败" + args[0] );
            }
        };
        
        private Emitter.Listener onConnectTimeoutError = new Emitter.Listener() {
            @Override
            public void call( final Object... args ) {
                Log.e( TAG, "连接 超时" + args[0] );
                
            }
        };
        
        @Nullable
        @Override
        public IBinder onBind( Intent intent ) {
            return null;
        }
    }
    

就这样完成了一个消息推送,不需要集成什么东西,也不用受制于人。

总结:刚开始做这个的时候找不到各种 JAR (项目还是在使用Eclipse开发中,所以还是需要去找到 JAR),那会自己才倒腾 Android Studio 没多久,把 Demo 下载下来运行没问题,然后就去找 这个资源的 JAR,准备集成到 项目里面去,一开始以为只要 engine.io-client JAR 和 socket.io-client JAR ,结果就报错了,然后不断的查资料找包(这时候还不知道 Android Studio 已经把JAR 下载到电脑了),最后才知道 Android Studio 下载的JAR在哪里(这才有了前面的找JAR),等到所有的JAR找到后,集成到了项目里面去,最后还是不断的报 NoClassDefFoundError ,把这些JAR全部放到 Android Studio 集成运行却没有问题,这才发现是我的 Eclipse 问题,等找到这些原因,时间已经过去两天了,悲剧呀,然后快速的集成到项目,最终运行一切Ok啦!但是,如果你觉得故事就这样没了,那就太没意思了,这个时候公司的接口全部转成 https ,悲剧呀,项目运行不起来了。花时间找源码,看看怎样实现,最终在stackoverflow找到了关于加载证书的方式,但是翻源码的时候却发现没有这个 opts.hostnameVerifier 属性,再一次悲剧(这个时候,我用的是上面的第一种JAR,也就是官网介绍的: compile 'com.github.nkzawa:socket.io-client:0.3.0')。这个时候时候发现了 Socket.io 官网的介绍和 Github 的介绍是两种不同的 JAR,最后导入 githubJAR(第二种导包方式,所以你还是直接用这个包吧) 确实可以运行了。满满的坑呀!



作者:xiaowen_2010
链接:https://www.jianshu.com/p/274db90fa0be
來源:简书
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。
原文地址:https://www.cnblogs.com/changyiqiang/p/9075377.html