Android 通過Socket 和服務器通訊


Android 通過Socket 和服務器通訊,是一種比較常用的通訊方式,時間比較緊,說下大致的思路,希望能幫到使用socket 進行通信的人

(1)開啟一個線程發送消息    SocketOutputThread

      消息是放在隊列里的,當有消息后,進入隊列,線程喚醒,發送消息,並反饋發送是否成功的回調

 

(2)開啟一個線程接受服務器消息 SocketInputThread

       為了防止一直收數據,浪費電池的電,采用NIO的方式讀socket的數據,這個是本文的關鍵

 

(3)開啟一個線程,做心跳,防止socket連接終斷 , SocketHeartThread

 

 

(4)構建 SocketThreadManager對以上三個thread進行管理

 

(5)構建 TCPClient 發送socket消息

     在NIO的方式實現TCP,特別是在接收服務器的數據,不用寫個線程定時去讀了。

 

 本文地址(http://www.cnblogs.com/likwo/p/3641135.html

 

DEMO 截圖

 

主要代碼如下,詳細代碼在附件里。

 

SocketOutPutThread 類

package com.example.socketblockdemo;

import java.io.IOException;
import java.io.ObjectOutputStream;
import java.net.Socket;
import java.util.List;
import java.util.concurrent.CopyOnWriteArrayList;

import android.os.Bundle;
import android.os.Handler;
import android.os.Message;


/**
 * 客戶端寫消息線程
 * 
 * @author way
 * 
 */
public class SocketOutputThread extends Thread
{
    private boolean isStart = true;
    private static String tag = "socketOutputThread";
    private List<MsgEntity> sendMsgList;
    
    public SocketOutputThread( )
    {

        sendMsgList = new CopyOnWriteArrayList<MsgEntity>();
    }
    
    public void setStart(boolean isStart)
    {
        this.isStart = isStart;
        synchronized (this)
        {
            notify();
        }
    }

    // 使用socket發送消息
    public boolean sendMsg(byte[] msg) throws Exception
    {
                
        
        if (msg == null)
        {
            CLog.e(tag, "sendMsg is null");
            return false;
        }
        
        try
        {
            TCPClient.instance().sendMsg(msg);
            
        } catch (Exception e)
        {
            throw (e);
        }
        
        return true;
    }
    
    // 使用socket發送消息
    public void addMsgToSendList(MsgEntity msg) 
    {

        synchronized (this)
        {
            this.sendMsgList.add(msg);
            notify();
        }
    }
    
    @Override
    public void run()
    {
        while (isStart)
        {
            // 鎖發送list
            synchronized (sendMsgList)
            {
                // 發送消息
                for (MsgEntity msg : sendMsgList)
                {
                    
                    Handler handler = msg.getHandler();
                    try
                    {
                        sendMsg(msg.getBytes());
                        sendMsgList.remove(msg);
                        // 成功消息,通過hander回傳
                        if (handler != null)
                        {
                            Message message =  new Message();
                            message.obj = msg.getBytes();
                            message.what =1;
                           handler.sendMessage(message);
                        //    handler.sendEmptyMessage(1);
                        }
                        
                    } catch (Exception e)
                    {
                        e.printStackTrace();
                        CLog.e(tag, e.toString());
                        // 錯誤消息,通過hander回傳
                        if (handler != null)
                        {
                            Message message =  new Message();
                            message.obj = msg.getBytes();
                            message.what = 0;;
                            handler.sendMessage(message);
                        
                        }
                    }
                }
            }
            
            synchronized (this)
            {
                try
                {
                    wait();
                    
                } catch (InterruptedException e)
                {
                    // TODO Auto-generated catch block
                    e.printStackTrace();
                }// 發送完消息后,線程進入等待狀態
            }
        }
        
    }
}
View Code

SocketInputThread

package com.example.socketblockdemo;

import java.io.IOException;
import java.io.UnsupportedEncodingException;
import java.nio.ByteBuffer;
import java.nio.channels.CancelledKeyException;
import java.nio.channels.ClosedSelectorException;
import java.nio.channels.SelectionKey;
import java.nio.channels.Selector;
import java.nio.channels.SocketChannel;
import java.nio.charset.CharacterCodingException;
import java.nio.charset.Charset;


import android.content.Intent;
import android.text.TextUtils;

/**
 * 客戶端讀消息線程
 * 
 * @author way
 * 
 */
public class SocketInputThread extends Thread
{
    private boolean isStart = true;
    
    private static String tag = "socket";
    
    // private MessageListener messageListener;// 消息監聽接口對象
    
    public SocketInputThread()
    {
    }
    
    public void setStart(boolean isStart)
    {
        this.isStart = isStart;
    }
    
    @Override
    public void run()
    {
        while (isStart)
        {
            // 手機能聯網,讀socket數據
            if (NetManager.instance().isNetworkConnected())
            {
                
                if (!TCPClient.instance().isConnect())
                {
                    CLog.e(tag, "TCPClient connet server is fail read thread sleep second" +Const.SOCKET_SLEEP_SECOND );
                    
                    try
                    {
                        sleep(Const.SOCKET_SLEEP_SECOND * 1000);
                    } catch (InterruptedException e)
                    {
                        // TODO Auto-generated catch block
                        e.printStackTrace();
                    }
                }
                
                readSocket();
                
                // 如果連接服務器失敗,服務器連接失敗,sleep固定的時間,能聯網,就不需要sleep
                
                CLog.e("socket","TCPClient.instance().isConnect() " + TCPClient.instance().isConnect() );
                
                
            }
        }
    }
    
    public void readSocket()
    {
        Selector selector = TCPClient.instance().getSelector();
        if (selector == null)
        {
            return;
        }
        try
        {
            // 如果沒有數據過來,一直柱塞
            while (selector.select() > 0)
            {
                for (SelectionKey sk : selector.selectedKeys())
                {
                    // 如果該SelectionKey對應的Channel中有可讀的數據
                    if (sk.isReadable())
                    {
                        // 使用NIO讀取Channel中的數據
                        SocketChannel sc = (SocketChannel) sk.channel();
                        ByteBuffer buffer = ByteBuffer.allocate(1024);
                        try
                        {
                            sc.read(buffer);
                        } catch (IOException e)
                        {
                            // TODO Auto-generated catch block
                            e.printStackTrace();
                            // continue;
                        }
                        buffer.flip();
                        String receivedString = "";
                        // 打印收到的數據
                        try
                        {
                            receivedString = Charset.forName("UTF-8")
                                    .newDecoder().decode(buffer).toString();
                            
                            CLog.e(tag, receivedString);
                            

                            Intent i = new Intent(Const.BC);
                            
                            i.putExtra("response", receivedString);
                            
                            MainActivity.s_context.sendBroadcast(i );
                            
                        } catch (CharacterCodingException e)
                        {
                            // TODO Auto-generated catch block
                            e.printStackTrace();
                        }
                        buffer.clear();
                        buffer = null;
                        
                        try
                        {
                            // 為下一次讀取作准備
                            sk.interestOps(SelectionKey.OP_READ);
                            // 刪除正在處理的SelectionKey
                            selector.selectedKeys().remove(sk);
                            
                        } catch (CancelledKeyException e)
                        {
                            e.printStackTrace();
                        }
                        
                    
                    }
                }
            }
            // selector.close();
            // TCPClient.instance().repareRead();
            
        } catch (IOException e1)
        {
            // TODO Auto-generated catch block
            e1.printStackTrace();
        } catch (ClosedSelectorException e2)
        {
        }
    }
    
}
View Code

SocketHeartHread 心態類

package com.example.socketblockdemo;

import java.io.IOException;

import android.text.TextUtils;


class SocketHeartThread extends Thread
{
    boolean isStop = false;
    boolean mIsConnectSocketSuccess = false;
    static SocketHeartThread s_instance;
    
    private TCPClient mTcpClient = null;
    
    static final String tag = "SocketHeartThread";
    
    public static synchronized SocketHeartThread instance()
    {
        if (s_instance == null)
        {
            s_instance = new SocketHeartThread();
        }
        return s_instance;
    }
    
    public SocketHeartThread()
    {
       TCPClient.instance();
                // 連接服務器
    //    mIsConnectSocketSuccess = connect();

    }

    public void stopThread()
    {
        isStop = true;
    }
    
    /**
     * 連接socket到服務器, 並發送初始化的Socket信息
     * 
     * @return
     */
    
    
    private boolean reConnect()
    {
        return TCPClient.instance().reConnect();
    }

    
    public void run()
    {
        isStop = false;
        while (!isStop)
        {
                // 發送一個心跳包看服務器是否正常
                boolean canConnectToServer = TCPClient.instance().canConnectToServer();
                
                if(canConnectToServer == false){
                    reConnect();
                }
                try
                {
                    Thread.sleep(Const.SOCKET_HEART_SECOND * 1000);
                    
                } catch (InterruptedException e)
                {
                    e.printStackTrace();
                }
            }
    }
}
View Code

 

線程管理類

package com.example.socketblockdemo;

import android.os.Handler;
import android.text.TextUtils;


public class SocketThreadManager
{
    
    private static SocketThreadManager s_SocketManager = null;
    
    private SocketInputThread mInputThread = null;
    
    private SocketOutputThread mOutThread = null;
    
    private SocketHeartThread mHeartThread = null;

    
    // 獲取單例
    public static SocketThreadManager sharedInstance()
    {
        if (s_SocketManager == null)
        {
            s_SocketManager = new SocketThreadManager();
            s_SocketManager.startThreads();
        }
        return s_SocketManager;
    }
    
    // 單例,不允許在外部構建對象
    private SocketThreadManager()
    {
        mHeartThread = new SocketHeartThread();
        mInputThread = new SocketInputThread();
        mOutThread = new SocketOutputThread();
    }
    
    /**
     * 啟動線程
     */
    
    private void startThreads()
    {
        mHeartThread.start();
        mInputThread.start();
        mInputThread.setStart(true);
        mOutThread.start();
        mInputThread.setStart(true);
        // mDnsthread.start();
    }
    
    /**
     * stop線程
     */
    public void stopThreads()
    {
        mHeartThread.stopThread();
        mInputThread.setStart(false);
        mOutThread.setStart(false);
    }
    
    public static void releaseInstance()
    {
        if (s_SocketManager != null)
        {
            s_SocketManager.stopThreads();
            s_SocketManager = null;
        }
    }
    
    public void sendMsg(byte [] buffer, Handler handler)
    {
        MsgEntity entity = new MsgEntity(buffer, handler);
        mOutThread.addMsgToSendList(entity);
    }
    
}

 

TCPClient ,采用NIO的方式構建

package com.example.socketblockdemo;

import java.io.IOException;
import java.net.ConnectException;
import java.net.InetSocketAddress;
import java.net.SocketAddress;
import java.net.SocketTimeoutException;
import java.nio.ByteBuffer;
import java.nio.channels.ClosedChannelException;
import java.nio.channels.SelectionKey;
import java.nio.channels.Selector;
import java.nio.channels.SocketChannel;


/**
 * NIO TCP 客戶端
 * 
 */
public class TCPClient
{
    // 信道選擇器
    private Selector selector;
    
    // 與服務器通信的信道
    SocketChannel socketChannel;
    
    // 要連接的服務器Ip地址
    private String hostIp;
    
    // 要連接的遠程服務器在監聽的端口
    private int hostListenningPort;
    
    private static TCPClient s_Tcp = null;
    
    public boolean isInitialized = false;
    
    public static synchronized TCPClient instance()
    {
        if (s_Tcp == null)
        {
            
            s_Tcp = new TCPClient(Const.SOCKET_SERVER,
                    Const.SOCKET_PORT);
        }
        return s_Tcp;
    }
    
    /**
     * 構造函數
     * 
     * @param HostIp
     * @param HostListenningPort
     * @throws IOException
     */
    public TCPClient(String HostIp, int HostListenningPort)
    {
        this.hostIp = HostIp;
        this.hostListenningPort = HostListenningPort;
        
        try
        {
            initialize();
            this.isInitialized = true;
        } catch (IOException e)
        {
            this.isInitialized = false;
            // TODO Auto-generated catch block
            e.printStackTrace();
        } catch (Exception e)
        {
            this.isInitialized = false;
            e.printStackTrace();
        }
    }
    
    /**
     * 初始化
     * 
     * @throws IOException
     */
    public void initialize() throws IOException
    {
        boolean done = false;
        
        try
        {
            // 打開監聽信道並設置為非阻塞模式
            socketChannel = SocketChannel.open(new InetSocketAddress(hostIp,
                    hostListenningPort));
            if (socketChannel != null)
            {
                socketChannel.socket().setTcpNoDelay(false);
                socketChannel.socket().setKeepAlive(true);
                // 設置 讀socket的timeout時間
                socketChannel.socket().setSoTimeout(
                        Const.SOCKET_READ_TIMOUT);
                socketChannel.configureBlocking(false);
                
                // 打開並注冊選擇器到信道
                selector = Selector.open();
                if (selector != null)
                {
                    socketChannel.register(selector, SelectionKey.OP_READ);
                    done = true;
                }
            }
        } finally
        {
            if (!done && selector != null)
            {
                selector.close();
            }
            if (!done)
            {
                socketChannel.close();
            }
        }
    }
    
    static void blockUntil(SelectionKey key, long timeout) throws IOException
    {
        
        int nkeys = 0;
        if (timeout > 0)
        {
            nkeys = key.selector().select(timeout);
            
        } else if (timeout == 0)
        {
            nkeys = key.selector().selectNow();
        }
        
        if (nkeys == 0)
        {
            throw new SocketTimeoutException();
        }
    }
    
    /**
     * 發送字符串到服務器
     * 
     * @param message
     * @throws IOException
     */
    public void sendMsg(String message) throws IOException
    {
        ByteBuffer writeBuffer = ByteBuffer.wrap(message.getBytes("utf-8"));
        
        if (socketChannel == null)
        {
            throw new IOException();
        }
        socketChannel.write(writeBuffer);
    }
    
    /**
     * 發送數據
     * 
     * @param bytes
     * @throws IOException
     */
    public void sendMsg(byte[] bytes) throws IOException
    {
        ByteBuffer writeBuffer = ByteBuffer.wrap(bytes);
        
        if (socketChannel == null)
        {
            throw new IOException();
        }
        socketChannel.write(writeBuffer);
    }
    
    /**
     * 
     * @return
     */
    public synchronized Selector getSelector()
    {
        return this.selector;
    }
    
    /**
     * Socket連接是否是正常的
     * 
     * @return
     */
    public boolean isConnect()
    {
        boolean isConnect = false;
        if (this.isInitialized)
        {
            isConnect =  this.socketChannel.isConnected();
        }
        return isConnect;
    }
    
    /**
     * 關閉socket 重新連接
     * 
     * @return
     */
    public boolean reConnect()
    {
        closeTCPSocket();
        
        try
        {
            initialize();
            isInitialized = true;
        } catch (IOException e)
        {
            isInitialized = false;
            e.printStackTrace();
        }
        catch (Exception e)
        {
            isInitialized = false;
            e.printStackTrace();
        }
        return isInitialized;
    }
    
    /**
     * 服務器是否關閉,通過發送一個socket信息
     * 
     * @return
     */
    public boolean canConnectToServer()
    {
        try
        {
            if (socketChannel != null)
            {
                socketChannel.socket().sendUrgentData(0xff);
            }
        } catch (IOException e)
        {
            // TODO Auto-generated catch block
            e.printStackTrace();
            return false;
        }
        catch (Exception e){
            e.printStackTrace();
            return false;
        }
        return true;
    }
    
    /**
     * 關閉socket
     */
    public void closeTCPSocket()
    {
        try
        {
            if (socketChannel != null)
            {
                socketChannel.close();
            }
            
        } catch (IOException e)
        {
            
        }
        try
        {
            if (selector != null)
            {
                selector.close();
            }
        } catch (IOException e)
        {
        }
    }
    
    /**
     * 每次讀完數據后,需要重新注冊selector,讀取數據
     */
    public synchronized void repareRead()
    {
        if (socketChannel != null)
        {
            try
            {
                selector = Selector.open();
                socketChannel.register(selector, SelectionKey.OP_READ);
            } catch (ClosedChannelException e)
            {
                e.printStackTrace();
                
            } catch (IOException e)
            {
                e.printStackTrace();
            }
        }
    }
}
View Code

 

如何使用

            // 發送消息,失敗或者成功的handler
            SocketThreadManager.sharedInstance().sendMsg(str.getBytes(), handler);

 

 

代碼下載 http://files.cnblogs.com/likwo/SocketBlockDemo.zip

 

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM