基于Java Socket的自定义协议,实现Android与服务器的长连接(二)

本文涉及的产品
公共DNS(含HTTPDNS解析),每月1000万次HTTP解析
容器服务 Serverless 版 ACK Serverless,317元额度 多规格
容器服务 Serverless 版 ACK Serverless,952元额度 多规格
简介:

在阅读本文前需要对socket以及自定义协议有一个基本的了解,可以先查看上一篇文章《基于Java Socket的自定义协议,实现Android与服务器的长连接(一)》学习相关的基础知识点。

一、协议定义

上一篇文章中,我们对socket编程和自定义协议做了一个简单的了解,本文将在此基础上加以深入,来实现Android和服务器之间的长连接,现定义协议如下:

  • 数据类协议(Data)
    • 长度(length,32bit)
    • 版本号(version,8bit,前3位预留,后5位用于表示真正的版本号)
    • 数据类型(type,8bit,0表示数据)
    • 业务类型(pattion,8bit,0表示push,其他暂未定)
    • 数据格式(dtype,8bit,0表示json,其他暂未定)
    • 消息id(msgId,32bit)
    • 正文数据(data)
  • 数据ack类协议(DataAck)
    • 长度(length,32bit)
    • 版本号(version,8bit,前3位预留,后5位用于表示真正的版本号)
    • 数据类型(type,8bit,1表示数据ack)
    • ack消息id(ackMsgId,32bit)
    • 预留信息(unused)
  • 心跳类协议(ping)
    • 长度(length,32bit)
    • 版本号(version,8bit,前3位预留,后5位用于表示真正的版本号)
    • 数据类型(type,8bit,2表示心跳)
    • 心跳id(pingId,32bit,client上报取奇数,即1,3,5...,server下发取偶数,即0,2,4...)
    • 预留信息(unused)
  • 心跳ack类协议(pingAck)
    • 长度(length,32bit)
    • 版本号(version,8bit,前3位预留,后5位用于表示真正的版本号)
    • 数据类型(type,8bit,3表示心跳ack)
    • ack心跳id(pingId,32bit,client上报取奇数,即1,3,5...,server下发取偶数,即0,2,4...)
    • 预留信息(unused)

二、协议实现

从上述的协议定义中,我们可以看出,四种协议有共同的3个要素,分别是:长度、版本号、数据类型,那么我们可以先抽象出一个基本的协议,如下:

