.net 与android的跨平台tcp网络通信的字节处理(大端序,小端序,字节转换,网络字节)

-- .net 与android的跨平台的tcp网络通信处理
【官网】:#

应用场景

在涉及服务端与设备通信时往往会涉及跨平台tcp通信,涉及到的具体字节端序问题需要仔细处理

基础资源

关注各平台各编程语言的字节端序

使用须知

注意各个项目内部的通信协议,本例仅作为示例。

配置步骤

A.)主业务流程顺序.

1.)梳理两端的通讯协议,发送方的封包和接收方的解包是一个对称的过程,另外如果跨语言,跨平台还需要考虑字节的大端序,小端序等.

2.)考虑以哪一方作为服务端.//需要考虑是否需要端口映射,客户端有多少个连接等.

B.)应用案例.

B1.)关于一个跨平台tcp通信的应用:android插件与pc的调用.

1.)启动androidserver端)的监听服务.

2.)pc(client)进行端口映射.//端口映射之前,确保对方有监听的服务,否则没有意义.

3.) pc(client)进行连接服务端.

4.) android端可以发送.

C.)关于对象的创建及维持.

1)c#端的对象的使用.

:无论是读还是写,如果想重用当前的TcpSocket连接,则这个Networkstream对象不能执行.close,.dispose,或者在using中执行...因为实践发现stream关闭后对应的socket连接也会关闭...造成网络的另一端断开连接,抛异常..

[]保持socket连接,适用于比如(pcandroid设备一一对应),不会出现连接数太多导致连接数不够的问题.如果连接数太多,则需要每次都进行创建和维持.

2)c#读取tcp网络字节的格式:

NetworkStream stream = null;

try

{

stream = this.GetClientStream();//TcpClient中获取流.

while (this.IsConnected() && stream != null && (iTotalRecv < iNeedRecvLen))

{

if (stream.DataAvailable == false)  //没有可读数据就等待

{

continue;

}

if (this.IsConnected() && stream != null && stream.CanRead)//socket处于连接状态,且流可读

{

iRead = 0;

iRead = stream.Read(buffer, iTotalRecv, iNeedRecvLen - iTotalRecv);

if (iRead <= 0)

{

break;

}

iTotalRecv += iRead;

}

}

}

D.)细节注意点.

1.)需要注意,c#端在端口映射时,只管pc端的端口自己控制,手机端的端口需要手机中的服务自行定义并约定.

2.)android插件涉及相关的服务的androidmanifest.xml中的生命,及相关的网络,本地存储等权限生命都是必须的.

3.)需要考虑手机端插件内部监听的端口不一致的问题,比如万一一个手机上的我们约定的端口被其它APP占用,则应该想办法给结束掉.如果是不一致的,则需要想办法进行通知pc.

4.)再次强调,服务端和客户端tcp处理的封包,解包的对称性,不可盲目各写各的.

5.)byte来说各平台的接收处理都是一样的,只是在涉及(字节intlong类型的包长)的转换方面,各个平台语言是不一样的,很容易出现包场紊乱进而导致粘包,丢包或无法收包等问题.

//c#发送给java包的长度

///

/// 写入大端序的long

///

///

private byte[] LongToBytes(long lLength)

{

//方案1:跨平台语言(测试通过)

//byte[] bs = BitConverter.GetBytes(lLength);

//Array.Reverse(bs);

//return bs;

//方案2:跨平台语言(测试通过)

return BitConverter.GetBytes(IPAddress.HostToNetworkOrder((long)lLength));

}


//java从c#哪里接收包的长度

private long GetBytesFromLong(byte [] bytes){

ByteBuffer buffer = ByteBuffer.allocate(8);

buffer.put(bytes, 0, bytes.length);

buffer.flip();

return buffer.getLong();

}

[]节序,又称端序,尾序,英文:Endianness

在计算机科学领域中,字节序是指存放多字节数据的字节(byte)的顺序,典型的情况是整数在内存中的存放方式和网络传输的传输顺序。Endianness有时候也可以用指位序(bit)。

大小端序跟硬件的体系结构有关,所有x86系列的pc机都是小端序,跟操作系统无关。在x86系列的pc上的solaris系统是小端序,sun sparc平台的solaris是大端序。

