前提
之前寫過(guò)兩篇關(guān)于socket的文章荐吵,但是,只是簡(jiǎn)單的介紹了一下關(guān)于socket Tcp和Udp的簡(jiǎn)單使用入撒。如果沒有看過(guò)的朋友可以去看看Android Socket編程(tcp)初探和Android Socket編程(udp)初探隆豹。相信很多朋友在公司使用socket開發(fā)的時(shí)候都會(huì)自定義協(xié)議來(lái)傳遞信息。一方面是為了安全排除臟數(shù)據(jù)茅逮,另一個(gè)方面是為了更加高效的處理自己所需要的數(shù)據(jù)璃赡。今天就來(lái)介紹一下關(guān)于socket自定義協(xié)議和使用Protocol Buffer解析數(shù)據(jù)。
首先
既然說(shuō)到了Protocol Buffer氮唯,那么我們就簡(jiǎn)單介紹一下Protocol Buffer是什么鉴吹?并且使用為什么要使用Protocol Buffer?
- 1惩琉、什么是Protocol Buffer
一種 結(jié)構(gòu)化數(shù)據(jù) 的數(shù)據(jù)存儲(chǔ)格式(類似于 XML豆励、Json ),其作用是通過(guò)將 結(jié)構(gòu)化的數(shù)據(jù) 進(jìn)行 串行化(序列化)瞒渠,從而實(shí)現(xiàn) 數(shù)據(jù)存儲(chǔ) / RPC 數(shù)據(jù)交換的功能良蒸。至于更詳細(xì)的用法和介紹請(qǐng)移步Protocol Buffer 序列化原理大揭秘 - 為什么Protocol Buffer性能這么好?
-
2伍玖、為什么要使用Protocol Buffer
在回答這個(gè)問題之前嫩痰,我們還是先給出一個(gè)在實(shí)際開發(fā)中經(jīng)常會(huì)遇到的系統(tǒng)場(chǎng)景。比如:我們的客戶端程序是使用Java開發(fā)的窍箍,可能運(yùn)行自不同的平臺(tái)串纺,如:Linux、Windows或者是Android椰棘,而我們的服務(wù)器程序通常是基于Linux平臺(tái)并使用C++或者Python開發(fā)完成的纺棺。在這兩種程序之間進(jìn)行數(shù)據(jù)通訊時(shí)存在多種方式用于設(shè)計(jì)消息格式,如:
1邪狞、 直接傳遞C/C++/Python語(yǔ)言中一字節(jié)對(duì)齊的結(jié)構(gòu)體數(shù)據(jù)祷蝌,只要結(jié)構(gòu)體的聲明為定長(zhǎng)格式,那么該方式對(duì)于C/C++/Python程序而言就非常方便了帆卓,僅需將接收到的數(shù)據(jù)按照結(jié)構(gòu)體類型強(qiáng)行轉(zhuǎn)換即可巨朦。事實(shí)上對(duì)于變長(zhǎng)結(jié)構(gòu)體也不會(huì)非常麻煩米丘。在發(fā)送數(shù)據(jù)時(shí),也只需定義一個(gè)結(jié)構(gòu)體變量并設(shè)置各個(gè)成員變量的值之后糊啡,再以char*的方式將該二進(jìn)制數(shù)據(jù)發(fā)送到遠(yuǎn)端拄查。反之,該方式對(duì)于Java開發(fā)者而言就會(huì)非常繁瑣悔橄,首先需要將接收到的數(shù)據(jù)存于ByteBuffer之中靶累,再根據(jù)約定的字節(jié)序逐個(gè)讀取每個(gè)字段,并將讀取后的值再賦值給另外一個(gè)值對(duì)象中的域變量癣疟,以便于程序中其他代碼邏輯的編寫。對(duì)于該類型程序而言潮酒,聯(lián)調(diào)的基準(zhǔn)是必須客戶端和服務(wù)器雙方均完成了消息報(bào)文構(gòu)建程序的編寫后才能展開睛挚,而該設(shè)計(jì)方式將會(huì)直接導(dǎo)致Java程序開發(fā)的進(jìn)度過(guò)慢。即便是Debug階段急黎,也會(huì)經(jīng)常遇到Java程序中出現(xiàn)各種域字段拼接的小錯(cuò)誤扎狱。
2、 使用SOAP協(xié)議(WebService)作為消息報(bào)文的格式載體勃教,由該方式生成的報(bào)文是基于文本格式的淤击,同時(shí)還存在大量的XML描述信息,因此將會(huì)大大增加網(wǎng)絡(luò)IO的負(fù)擔(dān)故源。又由于XML解析的復(fù)雜性污抬,這也會(huì)大幅降低報(bào)文解析的性能∩總之印机,使用該設(shè)計(jì)方式將會(huì)使系統(tǒng)的整體運(yùn)行性能明顯下降。
對(duì)于以上兩種方式所產(chǎn)生的問題门驾,Protocol Buffer均可以很好的解決射赛,不僅如此,Protocol Buffer還有一個(gè)非常重要的優(yōu)點(diǎn)就是可以保證同一消息報(bào)文新舊版本之間的兼容性奶是。對(duì)于Protocol Buffer具體的用法請(qǐng)移步Protocol Buffer技術(shù)詳解(語(yǔ)言規(guī)范)今天主要講解的是socket自定義協(xié)議這塊
其次
說(shuō)了那么多楣责,我們來(lái)看看我們今天的主要內(nèi)容— 自定義socket協(xié)議
先看一張心跳返回的圖
- 1、Protobuf協(xié)議
- 假設(shè)客戶端請(qǐng)求包體數(shù)據(jù)協(xié)議如下
request.proto
syntax = "proto3";
// 登錄的包體數(shù)據(jù)
message Request {
int32 uid = 0;
string api_token = 1;
}
發(fā)送的格式:
{包頭}{命令}{包體}
{包頭} -> 包體轉(zhuǎn)成protubuf的長(zhǎng)度
{命令} -> 對(duì)應(yīng)功能的命令字參數(shù)
{包體} -> 對(duì)應(yīng)的protubuf數(shù)據(jù)
- 假設(shè)服務(wù)端返回包體數(shù)據(jù)協(xié)議
response.proto
syntax = "proto3";
// 登錄成功后服務(wù)器返回的包體數(shù)據(jù)
message Response {
int32 login = 1;
}
服務(wù)器返回的格式:
{包頭}{命令}{狀態(tài)碼}{包體}
{包頭} -> 包體轉(zhuǎn)成protubuf的長(zhǎng)度
{命令} -> 對(duì)應(yīng)功能的命令字參數(shù)
{狀態(tài)碼} -> 對(duì)應(yīng)狀態(tài)的狀態(tài)碼
{包體} -> 對(duì)應(yīng)的protubuf數(shù)據(jù)
2聂沙、客戶端socket寫法
分析:試想一下秆麸,要socket不會(huì)因?yàn)槭謾C(jī)屏幕的熄滅或者其他什么的而斷開,我們應(yīng)該把socket放到哪里去寫逐纬,又要怎么保證socket的連接狀態(tài)呢蛔屹?對(duì)于Android來(lái)說(shuō)放到 service里面去是最合適的,并且為了保證連接狀態(tài)豁生。那么兔毒,就要發(fā)送一個(gè)心跳包保證連接狀態(tài)漫贞。既然這樣,那么我們來(lái)寫service和socket育叁。
-
3迅脐、service寫法
public class SocketService extends Service { Thread mSocketThread; Socket mSocket; InetSocketAddress mSocketAddress; //心跳線程 Thread mHeartThread; //接收線程 Thread mReceiveThread; //登錄線程 Thread mLoginThread; boolean isHeart = false; boolean isReceive = false; SocketBinder mBinder = new SocketBinder(this); public SocketService() { } @Override public void onCreate() { super.onCreate(); createConnection(); receiveMsg(); isHeart = true; isReceive = true; } @Override public IBinder onBind(Intent intent) { return mBinder; } @Override public int onStartCommand(Intent intent, int flags, int startId) { startGps(); sendHeart(); if (!TextUtils.isEmpty(intent.getStringExtra(AppConfig.SERVICE_TAG))) { String TAG = intent.getStringExtra(AppConfig.SERVICE_TAG); switch (TAG) { case AppConfig.STOP_SERVICE_VALUE: {//停止服務(wù) ClientSocket.getsInstance().shutDownConnection(mSocket); stopSelf(); mSocket = null; mHeartThread = null; mReceiveThread = null; mLoginThread = null; mSocketThread = null; isHeart = false; isReceive = false; break; } default: break; } } return super.onStartCommand(intent, flags, startId); } /** * 發(fā)送心跳包 */ private void sendHeart() { mHeartThread = new Thread(new Runnable() { @Override public void run() { while (isHeart) { ClientSocket.getsInstance().sendHeart(mSocket, SocketStatus.HEART_CODE); try { Thread.sleep(1000); } catch (InterruptedException e) { e.printStackTrace(); } } } }); mHeartThread.start(); } /** * 登錄 */ private void login(final double mLatitude, final double mLongitude) { mLoginThread = new Thread(new Runnable() { @Override public void run() { if (PreferencesUtils.getInt(SocketService.this, Constants.USER_ID) != 0 && !TextUtils.isEmpty(PreferencesUtils.getString(SocketService.this, Constants.USER_TOKEN))) { Request.Request requestLogin = Request.Request.newBuilder() .setUid(PreferencesUtils.getInt(SocketService.this, Constants.USER_ID)) .setApiToken(PreferencesUtils.getString(SocketService.this, Constants.USER_TOKEN).trim()) .build(); ClientSocket.getsInstance().sendLogin(mSocket, requestLogin, SocketStatus.LOGIN_CODE); } } }); mLoginThread.start(); } /** * 創(chuàng)建連接 * * @return */ public void createConnection() { mSocketThread = new Thread(new Runnable() { @Override public void run() { try { mSocket = new Socket(); mSocketAddress = new InetSocketAddress(AppConfig.TCP_IP, AppConfig.TCP_PORT); mSocket.connect(mSocketAddress, 20 * 1000); // 設(shè)置 socket 讀取數(shù)據(jù)流的超時(shí)時(shí)間 mSocket.setSoTimeout(20 * 1000); // 發(fā)送數(shù)據(jù)包,默認(rèn)為 false豪嗽,即客戶端發(fā)送數(shù)據(jù)采用 Nagle 算法谴蔑; // 但是對(duì)于實(shí)時(shí)交互性高的程序,建議其改為 true龟梦,即關(guān)閉 Nagle // 算法隐锭,客戶端每發(fā)送一次數(shù)據(jù),無(wú)論數(shù)據(jù)包大小都會(huì)將這些數(shù)據(jù)發(fā)送出去 mSocket.setTcpNoDelay(true); // 設(shè)置客戶端 socket 關(guān)閉時(shí)计贰,close() 方法起作用時(shí)延遲 30 秒關(guān)閉钦睡,如果 30 秒內(nèi)盡量將未發(fā)送的數(shù)據(jù)包發(fā)送出去 // socket.setSoLinger(true, 30); // 設(shè)置輸出流的發(fā)送緩沖區(qū)大小,默認(rèn)是4KB躁倒,即4096字節(jié) mSocket.setSendBufferSize(10 * 1024); // 設(shè)置輸入流的接收緩沖區(qū)大小荞怒,默認(rèn)是4KB,即4096字節(jié) mSocket.setReceiveBufferSize(10 * 1024); // 作用:每隔一段時(shí)間檢查服務(wù)器是否處于活動(dòng)狀態(tài)秧秉,如果服務(wù)器端長(zhǎng)時(shí)間沒響應(yīng)褐桌,自動(dòng)關(guān)閉客戶端socket // 防止服務(wù)器端無(wú)效時(shí),客戶端長(zhǎng)時(shí)間處于連接狀態(tài) mSocket.setKeepAlive(true); } catch (UnknownHostException e) { Logger.e(e.getMessage() + "========+UnknownHostException"); e.printStackTrace(); } catch (IOException e) { createConnection(); Logger.e(e.getMessage() + "========IOException"); e.printStackTrace(); } catch (NetworkOnMainThreadException e) { Logger.e(e.getMessage() + "========NetworkOnMainThreadException"); e.printStackTrace(); } } }); mSocketThread.start(); } /** * 接收 */ private void receiveMsg() { mReceiveThread = new Thread(new Runnable() { @Override public void run() { while (isReceive) { try { if (mSocket != null && mSocket.isConnected()) { DataInputStream dis = ClientSocket.getsInstance().getMessageStream(mSocket); ByteArrayOutputStream bos = new ByteArrayOutputStream(); if (dis != null) { int length = 0; int head = 0; int buffer_size = 4; byte[] headBuffer = new byte[4]; byte[] cmdBuffer = new byte[4]; byte[] stateBuffer = new byte[4]; length = dis.read(headBuffer, 0, buffer_size); if (length == 4) { bos.write(headBuffer, 0, length); System.arraycopy(bos.toByteArray(), 0, headBuffer, 0, buffer_size); head = ByteUtil.bytesToInt(headBuffer, 0); length = dis.read(cmdBuffer, 0, buffer_size); bos.write(cmdBuffer, 0, length); System.arraycopy(bos.toByteArray(), 4, cmdBuffer, 0, buffer_size); int cmd = ByteUtil.hexStringToAlgorism(ByteUtil.str2HexStr(ByteUtil.byte2hex(cmdBuffer))); int heartNumber = ByteUtil.hexStringToAlgorism(ByteUtil.str2HexStr(SocketStatus.HEART)); String discover = Integer.toHexString(0x0101); int discoverNumber = ByteUtil.hexStringToAlgorism(ByteUtil.str2HexStr(SocketStatus.DISCOVER)); int giftNumber = ByteUtil.hexStringToAlgorism(ByteUtil.str2HexStr(SocketStatus.GIFT)); if (cmd == heartNumber) { length = dis.read(stateBuffer, 0, buffer_size); bos.write(stateBuffer, 0, length); System.arraycopy(bos.toByteArray(), 8, stateBuffer, 0, buffer_size); switch (ByteUtil.bytesToInt(stateBuffer, 0)) { case SocketStatus.LOGIN_SUCCESS: {//登錄成功 Logger.e("登錄成功"); mLoginValue = "1"; EventUtils.sendEvent(new Event<>(Constants.MSG_LOGIN_SUCCESS)); break; } case SocketStatus.HEART_SUCCESS: {//心跳返回 if (ByteUtil.bytesToInt(stateBuffer, 0) == 200 && Integer.toHexString(ByteUtil.bytesToInt(cmdBuffer, 0)) .equals(discover)) { byte[] buffer = new byte[head]; length = dis.read(buffer, 0, head); bos.write(buffer, 0, length); Response.Response response = Response. Response.parseFrom(buffer); Logger.e(responseExplore.getNickname() + responseExplore.getAvatar()); //發(fā)送到activity中對(duì)數(shù)據(jù)進(jìn)行處理 EventUtils.sendEvent(new Event<>(Constants.MSG_START_DISCOVER_RESULT, responseExplore)); Logger.e(responseExplore + "=======response"); } else { Logger.e("心跳返回"); } break; } default: break; } } } else { //出錯(cuò)重連 ClientSocket.getsInstance().shutDownConnection(mSocket); createConnection(); } } else { createConnection(); } } } catch (IOException ex) { ex.printStackTrace(); } try { Thread.sleep(50); } catch (InterruptedException e) { e.printStackTrace(); } } } }); mReceiveThread.start(); } @Override public void onDestroy() { super.onDestroy(); ClientSocket.getsInstance().shutDownConnection(mSocket); stopSelf(); mHeartThread = null; mReceiveThread = null; mLoginThread = null; mSocketThread = null; mStopDiscoverThread = null; isHeart = false; isReceive = false; } /** * Binder */ public class SocketBinder extends Binder { private SocketService mService; public OnServiceCallBack mCallBack; public SocketBinder(SocketService mService) { this.mService = mService; } /** * 發(fā)送方法 * * @param object */ public void sendMethod(Object object) { mService.sendMsg(object); mCallBack.onService(object); } /** * 設(shè)置回調(diào) * * @param callBack */ public void setOnServiceCallBack(OnServiceCallBack callBack) { this.mCallBack = callBack; } } }
分析
上面的service中首先創(chuàng)建socket象迎,然后連接荧嵌,在socket發(fā)生錯(cuò)誤的時(shí)候(比如網(wǎng)絡(luò)異常)重新進(jìn)行創(chuàng)建在連接。然后挖帘,開一個(gè)接收線程一直接收完丽,每次接收都是接收4個(gè)字節(jié)的int值進(jìn)行判斷是否可以進(jìn)入到下一步,如果可以則繼續(xù)向下拇舀。讀取4個(gè)字節(jié)的包頭然后讀取4個(gè)字節(jié)的命令 再讀取4個(gè)字節(jié)的狀態(tài)碼 最后讀取4個(gè)字節(jié)的包體逻族,包體就包含我們所需要返回的數(shù)據(jù)。并且骄崩,在剛開始的時(shí)候就開啟了一個(gè)接收線程每隔50毫秒接收一次數(shù)據(jù)聘鳞,這樣不僅可以讀取到心跳包還可以讀取到我們需要的數(shù)據(jù)。在最后要拂,server生命周期結(jié)束的時(shí)候停止所有的線程抠璃。-
4、發(fā)送數(shù)據(jù)的類
public class ClientSocket { private DataOutputStream out = null; private DataInputStream getMessageStream; private static ClientSocket sInstance; private ClientSocket() { } /** * 單例 * * @return */ public static ClientSocket getsInstance() { if (sInstance == null) { synchronized (ClientSocket.class) { if (sInstance == null) { sInstance = new ClientSocket(); } } } return sInstance; } /** * 登錄 * * @return */ public void sendLogin(Socket socket, Request.RequestLogin requestLogin, int code) { byte[] data = requestLogin.toByteArray(); byte[] head = ByteUtil.intToBytes(data.length); byte[] cmd = ByteUtil.intToBytes(code); byte[] bytes = addBytes(head, cmd, data); if (socket != null) { if (socket.isConnected()) { try { OutputStream os = socket.getOutputStream(); os.write(bytes); os.flush(); } catch (IOException e) { e.printStackTrace(); } } } } /** * 心跳 * * @param code 關(guān)鍵字(命令) * @return */ public boolean sendHeart(Socket socket, int code) { boolean isSuccess; byte[] head = ByteUtil.intToBytes(0); byte[] cmd = ByteUtil.intToBytes(code); byte[] bytes = addBytes(head, cmd); if (socket.isConnected()) { try { out = new DataOutputStream(socket.getOutputStream()); out.write(bytes); out.flush(); isSuccess = true; } catch (IOException e) { e.printStackTrace(); isSuccess = false; } } else { isSuccess = false; } return isSuccess; } /** * 斷開連接 */ public void shutDownConnection(Socket socket) { try { if (out != null) { out.close(); } if (getMessageStream != null) { getMessageStream.close(); } if (socket != null) { socket.close(); } } catch (IOException e) { e.printStackTrace(); } } /** * 獲取服務(wù)器返回的流 * * @param socket * @return */ public DataInputStream getMessageStream(Socket socket) { if (socket == null) { return null; } if (socket.isClosed()) { return null; } if (!socket.isConnected()) { return null; } try { getMessageStream = new DataInputStream(new BufferedInputStream( socket.getInputStream())); } catch (IOException e) { e.printStackTrace(); if (getMessageStream != null) { try { getMessageStream.close(); } catch (IOException e1) { e1.printStackTrace(); } } } return getMessageStream; } }
分析:
這里使用了單例模式脱惰,保證了數(shù)據(jù)的唯一性搏嗡,不會(huì)重復(fù)創(chuàng)建,可以看到登錄發(fā)送了包頭、命令和數(shù)據(jù)長(zhǎng)度采盒,而心跳只是包頭和命令旧乞,因?yàn)榘w長(zhǎng)度為空,所以不用發(fā)送磅氨,最后轉(zhuǎn)成4個(gè)字節(jié)的二進(jìn)制數(shù)據(jù)進(jìn)行發(fā)送尺栖。這樣,proto buffer的優(yōu)點(diǎn)就體現(xiàn)出來(lái)了烦租,方便客戶端和服務(wù)端的解析延赌。-
二進(jìn)制轉(zhuǎn)換工具類
public class ByteUtil { /** * 將2個(gè)byte數(shù)組進(jìn)行拼接 */ public static byte[] addBytes(byte[] data1, byte[] data2) { byte[] data3 = new byte[data1.length + data2.length]; System.arraycopy(data1, 0, data3, 0, data1.length); System.arraycopy(data2, 0, data3, data1.length, data2.length); return data3; } /** * 將3個(gè)byte數(shù)組進(jìn)行拼接 */ public static byte[] addBytes(byte[] data1, byte[] data2, byte[] data3) { byte[] data4 = new byte[data1.length + data2.length + data3.length]; System.arraycopy(data1, 0, data4, 0, data1.length); System.arraycopy(data2, 0, data4, data1.length, data2.length); System.arraycopy(data3, 0, data4, data1.length + data2.length, data3.length); return data4; } /** * int轉(zhuǎn)byte{} */ public static byte[] intToBytes(int value, ByteOrder mode) { byte[] src = new byte[4]; if (mode == ByteOrder.LITTLE_ENDIAN) { src[3] = (byte) ((value >> 24) & 0xFF); src[2] = (byte) ((value >> 16) & 0xFF); src[1] = (byte) ((value >> 8) & 0xFF); src[0] = (byte) (value & 0xFF); } else { src[0] = (byte) ((value >> 24) & 0xFF); src[1] = (byte) ((value >> 16) & 0xFF); src[2] = (byte) ((value >> 8) & 0xFF); src[3] = (byte) (value & 0xFF); } return src; } /** * 16進(jìn)制表示的字符串轉(zhuǎn)換為字節(jié)數(shù)組 * * @param s 16進(jìn)制表示的字符串 * @return byte[] 字節(jié)數(shù)組 */ public static byte[] hexStringToByteArray(String s) { int len = s.length(); byte[] b = new byte[len / 2]; for (int i = 0; i < len; i += 2) { // 兩位一組,表示一個(gè)字節(jié),把這樣表示的16進(jìn)制字符串叉橱,還原成一個(gè)字節(jié) b[i / 2] = (byte) ((Character.digit(s.charAt(i), 16) << 4) + Character .digit(s.charAt(i + 1), 16)); } return b; } /** * byte數(shù)組中取int數(shù)值挫以,本方法適用于(低位在前,高位在后)的順序窃祝,和和intToBytes()配套使用 * * @param src byte數(shù)組 * @param offset 從數(shù)組的第offset位開始 * @return int數(shù)值 */ public static int bytesToInt(byte[] src, int offset) { int value; value = (int) ((src[offset] & 0xFF) | ((src[offset + 1] & 0xFF) << 8) | ((src[offset + 2] & 0xFF) << 16) | ((src[offset + 3] & 0xFF) << 24)); return value; } /** * byte數(shù)組中取int數(shù)值屡贺,本方法適用于(低位在后,高位在前)的順序锌杀。和intToBytes2()配套使用 */ public static int bytesToInt2(byte[] src, int offset) { int value; value = (int) (((src[offset] & 0xFF) << 24) | ((src[offset + 1] & 0xFF) << 16) | ((src[offset + 2] & 0xFF) << 8) | (src[offset + 3] & 0xFF)); return value; } /** * 將int數(shù)值轉(zhuǎn)換為占四個(gè)字節(jié)的byte數(shù)組,本方法適用于(低位在前泻仙,高位在后)的順序糕再。 和 bytesToInt()配套使用 * * @param value 要轉(zhuǎn)換的int值 * @return byte數(shù)組 */ public static byte[] intToBytes(int value) { byte[] src = new byte[4]; src[3] = (byte) ((value >> 24) & 0xFF); src[2] = (byte) ((value >> 16) & 0xFF); src[1] = (byte) ((value >> 8) & 0xFF); src[0] = (byte) (value & 0xFF); return src; } /** * 將int數(shù)值轉(zhuǎn)換為占四個(gè)字節(jié)的byte數(shù)組,本方法適用于(高位在前玉转,低位在后)的順序突想。 和 bytesToInt2()配套使用 */ public static byte[] intToBytes2(int value) { byte[] src = new byte[4]; src[0] = (byte) ((value >> 24) & 0xFF); src[1] = (byte) ((value >> 16) & 0xFF); src[2] = (byte) ((value >> 8) & 0xFF); src[3] = (byte) (value & 0xFF); return src; } /** * 將字節(jié)轉(zhuǎn)換為二進(jìn)制字符串 * * @param bytes 字節(jié)數(shù)組 * @return 二進(jìn)制字符串 */ public static String byteToBit(byte... bytes) { StringBuffer sb = new StringBuffer(); int z, len; String str; for (int w = 0; w < bytes.length; w++) { z = bytes[w]; z |= 256; str = Integer.toBinaryString(z); len = str.length(); sb.append(str.substring(len - 8, len)); } return sb.toString(); } /** * 字節(jié)數(shù)組轉(zhuǎn)為普通字符串(ASCII對(duì)應(yīng)的字符) * * @param bytearray byte[] * @return String */ public static String byte2String(byte[] bytearray) { String result = ""; char temp; int length = bytearray.length; for (int i = 0; i < length; i++) { temp = (char) bytearray[i]; result += temp; } return result; } /** * 二進(jìn)制字符串轉(zhuǎn)十進(jìn)制 * * @param binary 二進(jìn)制字符串 * @return 十進(jìn)制數(shù)值 */ public static int binaryToAlgorism(String binary) { int max = binary.length(); int result = 0; for (int i = max; i > 0; i--) { char c = binary.charAt(i - 1); int algorism = c - '0'; result += Math.pow(2, max - i) * algorism; } return result; } /** * 字節(jié)數(shù)組轉(zhuǎn)換為十六進(jìn)制字符串 * * @param b byte[] 需要轉(zhuǎn)換的字節(jié)數(shù)組 * @return String 十六進(jìn)制字符串 */ public static String byte2hex(byte b[]) { if (b == null) { throw new IllegalArgumentException( "Argument b ( byte array ) is null! "); } String hs = ""; String stmp = ""; for (int n = 0; n < b.length; n++) { stmp = Integer.toHexString(b[n] & 0xff); if (stmp.length() == 1) { hs = hs + "0" + stmp; } else { hs = hs + stmp; } } return hs.toUpperCase(); } /** * 十六進(jìn)制字符串轉(zhuǎn)換十進(jìn)制 * * @param hex 十六進(jìn)制字符串 * @return 十進(jìn)制數(shù)值 */ public static int hexStringToAlgorism(String hex) { hex = hex.toUpperCase(); int max = hex.length(); int result = 0; for (int i = max; i > 0; i--) { char c = hex.charAt(i - 1); int algorism = 0; if (c >= '0' && c <= '9') { algorism = c - '0'; } else { algorism = c - 55; } result += Math.pow(16, max - i) * algorism; } return result; } /** * 字符串轉(zhuǎn)換成十六進(jìn)制字符串 * * @param str 待轉(zhuǎn)換的ASCII字符串 * @return String 每個(gè)Byte之間空格分隔,如: [61 6C 6B] */ public static String str2HexStr(String str) { char[] chars = "0123456789ABCDEF".toCharArray(); StringBuilder sb = new StringBuilder(""); byte[] bs = str.getBytes(); int bit; for (int i = 0; i < bs.length; i++) { bit = (bs[i] & 0x0f0) >> 4; sb.append(chars[bit]); bit = bs[i] & 0x0f; sb.append(chars[bit]); sb.append(' '); } return sb.toString().trim(); } /** * 16進(jìn)制轉(zhuǎn)換成字符串 * * @param hexStr * @return */ public static String hexStr2Str(String hexStr) { String str = "0123456789ABCDEF"; char[] hexs = hexStr.toCharArray(); byte[] bytes = new byte[hexStr.length() / 2]; int n; for (int i = 0; i < bytes.length; i++) { n = str.indexOf(hexs[2 * i]) * 16; n += str.indexOf(hexs[2 * i + 1]); bytes[i] = (byte) (n & 0xff); } return new String(bytes); } /** * 重寫了Inpustream 中的skip(long n) 方法, * 將數(shù)據(jù)流中起始的n 個(gè)字節(jié)跳過(guò) */ public static long skipBytesFromStream(InputStream inputStream, long n) { long remaining = n; // SKIP_BUFFER_SIZE is used to determine the size of skipBuffer int SKIP_BUFFER_SIZE = 2048; // skipBuffer is initialized in skip(long), if needed. byte[] skipBuffer = null; int nr = 0; if (skipBuffer == null) { skipBuffer = new byte[SKIP_BUFFER_SIZE]; } byte[] localSkipBuffer = skipBuffer; if (n <= 0) { return 0; } while (remaining > 0) { try { nr = inputStream.read(localSkipBuffer, 0, (int) Math.min(SKIP_BUFFER_SIZE, remaining)); } catch (IOException e) { e.printStackTrace(); } if (nr < 0) { break; } remaining -= nr; } return n - remaining; } }
最后
對(duì)于socket和 proto buffer來(lái)說(shuō),能靈活運(yùn)用燎悍,首先要感謝我們公司的同事客峭,沒有他們提供思路,估計(jì)很難靈活運(yùn)用socket和proto buffer唧席。其次,要感謝之前公司的大佬,還有給我提供寶貴意見的各位好友工腋。還有要感謝自己,能靜下心來(lái)畅卓,堅(jiān)持不懈擅腰,克服proto buffer和socket相結(jié)合的寫法。
感謝
Protocol Buffer技術(shù)詳解(語(yǔ)言規(guī)范)
Protocol Buffer 序列化原理大揭秘 - 為什么Protocol Buffer性能這么好翁潘?