1. BasicProtocol

 
 
  1. import android.util.Log; 
  2.  
  3. import com.shandiangou.sdgprotocol.lib.Config; 
  4. import com.shandiangou.sdgprotocol.lib.ProtocolException; 
  5. import com.shandiangou.sdgprotocol.lib.SocketUtil; 
  6.  
  7. import java.io.ByteArrayOutputStream; 
  8.  
  9. /** 
  10.  * Created by meishan on 16/12/1. 
  11.  * <p> 
  12.  * 协议类型: 0表示数据,1表示数据Ack,2表示ping,3表示pingAck 
  13.  */ 
  14. public abstract class BasicProtocol { 
  15.  
  16.     // 长度均以字节(byte)为单位 
  17.     public static final int LENGTH_LEN = 4;       //记录整条数据长度数值的长度 
  18.     protected static final int VER_LEN = 1;       //协议的版本长度(其中前3位作为预留位,后5位作为版本号) 
  19.     protected static final int TYPE_LEN = 1;      //协议的数据类型长度 
  20.  
  21.     private int reserved = 0;                     //预留信息 
  22.     private int version = Config.VERSION;         //版本号 
  23.  
  24.     /** 
  25.      * 获取整条数据长度 
  26.      * 单位:字节(byte) 
  27.      * 
  28.      * @return 
  29.      */ 
  30.     protected int getLength() { 
  31.         return LENGTH_LEN + VER_LEN + TYPE_LEN; 
  32.     } 
  33.  
  34.     public int getReserved() { 
  35.         return reserved; 
  36.     } 
  37.  
  38.     public void setReserved(int reserved) { 
  39.         this.reserved = reserved; 
  40.     } 
  41.  
  42.     public int getVersion() { 
  43.         return version; 
  44.     } 
  45.  
  46.     public void setVersion(int version) { 
  47.         this.version = version; 
  48.     } 
  49.  
  50.     /** 
  51.      * 获取协议类型,由子类实现 
  52.      * 
  53.      * @return 
  54.      */ 
  55.     public abstract int getProtocolType(); 
  56.      
  57.     /** 
  58.      * 由预留值和版本号计算完整版本号的byte[]值 
  59.      * 
  60.      * @return 
  61.      */ 
  62.     private int getVer(byte r, byte v, int vLen) { 
  63.         int num = 0; 
  64.         int rLen = 8 - vLen; 
  65.         for (int i = 0; i < rLen; i++) { 
  66.             num += (((r >> (rLen - 1 - i)) & 0x1) << (7 - i)); 
  67.         } 
  68.         return num + v; 
  69.     } 
  70.  
  71.     /** 
  72.      * 拼接发送数据,此处拼接了协议版本、协议类型和数据长度,具体内容子类中再拼接 
  73.      * 按顺序拼接 
  74.      * 
  75.      * @return 
  76.      */ 
  77.     public byte[] genContentData() { 
  78.         byte[] length = SocketUtil.int2ByteArrays(getLength()); 
  79.         byte reserved = (byte) getReserved(); 
  80.         byte version = (byte) getVersion(); 
  81.         byte[] ver = {(byte) getVer(reserved, version, 5)}; 
  82.         byte[] type = {(byte) getProtocolType()}; 
  83.  
  84.         ByteArrayOutputStream baos = new ByteArrayOutputStream(LENGTH_LEN + VER_LEN + TYPE_LEN); 
  85.         baos.write(length, 0, LENGTH_LEN); 
  86.         baos.write(ver, 0, VER_LEN); 
  87.         baos.write(type, 0, TYPE_LEN); 
  88.         return baos.toByteArray(); 
  89.     } 
  90.  
  91.     /** 
  92.      * 解析出整条数据长度 
  93.      * 
  94.      * @param data 
  95.      * @return 
  96.      */ 
  97.     protected int parseLength(byte[] data) { 
  98.         return SocketUtil.byteArrayToInt(data, 0, LENGTH_LEN); 
  99.     } 
  100.  
  101.     /** 
  102.      * 解析出预留位 
  103.      * 
  104.      * @param data 
  105.      * @return 
  106.      */ 
  107.     protected int parseReserved(byte[] data) { 
  108.         byte r = data[LENGTH_LEN];//前4个字节(0,1,2,3)为数据长度的int值,与版本号组成一个字节 
  109.         return (r >> 5) & 0xFF; 
  110.     } 
  111.  
  112.     /** 
  113.      * 解析出版本号 
  114.      * 
  115.      * @param data 
  116.      * @return 
  117.      */ 
  118.     protected int parseVersion(byte[] data) { 
  119.         byte v = data[LENGTH_LEN]; //与预留位组成一个字节 
  120.         return ((v << 3) & 0xFF) >> 3; 
  121.     } 
  122.  
  123.     /** 
  124.      * 解析出协议类型 
  125.      * 
  126.      * @param data 
  127.      * @return 
  128.      */ 
  129.     public static int parseType(byte[] data) { 
  130.         byte t = data[LENGTH_LEN + VER_LEN];//前4个字节(0,1,2,3)为数据长度的int值,以及ver占一个字节 
  131.         return t & 0xFF; 
  132.     } 
  133.  
  134.     /** 
  135.      * 解析接收数据,此处解析了协议版本、协议类型和数据长度,具体内容子类中再解析 
  136.      * 
  137.      * @param data 
  138.      * @return 
  139.      * @throws ProtocolException 协议版本不一致,抛出异常 
  140.      */ 
  141.     public int parseContentData(byte[] data) throws ProtocolException { 
  142.         int reserved = parseReserved(data); 
  143.         int version = parseVersion(data); 
  144.         int protocolType = parseType(data); 
  145.         if (version != getVersion()) { 
  146.             throw new ProtocolException("input version is error: " + version); 
  147.         } 
  148.         return LENGTH_LEN + VER_LEN + TYPE_LEN; 
  149.     } 
  150.  
  151.     @Override 
  152.     public String toString() { 
  153.         return "Version: " + getVersion() + ", Type: " + getProtocolType(); 
  154.     } 
  155.  

上述涉及到的Config类和SocketUtil类如下:

 
 
  1. /** 
  2.  * Created by meishan on 16/12/2. 
  3.  */ 
  4. public class Config { 
  5.  
  6.     public static final int VERSION = 1;                 //协议版本号 
  7.     public static final String ADDRESS = "10.17.64.237"; //服务器地址 
  8.     public static final int PORT = 9013;                 //服务器端口号 
  9.      
  10.  
 
 
  1. import java.io.BufferedInputStream; 
  2. import java.io.BufferedOutputStream; 
  3. import java.io.IOException; 
  4. import java.io.InputStream; 
  5. import java.io.OutputStream; 
  6. import java.nio.ByteBuffer; 
  7. import java.util.HashMap; 
  8. import java.util.Map; 
  9.  
  10. /** 
  11.  * Created by meishan on 16/12/1. 
  12.  */ 
  13. public class SocketUtil { 
  14.  
  15.     private static Map<Integer, String> msgImp = new HashMap<>(); 
  16.  
  17.     static { 
  18.         msgImp.put(DataProtocol.PROTOCOL_TYPE, "com.shandiangou.sdgprotocol.lib.protocol.DataProtocol");       //0 
  19.         msgImp.put(DataAckProtocol.PROTOCOL_TYPE, "com.shandiangou.sdgprotocol.lib.protocol.DataAckProtocol"); //1 
  20.         msgImp.put(PingProtocol.PROTOCOL_TYPE, "com.shandiangou.sdgprotocol.lib.protocol.PingProtocol");       //2 
  21.         msgImp.put(PingAckProtocol.PROTOCOL_TYPE, "com.shandiangou.sdgprotocol.lib.protocol.PingAckProtocol"); //3 
  22.     } 
  23.  
  24.     /** 
  25.      * 解析数据内容 
  26.      * 
  27.      * @param data 
  28.      * @return 
  29.      */ 
  30.     public static BasicProtocol parseContentMsg(byte[] data) { 
  31.         int protocolType = BasicProtocol.parseType(data); 
  32.         String className = msgImp.get(protocolType); 
  33.         BasicProtocol basicProtocol; 
  34.         try { 
  35.             basicProtocol = (BasicProtocol) Class.forName(className).newInstance(); 
  36.             basicProtocol.parseContentData(data); 
  37.         } catch (Exception e) { 
  38.             basicProtocol = null
  39.             e.printStackTrace(); 
  40.         } 
  41.         return basicProtocol; 
  42.     } 
  43.  
  44.     /** 
  45.      * 读数据 
  46.      * 
  47.      * @param inputStream 
  48.      * @return 
  49.      * @throws SocketExceptions 
  50.      */ 
  51.     public static BasicProtocol readFromStream(InputStream inputStream) { 
  52.         BasicProtocol protocol; 
  53.         BufferedInputStream bis; 
  54.          
  55.         //header中保存的是整个数据的长度值,4个字节表示。在下述write2Stream方法中,会先写入header 
  56.         byte[] header = new byte[BasicProtocol.LENGTH_LEN]; 
  57.  
  58.         try { 
  59.             bis = new BufferedInputStream(inputStream); 
  60.  
  61.             int temp
  62.             int len = 0; 
  63.             while (len < header.length) { 
  64.                 temp = bis.read(header, len, header.length - len); 
  65.                 if (temp > 0) { 
  66.                     len += temp
  67.                 } else if (temp == -1) { 
  68.                     bis.close(); 
  69.                     return null
  70.                 } 
  71.             } 
  72.  
  73.             len = 0; 
  74.             int length = byteArrayToInt(header);//数据的长度值 
  75.             byte[] content = new byte[length]; 
  76.             while (len < length) { 
  77.                 temp = bis.read(content, len, length - len); 
  78.  
  79.                 if (temp > 0) { 
  80.                     len += temp
  81.                 } 
  82.             } 
  83.  
  84.             protocol = parseContentMsg(content); 
  85.         } catch (IOException e) { 
  86.             e.printStackTrace(); 
  87.             return null
  88.         } 
  89.  
  90.         return protocol; 
  91.     } 
  92.  
  93.     /** 
  94.      * 写数据 
  95.      * 
  96.      * @param protocol 
  97.      * @param outputStream 
  98.      */ 
  99.     public static void write2Stream(BasicProtocol protocol, OutputStream outputStream) { 
  100.         BufferedOutputStream bufferedOutputStream = new BufferedOutputStream(outputStream); 
  101.         byte[] buffData = protocol.genContentData(); 
  102.         byte[] header = int2ByteArrays(buffData.length); 
  103.         try { 
  104.             bufferedOutputStream.write(header); 
  105.             bufferedOutputStream.write(buffData); 
  106.             bufferedOutputStream.flush(); 
  107.         } catch (IOException e) { 
  108.             e.printStackTrace(); 
  109.         } 
  110.     } 
  111.  
  112.     /** 
  113.      * 关闭输入流 
  114.      * 
  115.      * @param is 
  116.      */ 
  117.     public static void closeInputStream(InputStream is) { 
  118.         try { 
  119.             if (is != null) { 
  120.                 is.close(); 
  121.             } 
  122.         } catch (IOException e) { 
  123.             e.printStackTrace(); 
  124.         } 
  125.     } 
  126.  
  127.     /** 
  128.      * 关闭输出流 
  129.      * 
  130.      * @param os 
  131.      */ 
  132.     public static void closeOutputStream(OutputStream os) { 
  133.         try { 
  134.             if (os != null) { 
  135.                 os.close(); 
  136.             } 
  137.         } catch (IOException e) { 
  138.             e.printStackTrace(); 
  139.         } 
  140.     } 
  141.  
  142.     public static byte[] int2ByteArrays(int i) { 
  143.         byte[] result = new byte[4]; 
  144.         result[0] = (byte) ((i >> 24) & 0xFF); 
  145.         result[1] = (byte) ((i >> 16) & 0xFF); 
  146.         result[2] = (byte) ((i >> 8) & 0xFF); 
  147.         result[3] = (byte) (i & 0xFF); 
  148.         return result; 
  149.     } 
  150.  
  151.     public static int byteArrayToInt(byte[] b) { 
  152.         int intValue = 0; 
  153.         for (int i = 0; i < b.length; i++) { 
  154.             intValue += (b[i] & 0xFF) << (8 * (3 - i)); //int占4个字节(0,1,2,3) 
  155.         } 
  156.         return intValue; 
  157.     } 
  158.  
  159.     public static int byteArrayToInt(byte[] b, int byteOffset, int byteCount) { 
  160.         int intValue = 0; 
  161.         for (int i = byteOffset; i < (byteOffset + byteCount); i++) { 
  162.             intValue += (b[i] & 0xFF) << (8 * (3 - (i - byteOffset))); 
  163.         } 
  164.         return intValue; 
  165.     } 
  166.  
  167.     public static int bytes2Int(byte[] b, int byteOffset) { 
  168.         ByteBuffer byteBuffer = ByteBuffer.allocate(Integer.SIZE / Byte.SIZE); 
  169.         byteBuffer.put(b, byteOffset, 4); //占4个字节 
  170.         byteBuffer.flip(); 
  171.         return byteBuffer.getInt(); 
  172.     } 
  173.  

接下来我们实现具体的协议。

2. DataProtocol

 
 
  1. import android.util.Log; 
  2.  
  3. import com.shandiangou.sdgprotocol.lib.ProtocolException; 
  4. import com.shandiangou.sdgprotocol.lib.SocketUtil; 
  5.  
  6. import java.io.ByteArrayOutputStream; 
  7. import java.io.Serializable
  8. import java.io.UnsupportedEncodingException; 
  9.  
  10. /** 
  11.  * Created by meishan on 16/12/1. 
  12.  */ 
  13. public class DataProtocol extends BasicProtocol implements Serializable { 
  14.  
  15.     public static final int PROTOCOL_TYPE = 0; 
  16.  
  17.     private static final int PATTION_LEN = 1; 
  18.     private static final int DTYPE_LEN = 1; 
  19.     private static final int MSGID_LEN = 4; 
  20.  
  21.     private int pattion; 
  22.     private int dtype; 
  23.     private int msgId; 
  24.  
  25.     private String data; 
  26.  
  27.     @Override 
  28.     public int getLength() { 
  29.         return super.getLength() + PATTION_LEN + DTYPE_LEN + MSGID_LEN + data.getBytes().length; 
  30.     } 
  31.  
  32.     @Override 
  33.     public int getProtocolType() { 
  34.         return PROTOCOL_TYPE; 
  35.     } 
  36.  
  37.     public int getPattion() { 
  38.         return pattion; 
  39.     } 
  40.  
  41.     public void setPattion(int pattion) { 
  42.         this.pattion = pattion; 
  43.     } 
  44.  
  45.     public int getDtype() { 
  46.         return dtype; 
  47.     } 
  48.  
  49.     public void setDtype(int dtype) { 
  50.         this.dtype = dtype; 
  51.     } 
  52.  
  53.     public void setMsgId(int msgId) { 
  54.         this.msgId = msgId; 
  55.     } 
  56.  
  57.     public int getMsgId() { 
  58.         return msgId; 
  59.     } 
  60.  
  61.     public String getData() { 
  62.         return data; 
  63.     } 
  64.  
  65.     public void setData(String data) { 
  66.         this.data = data; 
  67.     } 
  68.  
  69.     /** 
  70.      * 拼接发送数据 
  71.      * 
  72.      * @return 
  73.      */ 
  74.     @Override 
  75.     public byte[] genContentData() { 
  76.         byte[] base = super.genContentData(); 
  77.         byte[] pattion = {(byte) this.pattion}; 
  78.         byte[] dtype = {(byte) this.dtype}; 
  79.         byte[] msgid = SocketUtil.int2ByteArrays(this.msgId); 
  80.         byte[] data = this.data.getBytes(); 
  81.  
  82.         ByteArrayOutputStream baos = new ByteArrayOutputStream(getLength()); 
  83.         baos.write(base, 0, base.length);          //协议版本+数据类型+数据长度+消息id 
  84.         baos.write(pattion, 0, PATTION_LEN);       //业务类型 
  85.         baos.write(dtype, 0, DTYPE_LEN);           //业务数据格式 
  86.         baos.write(msgid, 0, MSGID_LEN);           //消息id 
  87.         baos.write(data, 0, data.length);          //业务数据 
  88.         return baos.toByteArray(); 
  89.     } 
  90.  
  91.     /** 
  92.      * 解析接收数据,按顺序解析 
  93.      * 
  94.      * @param data 
  95.      * @return 
  96.      * @throws ProtocolException 
  97.      */ 
  98.     @Override 
  99.     public int parseContentData(byte[] data) throws ProtocolException { 
  100.         int pos = super.parseContentData(data); 
  101.  
  102.         //解析pattion 
  103.         pattion = data[pos] & 0xFF; 
  104.         pos += PATTION_LEN; 
  105.  
  106.         //解析dtype 
  107.         dtype = data[pos] & 0xFF; 
  108.         pos += DTYPE_LEN; 
  109.  
  110.         //解析msgId 
  111.         msgId = SocketUtil.byteArrayToInt(data, pos, MSGID_LEN); 
  112.         pos += MSGID_LEN; 
  113.  
  114.         //解析data 
  115.         try { 
  116.             this.data = new String(data, pos, data.length - pos, "utf-8"); 
  117.         } catch (UnsupportedEncodingException e) { 
  118.             e.printStackTrace(); 
  119.         } 
  120.  
  121.         return pos; 
  122.     } 
  123.  
  124.     @Override 
  125.     public String toString() { 
  126.         return "data: " + data; 
  127.     } 
  128.  

3. DataAckProtocol

 
 
  1. import com.shandiangou.sdgprotocol.lib.ProtocolException; 
  2. import com.shandiangou.sdgprotocol.lib.SocketUtil; 
  3.  
  4. import java.io.ByteArrayOutputStream; 
  5. import java.io.UnsupportedEncodingException; 
  6.  
  7. /** 
  8.  * Created by meishan on 16/12/1. 
  9.  */ 
  10. public class DataAckProtocol extends BasicProtocol { 
  11.  
  12.     public static final int PROTOCOL_TYPE = 1; 
  13.  
  14.     private static final int ACKMSGID_LEN = 4; 
  15.  
  16.     private int ackMsgId; 
  17.  
  18.     private String unused; 
  19.  
  20.     @Override 
  21.     public int getLength() { 
  22.         return super.getLength() + ACKMSGID_LEN + unused.getBytes().length; 
  23.     } 
  24.  
  25.     @Override 
  26.     public int getProtocolType() { 
  27.         return PROTOCOL_TYPE; 
  28.     } 
  29.  
  30.     public int getAckMsgId() { 
  31.         return ackMsgId; 
  32.     } 
  33.  
  34.     public void setAckMsgId(int ackMsgId) { 
  35.         this.ackMsgId = ackMsgId; 
  36.     } 
  37.  
  38.     public String getUnused() { 
  39.         return unused; 
  40.     } 
  41.  
  42.     public void setUnused(String unused) { 
  43.         this.unused = unused; 
  44.     } 
  45.  
  46.     /** 
  47.      * 拼接发送数据 
  48.      * 
  49.      * @return 
  50.      */ 
  51.     @Override 
  52.     public byte[] genContentData() { 
  53.         byte[] base = super.genContentData(); 
  54.         byte[] ackMsgId = SocketUtil.int2ByteArrays(this.ackMsgId); 
  55.         byte[] unused = this.unused.getBytes(); 
  56.  
  57.         ByteArrayOutputStream baos = new ByteArrayOutputStream(getLength()); 
  58.         baos.write(base, 0, base.length);              //协议版本+数据类型+数据长度+消息id 
  59.         baos.write(ackMsgId, 0, ACKMSGID_LEN);         //消息id 
  60.         baos.write(unused, 0, unused.length);          //unused 
  61.         return baos.toByteArray(); 
  62.     } 
  63.  
  64.     @Override 
  65.     public int parseContentData(byte[] data) throws ProtocolException { 
  66.         int pos = super.parseContentData(data); 
  67.  
  68.         //解析ackMsgId 
  69.         ackMsgId = SocketUtil.byteArrayToInt(data, pos, ACKMSGID_LEN); 
  70.         pos += ACKMSGID_LEN; 
  71.  
  72.         //解析unused 
  73.         try { 
  74.             unused = new String(data, pos, data.length - pos, "utf-8"); 
  75.         } catch (UnsupportedEncodingException e) { 
  76.             e.printStackTrace(); 
  77.         } 
  78.  
  79.         return pos; 
  80.     } 
  81.  
  82.  

4. PingProtocol

 
 
  1. import com.shandiangou.sdgprotocol.lib.ProtocolException; 
  2. import com.shandiangou.sdgprotocol.lib.SocketUtil; 
  3.  
  4. import java.io.ByteArrayOutputStream; 
  5. import java.io.UnsupportedEncodingException; 
  6.  
  7. /** 
  8.  * Created by meishan on 16/12/1. 
  9.  */ 
  10. public class PingProtocol extends BasicProtocol { 
  11.  
  12.     public static final int PROTOCOL_TYPE = 2; 
  13.  
  14.     private static final int PINGID_LEN = 4; 
  15.  
  16.     private int pingId; 
  17.  
  18.     private String unused; 
  19.  
  20.     @Override 
  21.     public int getLength() { 
  22.         return super.getLength() + PINGID_LEN + unused.getBytes().length; 
  23.     } 
  24.  
  25.     @Override 
  26.     public int getProtocolType() { 
  27.         return PROTOCOL_TYPE; 
  28.     } 
  29.  
  30.     public int getPingId() { 
  31.         return pingId; 
  32.     } 
  33.  
  34.     public void setPingId(int pingId) { 
  35.         this.pingId = pingId; 
  36.     } 
  37.  
  38.     public String getUnused() { 
  39.         return unused; 
  40.     } 
  41.  
  42.     public void setUnused(String unused) { 
  43.         this.unused = unused; 
  44.     } 
  45.  
  46.     /** 
  47.      * 拼接发送数据 
  48.      * 
  49.      * @return 
  50.      */ 
  51.     @Override 
  52.     public byte[] genContentData() { 
  53.         byte[] base = super.genContentData(); 
  54.         byte[] pingId = SocketUtil.int2ByteArrays(this.pingId); 
  55.         byte[] unused = this.unused.getBytes(); 
  56.  
  57.         ByteArrayOutputStream baos = new ByteArrayOutputStream(getLength()); 
  58.         baos.write(base, 0, base.length);          //协议版本+数据类型+数据长度+消息id 
  59.         baos.write(pingId, 0, PINGID_LEN);         //消息id 
  60.         baos.write(unused, 0, unused.length);            //unused 
  61.         return baos.toByteArray(); 
  62.     } 
  63.  
  64.     @Override 
  65.     public int parseContentData(byte[] data) throws ProtocolException { 
  66.         int pos = super.parseContentData(data); 
  67.  
  68.         //解析pingId 
  69.         pingId = SocketUtil.byteArrayToInt(data, pos, PINGID_LEN); 
  70.         pos += PINGID_LEN; 
  71.  
  72.         try { 
  73.             unused = new String(data, pos, data.length - pos, "utf-8"); 
  74.         } catch (UnsupportedEncodingException e) { 
  75.             e.printStackTrace(); 
  76.         } 
  77.  
  78.         return pos; 
  79.     } 
  80.  
  81.  

5. PingAckProtocol

 
 
  1. import com.shandiangou.sdgprotocol.lib.ProtocolException; 
  2. import com.shandiangou.sdgprotocol.lib.SocketUtil; 
  3.  
  4. import java.io.ByteArrayOutputStream; 
  5. import java.io.UnsupportedEncodingException; 
  6.  
  7. /** 
  8.  * Created by meishan on 16/12/1. 
  9.  */ 
  10. public class PingAckProtocol extends BasicProtocol { 
  11.  
  12.     public static final int PROTOCOL_TYPE = 3; 
  13.  
  14.     private static final int ACKPINGID_LEN = 4; 
  15.  
  16.     private int ackPingId; 
  17.  
  18.     private String unused; 
  19.  
  20.     @Override 
  21.     public int getLength() { 
  22.         return super.getLength() + ACKPINGID_LEN + unused.getBytes().length; 
  23.     } 
  24.  
  25.     @Override 
  26.     public int getProtocolType() { 
  27.         return PROTOCOL_TYPE; 
  28.     } 
  29.  
  30.     public int getAckPingId() { 
  31.         return ackPingId; 
  32.     } 
  33.  
  34.     public void setAckPingId(int ackPingId) { 
  35.         this.ackPingId = ackPingId; 
  36.     } 
  37.  
  38.     public String getUnused() { 
  39.         return unused; 
  40.     } 
  41.  
  42.     public void setUnused(String unused) { 
  43.         this.unused = unused; 
  44.     } 
  45.  
  46.     /** 
  47.      * 拼接发送数据 
  48.      * 
  49.      * @return 
  50.      */ 
  51.     @Override 
  52.     public byte[] genContentData() { 
  53.         byte[] base = super.genContentData(); 
  54.         byte[] ackPingId = SocketUtil.int2ByteArrays(this.ackPingId); 
  55.         byte[] unused = this.unused.getBytes(); 
  56.  
  57.         ByteArrayOutputStream baos = new ByteArrayOutputStream(getLength()); 
  58.         baos.write(base, 0, base.length);                //协议版本+数据类型+数据长度+消息id 
  59.         baos.write(ackPingId, 0, ACKPINGID_LEN);         //消息id 
  60.         baos.write(unused, 0, unused.length);            //unused 
  61.         return baos.toByteArray(); 
  62.     } 
  63.  
  64.     @Override 
  65.     public int parseContentData(byte[] data) throws ProtocolException { 
  66.         int pos = super.parseContentData(data); 
  67.  
  68.         //解析ackPingId 
  69.         ackPingId = SocketUtil.byteArrayToInt(data, pos, ACKPINGID_LEN); 
  70.         pos += ACKPINGID_LEN; 
  71.  
  72.         //解析unused 
  73.         try { 
  74.             unused = new String(data, pos, data.length - pos, "utf-8"); 
  75.         } catch (UnsupportedEncodingException e) { 
  76.             e.printStackTrace(); 
  77.         } 
  78.  
  79.         return pos; 
  80.     } 
  81.  
  82.  

三、任务调度

上述已经给出了四种协议的实现,接下来我们将使用它们来实现app和服务端之间的通信,这里我们把数据的发送、接收和心跳分别用一个线程去实现,具体如下:

1. 客户端

 
 
  1. import android.os.Handler; 
  2. import android.os.Looper; 
  3. import android.os.Message; 
  4. import android.util.Log; 
  5.  
  6. import com.shandiangou.sdgprotocol.lib.protocol.BasicProtocol; 
  7. import com.shandiangou.sdgprotocol.lib.protocol.DataProtocol; 
  8. import com.shandiangou.sdgprotocol.lib.protocol.PingProtocol; 
  9.  
  10. import java.io.IOException; 
  11. import java.io.InputStream; 
  12. import java.io.OutputStream; 
  13. import java.net.ConnectException; 
  14. import java.net.Socket; 
  15. import java.util.concurrent.ConcurrentLinkedQueue; 
  16.  
  17. import javax.net.SocketFactory; 
  18.  
  19. /** 
  20.  * 写数据采用死循环,没有数据时wait,有新消息时notify 
  21.  * <p> 
  22.  * Created by meishan on 16/12/1. 
  23.  */ 
  24. public class ClientRequestTask implements Runnable { 
  25.  
  26.     private static final int SUCCESS = 100; 
  27.     private static final int FAILED = -1; 
  28.  
  29.     private boolean isLongConnection = true
  30.     private Handler mHandler; 
  31.     private SendTask mSendTask; 
  32.     private ReciveTask mReciveTask; 
  33.     private HeartBeatTask mHeartBeatTask; 
  34.     private Socket mSocket; 
  35.  
  36.     private boolean isSocketAvailable; 
  37.     private boolean closeSendTask; 
  38.  
  39.     protected volatile ConcurrentLinkedQueue<BasicProtocol> dataQueue = new ConcurrentLinkedQueue<>(); 
  40.  
  41.     public ClientRequestTask(RequestCallBack requestCallBacks) { 
  42.         mHandler = new MyHandler(requestCallBacks); 
  43.     } 
  44.  
  45.     @Override 
  46.     public void run() { 
  47.         try { 
  48.             try { 
  49.                 mSocket = SocketFactory.getDefault().createSocket(Config.ADDRESS, Config.PORT); 
  50. //                mSocket.setSoTimeout(10); 
  51.             } catch (ConnectException e) { 
  52.                 failedMessage(-1, "服务器连接异常,请检查网络"); 
  53.                 return
  54.             } 
  55.  
  56.             isSocketAvailable = true
  57.  
  58.             //开启接收线程 
  59.             mReciveTask = new ReciveTask(); 
  60.             mReciveTask.inputStream = mSocket.getInputStream(); 
  61.             mReciveTask.start(); 
  62.  
  63.             //开启发送线程 
  64.             mSendTask = new SendTask(); 
  65.             mSendTask.outputStream = mSocket.getOutputStream(); 
  66.             mSendTask.start(); 
  67.  
  68.             //开启心跳线程 
  69.             if (isLongConnection) { 
  70.                 mHeartBeatTask = new HeartBeatTask(); 
  71.                 mHeartBeatTask.outputStream = mSocket.getOutputStream(); 
  72.                 mHeartBeatTask.start(); 
  73.             } 
  74.         } catch (IOException e) { 
  75.             failedMessage(-1, "网络发生异常,请稍后重试"); 
  76.             e.printStackTrace(); 
  77.         } 
  78.     } 
  79.  
  80.     public void addRequest(DataProtocol data) { 
  81.         dataQueue.add(data); 
  82.         toNotifyAll(dataQueue);//有新增待发送数据,则唤醒发送线程 
  83.     } 
  84.  
  85.     public synchronized void stop() { 
  86.  
  87.         //关闭接收线程 
  88.         closeReciveTask(); 
  89.  
  90.         //关闭发送线程 
  91.         closeSendTask = true
  92.         toNotifyAll(dataQueue); 
  93.  
  94.         //关闭心跳线程 
  95.         closeHeartBeatTask(); 
  96.  
  97.         //关闭socket 
  98.         closeSocket(); 
  99.  
  100.         //清除数据 
  101.         clearData(); 
  102.  
  103.         failedMessage(-1, "断开连接"); 
  104.     } 
  105.  
  106.     /** 
  107.      * 关闭接收线程 
  108.      */ 
  109.     private void closeReciveTask() { 
  110.         if (mReciveTask != null) { 
  111.             mReciveTask.interrupt(); 
  112.             mReciveTask.isCancle = true
  113.             if (mReciveTask.inputStream != null) { 
  114.                 try { 
  115.                     if (isSocketAvailable && !mSocket.isClosed() && mSocket.isConnected()) { 
  116.                         mSocket.shutdownInput();//解决java.net.SocketException问题,需要先shutdownInput 
  117.                     } 
  118.                 } catch (IOException e) { 
  119.                     e.printStackTrace(); 
  120.                 } 
  121.                 SocketUtil.closeInputStream(mReciveTask.inputStream); 
  122.                 mReciveTask.inputStream = null
  123.             } 
  124.             mReciveTask = null
  125.         } 
  126.     } 
  127.  
  128.     /** 
  129.      * 关闭发送线程 
  130.      */ 
  131.     private void closeSendTask() { 
  132.         if (mSendTask != null) { 
  133.             mSendTask.isCancle = true
  134.             mSendTask.interrupt(); 
  135.             if (mSendTask.outputStream != null) { 
  136.                 synchronized (mSendTask.outputStream) {//防止写数据时停止,写完再停 
  137.                     SocketUtil.closeOutputStream(mSendTask.outputStream); 
  138.                     mSendTask.outputStream = null
  139.                 } 
  140.             } 
  141.             mSendTask = null
  142.         } 
  143.     } 
  144.  
  145.     /** 
  146.      * 关闭心跳线程 
  147.      */ 
  148.     private void closeHeartBeatTask() { 
  149.         if (mHeartBeatTask != null) { 
  150.             mHeartBeatTask.isCancle = true
  151.             if (mHeartBeatTask.outputStream != null) { 
  152.                 SocketUtil.closeOutputStream(mHeartBeatTask.outputStream); 
  153.                 mHeartBeatTask.outputStream = null
  154.             } 
  155.             mHeartBeatTask = null
  156.         } 
  157.     } 
  158.  
  159.     /** 
  160.      * 关闭socket 
  161.      */ 
  162.     private void closeSocket() { 
  163.         if (mSocket != null) { 
  164.             try { 
  165.                 mSocket.close(); 
  166.                 isSocketAvailable = false
  167.             } catch (IOException e) { 
  168.                 e.printStackTrace(); 
  169.             } 
  170.         } 
  171.     } 
  172.  
  173.     /** 
  174.      * 清除数据 
  175.      */ 
  176.     private void clearData() { 
  177.         dataQueue.clear(); 
  178.         isLongConnection = false
  179.     } 
  180.  
  181.     private void toWait(Object o) { 
  182.         synchronized (o) { 
  183.             try { 
  184.                 o.wait(); 
  185.             } catch (InterruptedException e) { 
  186.                 e.printStackTrace(); 
  187.             } 
  188.         } 
  189.     } 
  190.  
  191.     /** 
  192.      * notify()调用后,并不是马上就释放对象锁的,而是在相应的synchronized(){}语句块执行结束,自动释放锁后 
  193.      * 
  194.      * @param o 
  195.      */ 
  196.     protected void toNotifyAll(Object o) { 
  197.         synchronized (o) { 
  198.             o.notifyAll(); 
  199.         } 
  200.     } 
  201.  
  202.     private void failedMessage(int code, String msg) { 
  203.         Message message = mHandler.obtainMessage(FAILED); 
  204.         message.what = FAILED; 
  205.         message.arg1 = code; 
  206.         message.obj = msg; 
  207.         mHandler.sendMessage(message); 
  208.     } 
  209.  
  210.     private void successMessage(BasicProtocol protocol) { 
  211.         Message message = mHandler.obtainMessage(SUCCESS); 
  212.         message.what = SUCCESS; 
  213.         message.obj = protocol; 
  214.         mHandler.sendMessage(message); 
  215.     } 
  216.  
  217.     private boolean isConnected() { 
  218.         if (mSocket.isClosed() || !mSocket.isConnected()) { 
  219.             ClientRequestTask.this.stop(); 
  220.             return false
  221.         } 
  222.         return true
  223.     } 
  224.  
  225.     /** 
  226.      * 服务器返回处理,主线程运行 
  227.      */ 
  228.     public class MyHandler extends Handler { 
  229.  
  230.         private RequestCallBack mRequestCallBack; 
  231.  
  232.         public MyHandler(RequestCallBack callBack) { 
  233.             super(Looper.getMainLooper()); 
  234.             this.mRequestCallBack = callBack; 
  235.         } 
  236.  
  237.         @Override 
  238.         public void handleMessage(Message msg) { 
  239.             super.handleMessage(msg); 
  240.             switch (msg.what) { 
  241.                 case SUCCESS: 
  242.                     mRequestCallBack.onSuccess((BasicProtocol) msg.obj); 
  243.                     break; 
  244.                 case FAILED: 
  245.                     mRequestCallBack.onFailed(msg.arg1, (String) msg.obj); 
  246.                     break; 
  247.                 default
  248.                     break; 
  249.             } 
  250.         } 
  251.     } 
  252.  
  253.     /** 
  254.      * 数据接收线程 
  255.      */ 
  256.     public class ReciveTask extends Thread { 
  257.  
  258.         private boolean isCancle = false
  259.         private InputStream inputStream; 
  260.  
  261.         @Override 
  262.         public void run() { 
  263.             while (!isCancle) { 
  264.                 if (!isConnected()) { 
  265.                     break; 
  266.                 } 
  267.  
  268.                 if (inputStream != null) { 
  269.                     BasicProtocol reciverData = SocketUtil.readFromStream(inputStream); 
  270.                     if (reciverData != null) { 
  271.                         if (reciverData.getProtocolType() == 1 || reciverData.getProtocolType() == 3) { 
  272.                             successMessage(reciverData); 
  273.                         } 
  274.                     } else { 
  275.                         break; 
  276.                     } 
  277.                 } 
  278.             } 
  279.  
  280.             SocketUtil.closeInputStream(inputStream);//循环结束则退出输入流 
  281.         } 
  282.     } 
  283.  
  284.     /** 
  285.      * 数据发送线程 
  286.      * 当没有发送数据时让线程等待 
  287.      */ 
  288.     public class SendTask extends Thread { 
  289.  
  290.         private boolean isCancle = false
  291.         private OutputStream outputStream; 
  292.  
  293.         @Override 
  294.         public void run() { 
  295.             while (!isCancle) { 
  296.                 if (!isConnected()) { 
  297.                     break; 
  298.                 } 
  299.  
  300.                 BasicProtocol dataContent = dataQueue.poll(); 
  301.                 if (dataContent == null) { 
  302.                     toWait(dataQueue);//没有发送数据则等待 
  303.                     if (closeSendTask) { 
  304.                         closeSendTask();//notify()调用后,并不是马上就释放对象锁的,所以在此处中断发送线程 
  305.                     } 
  306.                 } else if (outputStream != null) { 
  307.                     synchronized (outputStream) { 
  308.                         SocketUtil.write2Stream(dataContent, outputStream); 
  309.                     } 
  310.                 } 
  311.             } 
  312.  
  313.             SocketUtil.closeOutputStream(outputStream);//循环结束则退出输出流 
  314.         } 
  315.     } 
  316.  
  317.     /** 
  318.      * 心跳实现,频率5秒 
  319.      * Created by meishan on 16/12/1. 
  320.      */ 
  321.     public class HeartBeatTask extends Thread { 
  322.  
  323.         private static final int REPEATTIME = 5000; 
  324.         private boolean isCancle = false
  325.         private OutputStream outputStream; 
  326.         private int pingId; 
  327.  
  328.         @Override 
  329.         public void run() { 
  330.             pingId = 1; 
  331.             while (!isCancle) { 
  332.                 if (!isConnected()) { 
  333.                     break; 
  334.                 } 
  335.  
  336.                 try { 
  337.                     mSocket.sendUrgentData(0xFF); 
  338.                 } catch (IOException e) { 
  339.                     isSocketAvailable = false
  340.                     ClientRequestTask.this.stop(); 
  341.                     break; 
  342.                 } 
  343.  
  344.                 if (outputStream != null) { 
  345.                     PingProtocol pingProtocol = new PingProtocol(); 
  346.                     pingProtocol.setPingId(pingId); 
  347.                     pingProtocol.setUnused("ping..."); 
  348.                     SocketUtil.write2Stream(pingProtocol, outputStream); 
  349.                     pingId = pingId + 2; 
  350.                 } 
  351.  
  352.                 try { 
  353.                     Thread.sleep(REPEATTIME); 
  354.                 } catch (InterruptedException e) { 
  355.                     e.printStackTrace(); 
  356.                 } 
  357.             } 
  358.  
  359.             SocketUtil.closeOutputStream(outputStream); 
  360.         } 
  361.     } 
  362.  

其中涉及到的RequestCallBack接口如下:

 
 
  1. /** 
  2.  * Created by meishan on 16/12/1. 
  3.  */ 
  4. public interface RequestCallBack { 
  5.  
  6.     void onSuccess(BasicProtocol msg); 
  7.  
  8.     void onFailed(int errorCode, String msg); 
  9.  

2. 服务端

 
 
  1. import java.io.DataInputStream; 
  2. import java.io.DataOutputStream; 
  3. import java.net.Socket; 
  4. import java.util.Iterator; 
  5. import java.util.concurrent.ConcurrentHashMap; 
  6. import java.util.concurrent.ConcurrentLinkedQueue; 
  7.  
  8. /** 
  9.  * Created by meishan on 16/12/1. 
  10.  */ 
  11. public class ServerResponseTask implements Runnable { 
  12.  
  13.     private ReciveTask reciveTask; 
  14.     private SendTask sendTask; 
  15.     private Socket socket; 
  16.     private ResponseCallback tBack; 
  17.  
  18.     private volatile ConcurrentLinkedQueue<BasicProtocol> dataQueue = new ConcurrentLinkedQueue<>(); 
  19.     private static ConcurrentHashMap<String, Socket> onLineClient = new ConcurrentHashMap<>(); 
  20.  
  21.     private String userIP; 
  22.  
  23.     public String getUserIP() { 
  24.         return userIP; 
  25.     } 
  26.  
  27.     public ServerResponseTask(Socket socket, ResponseCallback tBack) { 
  28.         this.socket = socket; 
  29.         this.tBack = tBack; 
  30.         this.userIP = socket.getInetAddress().getHostAddress(); 
  31.         System.out.println("用户IP地址:" + userIP); 
  32.     } 
  33.  
  34.     @Override 
  35.     public void run() { 
  36.         try { 
  37.             //开启接收线程 
  38.             reciveTask = new ReciveTask(); 
  39.             reciveTask.inputStream = new DataInputStream(socket.getInputStream()); 
  40.             reciveTask.start(); 
  41.  
  42.             //开启发送线程 
  43.             sendTask = new SendTask(); 
  44.             sendTask.outputStream = new DataOutputStream(socket.getOutputStream()); 
  45.             sendTask.start(); 
  46.         } catch (Exception e) { 
  47.             e.printStackTrace(); 
  48.         } 
  49.     } 
  50.  
  51.     public void stop() { 
  52.         if (reciveTask != null) { 
  53.             reciveTask.isCancle = true
  54.             reciveTask.interrupt(); 
  55.             if (reciveTask.inputStream != null) { 
  56.                 SocketUtil.closeInputStream(reciveTask.inputStream); 
  57.                 reciveTask.inputStream = null
  58.             } 
  59.             reciveTask = null
  60.         } 
  61.  
  62.         if (sendTask != null) { 
  63.             sendTask.isCancle = true
  64.             sendTask.interrupt(); 
  65.             if (sendTask.outputStream != null) { 
  66.                 synchronized (sendTask.outputStream) {//防止写数据时停止,写完再停 
  67.                     sendTask.outputStream = null
  68.                 } 
  69.             } 
  70.             sendTask = null
  71.         } 
  72.     } 
  73.  
  74.     public void addMessage(BasicProtocol data) { 
  75.         if (!isConnected()) { 
  76.             return
  77.         } 
  78.  
  79.         dataQueue.offer(data); 
  80.         toNotifyAll(dataQueue);//有新增待发送数据,则唤醒发送线程 
  81.     } 
  82.  
  83.     public Socket getConnectdClient(String clientID) { 
  84.         return onLineClient.get(clientID); 
  85.     } 
  86.  
  87.     /** 
  88.      * 打印已经链接的客户端 
  89.      */ 
  90.     public static void printAllClient() { 
  91.         if (onLineClient == null) { 
  92.             return
  93.         } 
  94.         Iterator<String> inter = onLineClient.keySet().iterator(); 
  95.         while (inter.hasNext()) { 
  96.             System.out.println("client:" + inter.next()); 
  97.         } 
  98.     } 
  99.  
  100.     public void toWaitAll(Object o) { 
  101.         synchronized (o) { 
  102.             try { 
  103.                 o.wait(); 
  104.             } catch (InterruptedException e) { 
  105.                 e.printStackTrace(); 
  106.             } 
  107.         } 
  108.     } 
  109.  
  110.     public void toNotifyAll(Object obj) { 
  111.         synchronized (obj) { 
  112.             obj.notifyAll(); 
  113.         } 
  114.     } 
  115.  
  116.     private boolean isConnected() { 
  117.         if (socket.isClosed() || !socket.isConnected()) { 
  118.             onLineClient.remove(userIP); 
  119.             ServerResponseTask.this.stop(); 
  120.             System.out.println("socket closed..."); 
  121.             return false
  122.         } 
  123.         return true
  124.     } 
  125.  
  126.     public class ReciveTask extends Thread { 
  127.  
  128.         private DataInputStream inputStream; 
  129.         private boolean isCancle; 
  130.  
  131.         @Override 
  132.         public void run() { 
  133.             while (!isCancle) { 
  134.                 if (!isConnected()) { 
  135.                     isCancle = true
  136.                     break; 
  137.                 } 
  138.  
  139.                 BasicProtocol clientData = SocketUtil.readFromStream(inputStream); 
  140.  
  141.                 if (clientData != null) { 
  142.                     if (clientData.getProtocolType() == 0) { 
  143.                         System.out.println("dtype: " + ((DataProtocol) clientData).getDtype() + ", pattion: " + ((DataProtocol) clientData).getPattion() + ", msgId: " + ((DataProtocol) clientData).getMsgId() + ", data: " + ((DataProtocol) clientData).getData()); 
  144.  
  145.                         DataAckProtocol dataAck = new DataAckProtocol(); 
  146.                         dataAck.setUnused("收到消息:" + ((DataProtocol) clientData).getData()); 
  147.                         dataQueue.offer(dataAck); 
  148.                         toNotifyAll(dataQueue); //唤醒发送线程 
  149.  
  150.                         tBack.targetIsOnline(userIP); 
  151.                     } else if (clientData.getProtocolType() == 2) { 
  152.                         System.out.println("pingId: " + ((PingProtocol) clientData).getPingId()); 
  153.  
  154.                         PingAckProtocol pingAck = new PingAckProtocol(); 
  155.                         pingAck.setUnused("收到心跳"); 
  156.                         dataQueue.offer(pingAck); 
  157.                         toNotifyAll(dataQueue); //唤醒发送线程 
  158.  
  159.                         tBack.targetIsOnline(userIP); 
  160.                     } 
  161.                 } else { 
  162.                     System.out.println("client is offline..."); 
  163.                     break; 
  164.                 } 
  165.             } 
  166.  
  167.             SocketUtil.closeInputStream(inputStream); 
  168.         } 
  169.     } 
  170.  
  171.     public class SendTask extends Thread { 
  172.  
  173.         private DataOutputStream outputStream; 
  174.         private boolean isCancle; 
  175.  
  176.         @Override 
  177.         public void run() { 
  178.             while (!isCancle) { 
  179.                 if (!isConnected()) { 
  180.                     isCancle = true
  181.                     break; 
  182.                 } 
  183.  
  184.                 BasicProtocol procotol = dataQueue.poll(); 
  185.                 if (procotol == null) { 
  186.                     toWaitAll(dataQueue); 
  187.                 } else if (outputStream != null) { 
  188.                     synchronized (outputStream) { 
  189.                         SocketUtil.write2Stream(procotol, outputStream); 
  190.                     } 
  191.                 } 
  192.             } 
  193.  
  194.             SocketUtil.closeOutputStream(outputStream); 
  195.         } 
  196.     } 

其中涉及到的ResponseCallback接口如下:

 
 
  1. /** 
  2.  * Created by meishan on 16/12/1. 
  3.  */ 
  4. public interface ResponseCallback { 
  5.  
  6.     void targetIsOffline(DataProtocol reciveMsg); 
  7.  
  8.     void targetIsOnline(String clientIp); 
  9.  

上述代码中处理了几种情况下的异常,比如,建立连接后,服务端停止运行,此时客户端的输入流还在阻塞状态,怎么保证客户端不抛出异常,这些处理可以结合SocketUtil类来看。

四、调用封装

1. 客户端

import com.shandiangou.sdgprotocol.lib.protocol.DataProtocol;

 
 
  1. import com.shandiangou.sdgprotocol.lib.protocol.DataProtocol; 
  2.  
  3. /** 
  4.  * Created by meishan on 16/12/1. 
  5.  */ 
  6. public class ConnectionClient { 
  7.  
  8.     private boolean isClosed; 
  9.  
  10.     private ClientRequestTask mClientRequestTask; 
  11.  
  12.     public ConnectionClient(RequestCallBack requestCallBack) { 
  13.         mClientRequestTask = new ClientRequestTask(requestCallBack); 
  14.         new Thread(mClientRequestTask).start(); 
  15.     } 
  16.  
  17.     public void addNewRequest(DataProtocol data) { 
  18.         if (mClientRequestTask != null && !isClosed) 
  19.             mClientRequestTask.addRequest(data); 
  20.     } 
  21.  
  22.     public void closeConnect() { 
  23.         isClosed = true
  24.         mClientRequestTask.stop(); 
  25.     } 
  26.  

2. 服务端

 
 
  1. import com.shandiangou.sdgprotocol.lib.protocol.DataProtocol; 
  2.  
  3. import java.io.IOException; 
  4. import java.net.ServerSocket; 
  5. import java.net.Socket; 
  6. import java.util.concurrent.ExecutorService; 
  7. import java.util.concurrent.Executors; 
  8.  
  9. /** 
  10.  * Created by meishan on 16/12/1. 
  11.  */ 
  12. public class ConnectionServer { 
  13.  
  14.     private static boolean isStart = true
  15.     private static ServerResponseTask serverResponseTask; 
  16.  
  17.     public ConnectionServer() { 
  18.  
  19.     } 
  20.  
  21.     public static void main(String[] args) { 
  22.  
  23.         ServerSocket serverSocket = null
  24.         ExecutorService executorService = Executors.newCachedThreadPool(); 
  25.         try { 
  26.             serverSocket = new ServerSocket(Config.PORT); 
  27.             while (isStart) { 
  28.                 Socket socket = serverSocket.accept(); 
  29.                 serverResponseTask = new ServerResponseTask(socket, 
  30.                         new ResponseCallback() { 
  31.  
  32.                             @Override 
  33.                             public void targetIsOffline(DataProtocol reciveMsg) {// 对方不在线 
  34.                                 if (reciveMsg != null) { 
  35.                                     System.out.println(reciveMsg.getData()); 
  36.                                 } 
  37.                             } 
  38.  
  39.                             @Override 
  40.                             public void targetIsOnline(String clientIp) { 
  41.                                 System.out.println(clientIp + " is onLine"); 
  42.                                 System.out.println("-----------------------------------------"); 
  43.                             } 
  44.                         }); 
  45.  
  46.                 if (socket.isConnected()) { 
  47.                     executorService.execute(serverResponseTask); 
  48.                 } 
  49.             } 
  50.  
  51.             serverSocket.close(); 
  52.  
  53.         } catch (IOException e) { 
  54.             e.printStackTrace(); 
  55.         } finally { 
  56.             if (serverSocket != null) { 
  57.                 try { 
  58.                     isStart = false
  59.                     serverSocket.close(); 
  60.                     if (serverSocket != null
  61.                         serverResponseTask.stop(); 
  62.                 } catch (IOException e) { 
  63.                     e.printStackTrace(); 
  64.                 } 
  65.             } 
  66.         } 
  67.     } 
  68.  

总结

实现自定义协议的关键在于协议的拼装和解析,上述已给出了关键的代码,如果需要查看完整的代码以及demo,可以下载源码

注意:先运行服务端demo的main函数,再查看本机的ip地址,然后修改客户端(android)代码中Config.java里面的ip地址,当然,要确保android手机和服务端在同一个局域里面,最后再打开客户端。




作者:枚杉
来源:51CTO
相关实践学习
通过Ingress进行灰度发布
本场景您将运行一个简单的应用,部署一个新的应用用于新的发布,并通过Ingress能力实现灰度发布。
容器应用与集群管理
欢迎来到《容器应用与集群管理》课程,本课程是“云原生容器Clouder认证“系列中的第二阶段。课程将向您介绍与容器集群相关的概念和技术,这些概念和技术可以帮助您了解阿里云容器服务ACK/ACK Serverless的使用。同时,本课程也会向您介绍可以采取的工具、方法和可操作步骤,以帮助您了解如何基于容器服务ACK Serverless构建和管理企业级应用。 学习完本课程后,您将能够: 掌握容器集群、容器编排的基本概念 掌握Kubernetes的基础概念及核心思想 掌握阿里云容器服务ACK/ACK Serverless概念及使用方法 基于容器服务ACK Serverless搭建和管理企业级网站应用
目录
相关文章
|
14天前
|
Java Android开发 C++
🚀Android NDK开发实战!Java与C++混合编程,打造极致性能体验!📊
在Android应用开发中,追求卓越性能是不变的主题。本文介绍如何利用Android NDK(Native Development Kit)结合Java与C++进行混合编程,提升应用性能。从环境搭建到JNI接口设计,再到实战示例,全面展示NDK的优势与应用技巧,助你打造高性能应用。通过具体案例,如计算斐波那契数列,详细讲解Java与C++的协作流程,帮助开发者掌握NDK开发精髓,实现高效计算与硬件交互。
54 1
|
2月前
|
存储 搜索推荐 Java
探索安卓开发中的自定义视图:打造个性化UI组件Java中的异常处理:从基础到高级
【8月更文挑战第29天】在安卓应用的海洋中,一个独特的用户界面(UI)能让应用脱颖而出。自定义视图是实现这一目标的强大工具。本文将通过一个简单的自定义计数器视图示例,展示如何从零开始创建一个具有独特风格和功能的安卓UI组件,并讨论在此过程中涉及的设计原则、性能优化和兼容性问题。准备好让你的应用与众不同了吗?让我们开始吧!
|
2月前
|
Java 调度 Android开发
Android经典实战之Kotlin的delay函数和Java中的Thread.sleep有什么不同?
本文介绍了 Kotlin 中的 `delay` 函数与 Java 中 `Thread.sleep` 方法的区别。两者均可暂停代码执行,但 `delay` 适用于协程,非阻塞且高效;`Thread.sleep` 则阻塞当前线程。理解这些差异有助于提高程序效率与可读性。
51 1
|
3月前
|
弹性计算 资源调度 API
云服务器 ECS产品使用问题之如何实现自定义页面适配移动端
云服务器ECS(Elastic Compute Service)是各大云服务商阿里云提供的一种基础云计算服务,它允许用户租用云端计算资源来部署和运行各种应用程序。以下是一个关于如何使用ECS产品的综合指南。
云服务器 ECS产品使用问题之如何实现自定义页面适配移动端
|
2月前
|
Java Android开发
解决Android编译报错:Unable to make field private final java.lang.String java.io.File.path accessible
解决Android编译报错:Unable to make field private final java.lang.String java.io.File.path accessible
152 1
|
2月前
|
Android开发
Cannot create android app from an archive...containing both DEX and Java-bytecode content
Cannot create android app from an archive...containing both DEX and Java-bytecode content
23 2
|
2月前
|
IDE Java Linux
探索安卓开发:从基础到进阶的旅程Java中的异常处理:从基础到高级
【8月更文挑战第30天】在这个数字时代,移动应用已经成为我们日常生活中不可或缺的一部分。安卓系统由于其开放性和灵活性,成为了开发者的首选平台之一。本文将带领读者踏上一段从零开始的安卓开发之旅,通过深入浅出的方式介绍安卓开发的基础知识、核心概念以及进阶技巧。我们将一起构建一个简单的安卓应用,并探讨如何优化代码以提高性能和应用的用户体验。无论你是初学者还是有一定经验的开发者,这篇文章都将为你提供宝贵的知识和启发。
|
3月前
|
Java Android开发 C++
🚀Android NDK开发实战!Java与C++混合编程,打造极致性能体验!📊
【7月更文挑战第28天】在 Android 开发中, NDK 让 Java 与 C++ 混合编程成为可能, 从而提升应用性能。**为何选 NDK?** C++ 在执行效率与内存管理上优于 Java, 特别适合高性能需求场景。**环境搭建** 需 Android Studio 和 NDK, 工具如 CMake。**JNI** 构建 Java-C++ 交互, 通过声明 `native` 方法并在 C++ 中实现。**实战** 示例: 使用 C++ 计算斐波那契数列以提高效率。**总结** 混合编程增强性能, 但增加复杂性, 使用前需谨慎评估。
110 4
|
3月前
|
Java
如何在Java中实现多线程的Socket服务器?
在Java中,多线程Socket服务器能同时处理多个客户端连接以提升并发性能。示例代码展示了如何创建此类服务器:监听指定端口,并为每个新连接启动一个`ClientHandler`线程进行通信处理。使用线程池管理这些线程,提高了效率。`ClientHandler`读取客户端消息并响应,支持简单的文本交互,如发送欢迎信息及处理退出命令。
|
3月前
|
SQL Java Unix
Android经典面试题之Java中获取时间戳的方式有哪些?有什么区别?
在Java中获取时间戳有多种方式,包括`System.currentTimeMillis()`(毫秒级,适用于日志和计时)、`System.nanoTime()`(纳秒级,高精度计时)、`Instant.now().toEpochMilli()`(毫秒级,ISO-8601标准)和`Instant.now().getEpochSecond()`(秒级)。`Timestamp.valueOf(LocalDateTime.now()).getTime()`适用于数据库操作。选择方法取决于精度、用途和时间起点的需求。
47 3
下一篇
无影云桌面