大端字节序,高字节存于内存低地址,低字节存于内存高地址;小端字节序反之.

E)关于稳定性.

1.)对于(pc-手机)这个一一对应的组合内通信,保持一个连接即可,可以通过单例模式实现.

2.)如果遇到上述组合的通信失败,可以调用tcp重连进行处理.

常见问题

快速入门

【c#端示例】

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Net.Sockets;
using System.Threading;
using System.Net;
using MobileControlSDK.Publics;

namespace MobileControlSDK.Components
{
    /// <summary>
    /// 功能简介:tcp Socket客户端操作的封装.主要用于最新版的android sdk的通信处理
    /// 创建时间:2017-6-6
    /// 创建人:config.net.cn
    /// </summary>
    public class TcpClientWrapper
    {
        private TcpClient client = null;
        private string serverIPAddress = "";
        private int port = 0;
        private bool _stopListen = false;
        /// <summary>
        /// 停止监听
        /// </summary>
        public bool StopListen 
        {
            get { return _stopListen; }
            set { _stopListen = value; }
        }
        private Thread thread_ListenServer = null;
        /// <summary>
        /// 接收字节的事件处理
        /// </summary>
        public event Delegate_OnReceiveBytes OnReceiveBytes = null;
        /// <summary>
        /// Tcp的网络流对象
        /// </summary>
        public NetworkStream GetClientStream()
        {
            if (this.IsConnected())
            {
                return this.client.GetStream();
            }
            return null;
        }

        /// <summary>
        ///
        /// </summary>
        /// <param name="sServerIPAddress">服务端IP地址</param>
        /// <param name="iPort">端口</param>
        public TcpClientWrapper(string sServerIPAddress, int iPort)
        {
            this.serverIPAddress = sServerIPAddress;
            this.port = iPort;
        }
        /// <summary>
        /// 开启监听
        /// </summary>
        public Enum_TcpSocketStatus StartListen()
        {
            bool IsConnected = false;
            if (thread_ListenServer != null)
            {
                try
                {
                    thread_ListenServer.Abort();
                    thread_ListenServer.Join();
                    thread_ListenServer = null;
                }
                catch (Exception ex)
                {

                }
            }
            IsConnected = this.ConnectToServer();
            if (IsConnected == false)
            {
                return Enum_TcpSocketStatus.ConnectedFailure;
            }
            thread_ListenServer = new Thread(this.ListenServer);
            thread_ListenServer.Start();
            return Enum_TcpSocketStatus.Success;
        }
        /// <summary>
        /// Tcp是否正常连接
        /// </summary>
        /// <returns></returns>
        public bool IsConnected()
        {
            if (this.client == null)
                return false;
            if (!this.client.Connected)
                return false;
            return true;
        }
        /// <summary>
        /// 关闭套接字
        /// </summary>
        private void CloseSocket()
        {
            if (this.client != null)
            {
                try
                {
                    if (this.client != null)
                        this.client.Close();
                    this.client = null;
                }
                catch (Exception ex)
                {

                }
            }
        }
        /// <summary>
        /// 连接服务端
        /// </summary>
        /// <returns></returns>
        private bool ConnectToServer()
        {
            bool bSuccess = false;
            //关闭已有套接字
            try
            {
                this.CloseSocket();
            }
            catch (Exception error)
            {
                MobileControlRunner.Instance.RunTimeProcess.GetOutLog().OnCachException(error, "TcpClientWrapper.ConnectToServer(1)");
            }
            //连接服务端
            try
            {
                this.client = new TcpClient();
                this.client.Connect(new IPEndPoint(IPAddress.Parse(this.serverIPAddress), this.port));
                if (this.client.Connected)
                {
                    bSuccess = true;
                }
            }
            catch (Exception error)
            {
                MobileControlRunner.Instance.RunTimeProcess.GetOutLog().OnCachException(error, "TcpClientWrapper.ConnectToServer(2)");
                bSuccess = false;
            }
            return bSuccess;
        }

