主页 > 创业  > 

C#与php自定义数据流传输

C#与php自定义数据流传输

C#与php自定义数据流传输 介绍一、客户端与服务器数据传输流程图客户端发送数据给服务器:服务器返回数据给客户端: 二、自定义数据流C#版本数据流PHP版本数据流 三、数据传输测试1.在Unity中创建一个C#脚本NetWorkManager.cs2.服务器 目录创建StreamTest.php脚本代码如下:结果如下:这里需要注意一个问题,自定义数据类写入过程和读取过程顺序必须一致,否则无法获取数据。PHP中的pack与unpack的方法将数据转换为二进制的方法最好了解下。

介绍

如果不了解Unity与web如何通讯的可以看我之前的文章。 无论传输什么类型的数据,如int、float、string等,他们都被保存在文本中,接下来我们从字符串中解析这些数据。

一、客户端与服务器数据传输流程图 客户端发送数据给服务器:

服务器返回数据给客户端:

二、自定义数据流 C#版本数据流

我们要创建一个C#版本的数据流类,它的主要功能是将各种不同类型的数据压入一个单独的字符创中,或将从服务器读回的字节数组解析成响应的数据,这里要清楚不同类型数据所占字节长度,如32位int即占用4个字节,短整型short占2个字节等,代码如下:

