Android教程網
  1. 首頁
  2. Android 技術
  3. Android 手機
  4. Android 系統教程
  5. Android 游戲
 Android教程網 >> Android技術 >> 關於Android編程 >> 詳解Android 通過Socket 和服務器通訊(附demo)

詳解Android 通過Socket 和服務器通訊(附demo)

編輯:關於Android編程

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

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

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

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

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

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

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

(5)構建 TCPClient 發送socket消息

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

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();
    }// 發送完消息後,線程進入等待狀態
   }
  }
  
 }
}

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)
  {
  }
 }
 
}

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();
    }
   }
 }
}

線程管理類

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();
   }
  }
 }
}

如何使用

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

代碼下載:demo

以上就是本文的全部內容,希望對大家的學習有所幫助,也希望大家多多支持本站。

  1. 上一頁:
  2. 下一頁:
熱門文章
閱讀排行版
Copyright © Android教程網 All Rights Reserved