        /// <summary>
        /// 监听服务端
        /// </summary>
        private void ListenServer()
        {
            while (this.client != null && this.client.Connected)
            {
                if (this.StopListen == true)
                    break;
                byte[] bytesData = this.RecvPacket();
                if (bytesData == null)
                {
                    continue;
                }
                if (this.OnReceiveBytes != null)
                {
                    this.OnReceiveBytes(bytesData);
                }
                else
                {
                    bytesData = null;
                }
            }
        }

        #region//接收数据
        //接收数据,返回接收的长度
        private int RecvData(byte[] buffer)
        {
            return RecvData(buffer, buffer.Length);
        }

        //接收指定字节的数据,返回接收的长度
        private int RecvData(byte[] buffer, int iNeedRecvLen)
        {
            int iTotalRecv = 0;
            int iRead = 0;
            NetworkStream stream = null;
            try
            {
                stream = this.GetClientStream();
                while (this.IsConnected() && stream != null && (iTotalRecv < iNeedRecvLen))
                {
                    if (stream.DataAvailable == false)
                    {
                        continue;
                    }
                    if (this.IsConnected() && stream != null && stream.CanRead)
                    {
                        iRead = 0;
                        iRead = stream.Read(buffer, iTotalRecv, iNeedRecvLen - iTotalRecv);
                        if (iRead <= 0)
                        {
                            break;
                        }
                        iTotalRecv += iRead;
                    }
                }
            }
            catch (Exception ex)
            {
                MobileControlRunner.Instance.RunTimeProcess.GetOutLog().OnCachException(ex, "TcpClientWrapper.RecvData");
                if (stream != null)
                    stream.Close();
                if (stream != null)
                    stream.Dispose();
            }
            finally
            {
                //在这里执行stream.close或dispose之后会触发socket关闭
            }


            return iTotalRecv;
        }

        //接收数据.
        private byte[] RecvPacket()
        {
            byte[] lenBuffer = new byte[8];
            int iRecvLen = RecvData(lenBuffer);
            if (iRecvLen != 8)
            {
                return null;
            }
            long dataLen = BitConverter.ToInt64(lenBuffer, 0);
            //long dataLen = bytesToLong(lenBuffer);
            if (dataLen <= 0)
            {
                return null;
            }
            if (dataLen > int.MaxValue)
            {
                return null;
            }
            byte[] dataBuffer = new byte[(int)dataLen];
            RecvData(dataBuffer);
            return dataBuffer;
        }
        #endregion


        #region//发送数据
        /// <summary>
        /// 发送指定字节数组的指定长度的数据
        /// </summary>
        /// <param name="Bytes"></param>
        /// <param name="iLen"></param>
        /// <returns></returns>
        private int SendDataByLen(byte [] Bytes,int iLen,bool bIsFlushStream)
        {
            if (Bytes==null)
            {
                return 0;
            }
            try {
                NetworkStream stream = this.GetClientStream();
                if (stream != null && stream.CanWrite && this.IsConnected())
                {
                    stream.Write(Bytes, 0, iLen);
                    if (bIsFlushStream)
                    {
                        stream.Flush();
                    }
                    return iLen;
                }
            }
            catch(Exception ex)
            {
                MobileControlRunner.Instance.RunTimeProcess.GetOutLog().OnCachException(ex, "TcpClientWrapper.SendDataByLen");
            }
            return 0;
        }
        /// <summary>
        /// 发送指定字节的数据
        /// </summary>
        /// <param name="Bytes"></param>
        /// <returns></returns>
        public long SendData(byte[] Bytes)
        {
            byte[] bytesLen_Bytes = null;
            if (Bytes == null)
            {
                bytesLen_Bytes = this.LongToBytes((long)0); 
                return SendDataByLen(bytesLen_Bytes, 8,true);
            }
            bytesLen_Bytes = this.LongToBytes((long)Bytes.Length);
            this.SendDataByLen(bytesLen_Bytes, 8,false);
            return this.SendDataByLen(Bytes, Bytes.Length,false);
        }
        #endregion

        #region//转换函数
        /// <summary>
        /// 写入大端序的long
        /// </summary>
        /// <param name="value"></param>
        private byte[] LongToBytes(long lLength)
        {
            //方案1:跨平台语言
            //byte[] bs = BitConverter.GetBytes(lLength);
            //Array.Reverse(bs);
            //return bs;
            //方案2:跨平台语言
            return BitConverter.GetBytes(IPAddress.HostToNetworkOrder((long)lLength)); 
        }
        #endregion
    }

}