using System.Collections; using System.Collections.Generic; using System.Text; using UnityEngine; public class PostStream { public Dictionary<string,string> Headers = new Dictionary<string, string>(); const int HASHSIZE = 16; //末尾16个字节保存md5数字签名 const int BYTE_LEN = 1; //byte占一个字节 const int SHORT16_LEN = 2; //short占2个字节 const int INT32_LEN = 4; //int占4个字节 const int FLOAT_LEN = 4; //float占4个字节 private int m_index = 0; public int Length { get { return m_index; } } //秘密密码,用于数字签名 private string m_secretKey = "123456"; //存储Post信息 private string[,] m_field; /// <summary> /// 最大传输数量 /// </summary> private const int MAX_POST = 128; /// <summary> /// 单位Post信息所存储的信息量 /// </summary> private const int PAIR = 2; /// <summary> /// 信息头索引 /// </summary> private const int HEAD = 0; /// <summary> /// 信息内容索引 /// </summary> private const int CONTENT = 1; /// <summary> /// 收到的字节数组 /// </summary> private byte[] m_bytes = null; public byte[] BYTES { get { return m_bytes; } } /// <summary> /// 发送的字符串 /// </summary> private string m_content = ""; /// <summary> /// 读取是否出现错误 /// </summary> private bool m_errorRead = false; /// <summary> /// 是否进行数字签名 /// </summary> private bool m_sum = true; /// <summary> /// 构造函数初始化 /// </summary> public PostStream() { Headers = new Dictionary<string,string>(); m_index = 0; m_bytes = null; m_content = ""; m_errorRead = false; } //这个类的第一部分是将不同类型的数据按POST格式压入到m_content字符串和二位字符串数组m_field中。m_content中的数据时实际发送的数据,m_field中的数据用于MD5数字签名。 #region 写入数据 /// <summary> /// 开始压数据,issum参数用来标识是否进行MD5数字签名 /// </summary> public void BeginWrite(bool issum) { m_index = 0; m_sum = issum; m_field = new string[MAX_POST, PAIR]; Headers.Add("Content-Type", "application/x- -form-urlencoded"); } /// <summary> /// head表示POST的名字,content是实际的数据内容 /// </summary> /// <param name="head"></param> /// <param name="content"></param> public void Write(string head, string content) { if (m_index >= MAX_POST) return; m_field[m_index, HEAD] = head; m_field[m_index, CONTENT] = content; m_index++; if (m_content.Length == 0) m_content += (head + "=" + content); else m_content += ("&" + head + "=" + content); Debug.LogError(m_content); } /// <summary> /// 使用MD5对字符串进行数字签名 /// </summary> public void EndWrite() { if (m_sum) { string hasstring = ""; for (int i = 0; i < MAX_POST; i++) hasstring += m_field[i, CONTENT]; hasstring += m_secretKey; //数据: content1content2...m_secretKey Debug.LogError("hasstring=" + hasstring); m_content += "&key=" + Md5Sum(hasstring);//数据: head1=content1&head2=content2...&key=(hasstring的MD5值) Debug.LogError("m_content=" + m_content); } m_bytes = Encoding.UTF8.GetBytes(m_content); } #endregion //第二部分是读取从服务器返回的数据。从服务器返回的数据时一个单独的字节数组,我们将这个数组解析为相应的数据,这个过程用到了最多的是BitConverter函数,它可以将相应长度的字节转为对应的数据 #region 读取数据 /// <summary> /// 读取数据 /// </summary> /// <param name=" "></param> /// <param name="issum"></param> /// <returns></returns> public bool BeginRead(WWW ,bool issum) { m_bytes = .bytes; m_content = .text; m_sum = issum; //错误 if (m_bytes == null) { m_errorRead = true; return false; } //读取前2个字节,获得字符串长度 short length = 0; this.ReadShort(ref length);//服务器这里做了处理,在写入数据时先写入一个short类型的数据代表数据长度 if (length != m_bytes.Length) { m_index = length; m_errorRead = true; return false; } //比较本地与服务器数字签名是否一致 if (m_sum) { byte[] localhash = GetLocalHash(m_bytes, m_secretKey); byte[] hashbytes = GetCurrentHash(m_bytes); if (!ByteEquals(localhash,hashbytes)) { m_errorRead = true; return false; } } return true; } /// <summary> /// 忽略一个字节 /// </summary> public void IgnoreByte() { if (m_errorRead) return; m_index += BYTE_LEN; } /// <summary> /// 读取一个字节 /// </summary> public void ReadByte(ref byte bts) { if (m_errorRead) return; bts = m_bytes[m_index]; m_index += BYTE_LEN; } /// <summary> /// 读取一个short /// </summary> /// <param name="number"></param> public void ReadShort(ref short number) { if (m_errorRead) return; number = System.BitConverter.ToInt16(m_bytes,m_index); m_index += SHORT16_LEN; } /// <summary> /// 读取一个int /// </summary> public void ReadInt(ref int number) { if (m_errorRead) return; number = System.BitConverter.ToInt32(m_bytes,m_index); m_index += INT32_LEN; } /// <summary> /// 读取一个float /// </summary> public void ReadFloat(ref float number) { if (m_errorRead) return; number = System.BitConverter.ToSingle(m_bytes, m_index); m_index += FLOAT_LEN; } /// <summary> /// 读取一个字符串 /// </summary> public void ReadString(ref string str) { if (m_errorRead) return; short num = 0; ReadShort(ref num); str = Encoding.UTF8.GetString(m_bytes,m_index,(int)num); m_index += num; } /// <summary> /// 读取一个bytes数组 /// </summary> /// <param name="bytes"></param> public void ReadBytes(ref byte[] bytes) { if (m_errorRead) return; short len = 0; ReadShort(ref len); //字节流 bytes = new byte[len]; for (int i = m_index; i < m_index + len; i++) { bytes[i - m_index] = m_bytes[i]; } m_index += len; } /// <summary> /// 结束读取 /// </summary> /// <returns></returns> public bool EndRead() { if (m_errorRead) return false; else return true; } #endregion /// <summary> /// 去掉服务器返回的数字签名,使用本地秘钥重新计算数字签名 /// </summary> /// <returns></returns> public static byte[] GetLocalHash(byte[] bytes,string key) { //hash bytes byte[] hashbytes = null; int n = bytes.Length - HASHSIZE; if (n < 0) return hashbytes; //获得key的bytes byte[] keybytes = System.Text.ASCIIEncoding.ASCII.GetBytes(key); //创建用于hash的bytes byte[] getbytes = new byte[n + keybytes.Length]; for (int i = 0; i < n; i++) { getbytes[i] = bytes[i]; } keybytes.CopyTo(getbytes,n); System.Security.Cryptography.MD5 md5; md5 = System.Security.Cryptography.MD5CryptoServiceProvider.Create(); return md5.ComputeHash(getbytes); } /// <summary> /// 获得从服务器返回的数字签名 /// </summary> /// <param name="bytes"></param> /// <returns></returns> public static byte[] GetCurrentHash(byte[] bytes) { byte[] hashbytes = null; if (bytes.Length < HASHSIZE) return hashbytes; hashbytes = new byte[HASHSIZE]; for (int i = bytes.Length - HASHSIZE; i < bytes.Length; i++) { hashbytes[i - (bytes.Length - HASHSIZE)] = bytes[i]; } return hashbytes; } #region 比较两个bytes数组是否相等 /// <summary> /// 比较两个bytes数组是否相等 /// </summary> /// <param name="a"></param> /// <param name="b"></param> /// <returns></returns> public static bool ByteEquals(byte[] a,byte[] b) { if (a == null || b == null || a.Length != b.Length) return false; for (int i = 0; i < a.Length; i++) { if (a[i] != b[i]) return false; } return true; } #endregion #region 取字符串md5值 /// <summary> /// md5值 /// </summary> /// <param name="strToEncrypt">//数据: head1content1head2content2...m_secretKey</param> /// <returns></returns> public static string Md5Sum(string strToEncrypt) { byte[] bs = UTF8Encoding.UTF8.GetBytes(strToEncrypt); System.Security.Cryptography.MD5 md5; md5 = System.Security.Cryptography.MD5CryptoServiceProvider.Create(); byte[] hashBytes = md5.ComputeHash(bs); string hashString = ""; for (int i = 0; i < hashBytes.Length; i++) { hashString += System.Convert.ToString(hashBytes[i], 16).PadLeft(2,'0'); } return hashString.PadLeft(32,'0'); } #endregion } PHP版本数据流

PHP版本的代码与C#版本如出一辙,只是换成了PHP的语法:

<?php //PHPStream.php define("BYTE",1); define("SHORT",2); define("INT",4); define("FLOAT",4); define("HASHSIZE",16); define("PKEY",123456); class PHPStream { private $Key = ""; public $bytes = ""; public $Content = ""; public $index = 0; public $ErrorRead = false; //开始写数据 function BeginWrite( $Key ) { $this->index=0; $this->bytes=""; $this->Content=""; $this->ErrorRead=false; //total bytes length $this->WriteShort(0);//服务器这里在发送数据的时候会先去写入一个short,代表数据长度 if ( strlen($Key) ) { $this->Key=$Key; } } //写一个byte function WriteByte( $byte ) { //$this->bytes.=pack('c',$byte); $this->bytes.=$byte; $this->index+=BYTE; } //写一个short function WriteShort( $number ) { $this->bytes.=pack("v",$number); $this->index+=SHORT; } //写一个32位int function WriteInt( $number ) { $this->bytes.=pack("V",$number); $this->index+=INT; } //写一个float function WriteFLOAT( $number ) { $this->bytes.=pack("f",$number); $this->index+=FLOAT; } //写一个字符串 function WriteString( $str ) { $len=strlen($str); $this->WriteShort($len); $this->bytes.=$str; $this->index+=$len; } //写一组byte function WriteBytes( $bytes ) { $len=strlen($bytes); $this->WriteShort($len); $this->bytes.=$bytes; $this->index+=$len; } function EndWrite() { //数字签名 if ( strlen($this->Key)>0 ) { $len=$this->index+HASHSIZE; $str=pack("v",$len); //猜测这里的bytes内部对应是0-1 2-3 4-5 6-7 //猜测内部为键值对 $str[0] = $str[1] $this->bytes[0]=$str[0];//猜测为key值 $this->bytes[1]=$str[1];//猜测为key值对应的value //获取md5值 $hashbytes=md5($this->bytes.$this->Key,true); $this->bytes.=$hashbytes; } else { $str=pack("v",$this->index); $this->bytes[0]=$str[0]; $this->bytes[1]=$str[1]; } } //开始读入数据 function BeginRead( $Key ) { $this->index=0; $this->bytes=""; $this->Content=""; $this->ErrorRead=false; if ( strlen($Key)>0 )//strlen检测字符串长度 { $this->Key=$Key; } } //读取POST信息 function Read( $head ) { if( isset($_POST[$head]) ) { $this->Content.=$_POST[$head]; return $_POST[$head]; } else { $this->ErrorRead=true; } } //结束读取 function EndRead() { if ($this->ErrorRead) return false; if (strlen($this->Key)<1) return true;//如果不需要签名验证则将原本的PKEY改为空字符串 //取得数字签名 $hashkey=""; if ( isset($_POST["key"]) ) $hashkey=$_POST["key"]; else { $this->ErrorRead=true; return false; } //重新计算数字签名 $localhash=md5($this->Content.$this->Key); //比较数字签名 if (strcmp($hashkey,$localhash)==0) return true;//strcmp检测两个字符串是否一致 else { $this->ErrorRead=true; return false; } } } ?> 三、数据传输测试 1.在Unity中创建一个C#脚本NetWorkManager.cs

在脚本中创建一个WWW实例,分别发送int、float、short和string类型的数据至服务器,服务器收到后再将这些数据返回给Unity,下面是C#代码:

using System.Collections; using System.Collections.Generic; using UnityEngine; public class NetworkManager : MonoBehaviour { public const string URL = "http://192.168.1.5:8088/StreamTest.php"; private void Start() { StartCoroutine(Test()); } IEnumerator Test() { PostStream poststream = new PostStream(); int integer = 1000; float number = 8.99f; short small = 30; string txt = "编程其乐无穷"; //开始写入数据并指定需要签名认证 poststream.BeginWrite(true); //写入数据Content: integer=1000 poststream.Write("integer",integer.ToString()); //写入数据Content: integer=1000&number=8.99 poststream.Write("number",number.ToString()); //写入数据Content: integer=1000&number=8.99&short=30 poststream.Write("short",small.ToString()); //写入数据Content: integer=1000&number=8.99&short=30&string=编程其乐无穷 poststream.Write("string",txt); //1.最终签名认证的数据Content: integer=1000&number=8.99&short=30&string=编程其乐无穷&key=c344b95687a03452d4bf479a89affb94 //解释: c344b95687a03452d4bf479a89affb94为“10008.9930编程其乐无穷123456”字符串的MD5值 //123456为用于签名的密码 其组成是由写入的数组+密码组成 //2.最终非签名认证的数据Content: integer=1000&number=8.99&short=30&string=编程其乐无穷 poststream.EndWrite(); //服务器Post请求 WWW = new WWW(URL,poststream.BYTES,poststream.Headers); yield return ; //无错误 if ( .error != null) { Debug.LogError( .error); } else//读取返回值 { poststream = new PostStream(); poststream.BeginRead( , true); poststream.ReadInt(ref integer); poststream.ReadFloat(ref number); poststream.ReadShort(ref small); poststream.ReadString(ref txt); bool ok = poststream.EndRead(); if (ok) { Debug.LogError(integer); Debug.LogError(number); Debug.LogError(small); Debug.LogError(txt); } else { Debug.LogError("error"); } } } } 2.服务器 目录创建StreamTest.php脚本代码如下: <?php //StreamTest.php header('Content-Type:text/html; charset=utf-8'); require_once("PHPStream.php");//引用PHPStream.php文件 //read $stream=new PHPStream(); $stream->BeginRead(PKEY);//与客户端对应的数字签名密码 $integer=$stream->Read("integer");//从传入的数据中找到Key值为integer的Value $number=$stream->Read("number");//从传入的数据中找到Key值为number的Value $short=$stream->Read("short");//从传入的数据中找到Key值为short的Value $str=$stream->Read("string");//从传入的数据中找到Key值为string的Value $ok=$stream->EndRead(); if ($ok) { //开始写入一个short: bytes=pack("v",0) //开始的index: index = 0+2 $stream->BeginWrite(PKEY); //写入一个Int: bytes=pack("v",0)+pack("V",$integer) //当前index: index = 0 + 2 + 4 $stream->WriteInt($integer); //写入一个Float: bytes=pack("v",0)+pack("V",$integer)+pack("f",$number) //当前index: index = 0 + 2 + 4 + 4 $stream->WriteFloat($number); //写入一个Float: bytes=pack("v",0)+pack("V",$integer)+pack("f",$number)+pack("v",$short) //当前index: index = 0 + 2 + 4 + 4 + 2 $stream->WriteShort($short); //写入一个String: bytes=pack("v",0)+pack("V",$integer)+pack("f",$number)+pack("v",$short)+[pack("v",strlen($str))+$str] //当前index: index = 0 + 2 + 4 + 4 + 2 + (2 + strlen($str)) $stream->WriteString($str); //带有签名bytes 末尾加md5(bytes=pack("v",0)+pack("V",$integer)+pack("f",$number)+pack("v",$short)+[pack("v",strlen($str))+$str]) (无签名则不加) //带有签名index: index = 0 + 2 + 4 + 4 + 2 + (2 + strlen($str)) + 16 (无签名就去掉+16) $stream->EndWrite(); echo $stream->bytes; } else { echo "error"; } ?> 结果如下:

这里需要注意一个问题,自定义数据类写入过程和读取过程顺序必须一致,否则无法获取数据。 PHP中的pack与unpack的方法将数据转换为二进制的方法最好了解下。
标签:

C#与php自定义数据流传输由讯客互联创业栏目发布,感谢您对讯客互联的认可,以及对我们原创作品以及文章的青睐,非常欢迎各位朋友分享到个人网站或者朋友圈,但转载请说明文章出处“C#与php自定义数据流传输