Android开发规范技巧

【socket】- 长连接

2019-10-24  本文已影响0人  拔萝卜占坑

简介

很多时候项目需要及时获取消息或者推送消息,比如聊天,消息通知等等。当然市场上已经有很多成熟的聊天,推送产品。但是这些都需要收费,而免费的有些时候并不能满足我们的需求。况且引入一个sdk也会增加应用包的大小。如果项目存在敏感的数据,那么使用第三方工具也会有一定安全问题。基于以上考虑,可以自己实现Socket长连接,实现即时通讯。

这篇文章会基于前两篇文章进行讲解,如果还没有查看的,建议先看看前两篇文章,多Socket有个大致的了解。

客户端

创建客户端Service(服务),并让它允许在独立的进程中。当然我得尽量保证服务进程不被系统干掉。我们可以提高进程的优先级,开启前台进程,进程间互相唤醒,不定时检测进程是否被杀死等等。具体内容我将在后面的文章讲解。我们看一下客户端Socket部分的实现。

    override fun connect(ip: String, port: Int) {
        lock.lock()
        if (isConnected()){
            disConnect(false)
        }
        connectState = SState.STATE_CONNECTING
        this.ip = ip
        this.port = port
        Log.i(TAG,"connecting  ip=$ip , port = $port")
        try {
            while (true){
                try {
                    socket = Socket()
                    if (null == socket){
                        throw (Exception("connect failed,unknown error"))
                    }
                    val address = InetSocketAddress(ip,port)
                    socket!!.keepAlive = false
                    //inputStream read 超时时间
                    socket!!.soTimeout = 2 * 3 * 60 * 1000
                    socket!!.tcpNoDelay = true
                    socket!!.connect(address)
                    if (socket!!.isConnected){
                        dataInputStream = DataInputStream(socket!!.getInputStream())
                        dataOutputStream = DataOutputStream(socket!!.getOutputStream())
                        connectState = SState.STATE_CONNECTED
                        this.sCallback.onConnect()
                        break
                    }else{
                        throw (Exception("connect failed,unknown error"))
                    }
                }catch (e:Exception){
                    cRetryPolicy?.retry(e)
                    Thread.sleep(5*1000)
                    Log.i(TAG,"connect IOException =${e.message} , and retry count = ${cRetryPolicy?.getCurrentRetryCount()}")
                }
            }
        }catch (e:Exception){
            e.printStackTrace()
            Log.i(TAG,"connect IOException =  ${e.message}")
            connectState = SState.STATE_CONNECT_FAILED
            sCallback.onConnectFailed(e)
        }finally {
            lock.unlock()
        }
        if (connectState == SState.STATE_CONNECTED){
            receiveData()
        }
    }

Socket连接的过程就不讲了,不明白的可以查看前面的文章。在while循环里面,直到到达重试次数之前,一直连接服务器,直到连接成功。重试代码如下:

    fun retry(ex: Exception){
        mCurrentRetryCount++
        if (!hasAttemptRemaining()) 
            throw ex   
    }
获取Socket输入,输出流对象

当Socket连接成功后,拿到输出,输入流对象,我们就可以读取服务器端发送的数据和向服务器写数据。


    override fun disConnect(reconnect:Boolean) {
        Log.i(TAG,"disConnect")
        if(null != socket){
            try {
                closeInputStream()
                closeOutputStream()
                socket?.shutdownInput()
                socket?.shutdownOutput()
                socket?.close()
                socket = null
            }catch (e:Exception){
                e.printStackTrace()
            }
        }
    }

Java 的 Socket 类提供了 shutdownOutput() 和shutdownInput() 另个方法, 用来分别只关闭 Socket 的输出流和输入流, 而不影响其对应的输入流和输出流。

  1. shutdownInput: 禁用此套接字的输入流,这样发送到套接字的输入流端的任何数据都将被确认然后被静默丢弃。任何想从该套接字的输入流读取数据的操作都将返回-1。
  2. shutdownOutput:关闭套接字的输出流,告诉服务器我已经发送完所以数据,这样, 服务端的 read() 方法便会返回-1, 继续往下执行。

服务端

服务端监听客户端的连接消息,在连接成功后,获取流对象,便可以向客户端发送消息。

    fun startAccept(){
        try {
            Log.i(TAG,"start accept")
            //调用accept()方法开始监听,等待客户端的连接
            val socket = serverSocket.accept()
            //向客户端传递的信息
            val outputStream = socket.getOutputStream()
            val printWriter = PrintWriter(outputStream)
            printWriter.write("-----------socket connect success------------")
            printWriter.flush()
            //获取输入流,并读取客户端信息
            val inputStream = socket.getInputStream()
            //把字节流转换成字符流
            val inputStreamReader = InputStreamReader(inputStream)
            //为字符流增加缓冲区
            val bufferedReader = BufferedReader(inputStreamReader)
            var data :String? = bufferedReader.readLine()
            while (data != null){
                Log.i(TAG,"accept data = $data")
                data = bufferedReader.readLine()
            }
            socket.shutdownInput()
            printWriter.write("-----------socket disconnect------------")
            printWriter.flush()
            //关闭资源
            printWriter.close()
            outputStream.close()
            inputStream.close()
            inputStreamReader.close()
            bufferedReader.close()
            socket.close()
            serverSocket.close()
        }catch (e:Exception){
            e.printStackTrace()
        }finally {
            serverSocket.close()
        }
    }

serverSocket.accept()阻塞,当有连接到达时返回,获取输入,输出流,向客户端写数据。然后关闭流和socket连接。

上一篇 下一篇

猜你喜欢

热点阅读