【android端示例】


package common.protocal.tcpwrapper;
import java.io.BufferedReader;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.OutputStream;
import java.net.Socket;
import java.nio.ByteBuffer;
import java.nio.ByteOrder;

import common.utils.APPLog;
/*
 * 功能简介:自定义Tcp连接客户端.
 * 创建时间:2017-5-31
 * 创建人: pcw
 * */
public class CustomTcpClient {
//字节数组转换为长整形.
public static long bytesToLong(byte[] intByte) {
return    intByte[0] & 0xff 
               | (intByte[1] & 0xff) << 8 
               | (intByte[2] & 0xff) << 16
               | (intByte[3] & 0xff) << 24
               | (intByte[4] & 0xff) << 32 
       | (intByte[5] & 0xff) << 40
       | (intByte[6] & 0xff) << 48
       | (intByte[7] & 0xff) << 56;
}
//长整形转换为字节数组.
public static byte[] longToBytes(long i) {
byte[] result = new byte[8];
result[7] = (byte) ((i >> 56) & 0xFF);
result[6] = (byte) ((i >> 48) & 0xFF);
result[5] = (byte) ((i >> 40) & 0xFF);
result[4] = (byte) ((i >> 32) & 0xFF);
result[3] = (byte) ((i >> 24) & 0xFF);
result[2] = (byte) ((i >> 16) & 0xFF);
result[1] = (byte) ((i >> 8) & 0xFF);
result[0] = (byte) (i & 0xFF);
return result;
}
public CustomTcpClient(Socket clientSock)
{
m_Socket = clientSock;
}
//接收数据,返回接收的长度
public int RecvData(byte[] buffer)
{
return RecvData(buffer,buffer.length);
}

//接收数据,返回接收的长度
public int RecvData(byte[] buffer,int iNeedRecvLen)
{
int iTotalRecv = 0;
try {
InputStream inputStream = m_Socket.getInputStream();
while (iTotalRecv<iNeedRecvLen) {
int iRead = inputStream.read(buffer, iTotalRecv, iNeedRecvLen-iTotalRecv);
if (iRead <= 0) {
break;
}
iTotalRecv += iRead;
}
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
return iTotalRecv;
}

//发送数据
public int SendData(byte[] data,int iLen)
{
try {
OutputStream outputStream = m_Socket.getOutputStream();
outputStream.write(data, 0, iLen);
return iLen;
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
return 0;
}
public long SendPacket(byte []data) {
if (data == null) {
return SendData(longToBytes(0), 8);
}
return SendPacket(data, data.length);
}
//发送和接收一整个包
public long SendPacket(byte []data,int iLen) {
//先发8字节长度
SendData(longToBytes(iLen), 8);
return SendData(data, iLen);
}
private int BytesToLongForCrossPlat(byte [] bytes){
ByteBuffer bbBuffer = ByteBuffer.wrap(bytes);
bbBuffer.order(ByteOrder.BIG_ENDIAN);
return bbBuffer.getInt();
}
private long GetBytesFromLong(byte [] bytes){
ByteBuffer buffer = ByteBuffer.allocate(8);   
buffer.put(bytes, 0, bytes.length);  
      buffer.flip();
      return buffer.getLong();  
}
//接收数据(解决跨平台问题)
public byte[]  RecvPacket() {
byte[] lenBuffer = new byte[8];
int iRecvLen =0;
iRecvLen=RecvData(lenBuffer);
if (iRecvLen != 8) {
return null;
}
    long dataLen=this.GetBytesFromLong(lenBuffer);
//long dataLen=this.bytesToLong(lenBuffer);
//long dataLen =this.BytesToLongForCrossPlat(lenBuffer);
//APPLog.LogDebugToFile(String.format("Len=%s",String.valueOf(dataLen)));
if (dataLen <= 0) {
return null;
}
if (dataLen > 10 *1024*1024) {
return null;
}
byte[] dataBuffer = new byte[(int)dataLen];
RecvData(dataBuffer);
return dataBuffer;
}

private Socket m_Socket;
}



参考资料