好了,已经连上服务器了,可是怎么通信,,,byte[]是个什么鬼?
好吧,来了解下数据转换吧,还记得刚学编程时,字符串转Int的Convert吗?
public void Convert()
{
string str = "hello world";
byte[] strbyte = Encoding.UTF8.GetBytes(str);
Debug.Log(Encoding.UTF8.GetString(strbyte,0,strbyte.Length));
int n = 13;
byte[] nbyte = BitConverter.GetBytes(n);
Debug.Log(BitConverter.ToInt32(nbyte,0));
}
上面的就是两个常用的方法,第一个Encoding是编码类,里面有好多种编码,Utf8是万国码,你要想支持中文就得选这个。Ascii码学过汇编的都懂,就255个字符,中文指定不够用。
下面的BitConverter是值类型的转换,我是这么理解的,也没去查,说错就说错吧。
都知道string类型其实是个引用类型,说白了就是个对象,所以在转换的时候有三个参数,第一个是string,第二个是起始位置(内存的起始位置),第三个是长度(占用内存的长度)。
而BitConverter呢,只需要知道起始位置就行了,因为值类型的长度是固定的。
关于Encoding和BitConverter的内容可以上msdn查阅,我也不或者点这里了。
那上一篇的代码,我们只需要在Receive中间加入一句代码
private void Receive(IAsyncResult ar)
{
int size = client.EndReceive(ar);
//Debug.Log("收到数据" + size);
if (size < 1)
{
Debug.Log("服务器关闭");
closeConnect();
return;
}
Debug.Log(Encoding.UTF8.GetString(receiveData,0,size));//输出接受到的字符串
client.BeginReceive(receiveData, 0, receiveData.Length, SocketFlags.None, new AsyncCallback(Receive), null);
}
这样我们就能把服务器发送过来的字符串编译到本地了,然后继续等待.....
然后你会发现然而并没有什么卵用,,,
是的,我们不可能只传字符串,这连聊天室的功能都满足不了,我们要传的是数据,里面有各种各样的类型,变量等!!
public static byte[] StructToBytes(object structObj)//结构体转byte[]
{
int size = Marshal.SizeOf(structObj);//获得结构体大小
byte[] bytes=new byte[size];
IntPtr structPtr = Marshal.AllocHGlobal(size);//从非托管内存中划片空间
Marshal.StructureToPtr(structObj,structPtr,false);//把结构体放到非托管内存中就变成byte[]了
Marshal.Copy(structPtr,bytes,0,size);//把bytes[]从内存中复制出来
Marshal.FreeHGlobal(structPtr);//清理内存
return bytes;//输出
}
public static object BytesToStruct(byte[] bytes,Type type)//byte[]转结构体
{
int size = Marshal.SizeOf(type);//获得结构体类型大小
if (size > bytes.Length)//类型大小比byte[]大,就跳出
return null;
IntPtr structPtr = Marshal.AllocHGlobal(size);//开辟空间
Marshal.Copy(bytes,0,structPtr,size);//复制到内存中
object obj = Marshal.PtrToStructure(structPtr, type);//从非托管内存中转换成对应类型的obj托管对象
Marshal.FreeHGlobal(structPtr);//清理内存
return obj;
}
[Serializable]//可被序列化
[StructLayoutAttribute(LayoutKind.Sequential, CharSet = CharSet.Ansi, Pack = 1)]//整理内存布局
struct Person
{
public string name;
public int age;
}
public void Test()
{
Person P1 = new Person();
P1.name = "叶良辰";
P1.age = 250;
byte[] data = StructToBytes(P1);
Person P2 = (Person)BytesToStruct(data,typeof(Person));
Debug.Log("姓名:"+P2.name+"\n年龄:"+P2.age);
}
上面这个代码有些和网上的很象,没错,我就是抄的,啊,原文请点这里
序列化嘛,肯定的,设置内存布局,应该就是让这个结构体在内存中排列的规整点,我是这么猜的,说错就错了吧。
通过这段代码,我们可以把结构体转换成byte[],然后接受后再转换为对应的结构体(也可以转class),本地测试貌似没问题。
但实际上我在应用中遇到了很多问题,大部分问题出在这个托管与非托管上面,
托管就是有中间语言的,说白了.net执行的代码,非托管,就没中间语言了,系统直接执行的。
具体报什么错我忘了,毕竟好早以前的事了。
而且再看写法,发现很繁琐。
有没有不整什么托管非托管,内存不内存的1,2行代码就搞定的。
还真有,还记得struct上面的[Serializable]吗,Serializable特性,对,直接序列化就行了,好傻啊,简直是饶了一大弯。
public static byte[] ObjectToByte(object obj)//序列化
{
using (MemoryStream ms = new MemoryStream())//申请流内存,结束自动收回
{
BinaryFormatter bf = new BinaryFormatter();
bf.Serialize(ms, obj);//序列化
return ms.GetBuffer();//取出byte[]
}
}
public static object ByteToObject(byte[] data)//反序列化
{
using (MemoryStream ms = new MemoryStream(data))
{
BinaryFormatter bf = new BinaryFormatter();
return bf.Deserialize(ms);//反序列化
}
}
[Serializable]//可被序列化
struct Person
{
public string name;
public int age;
}
public void Test()
{
Person P1 = new Person();
P1.name = "叶良辰";
P1.age = 250;
byte[] data = ObjectToByte(P1);
Person P2 = (Person)ByteToObject(data);
Debug.Log("姓名:" + P2.name + "\n年龄:" + P2.age);
}
现在看这两个转换方法,是不是很简洁,两行代码搞定,而且反序列化也不需要知道什么类型,
最最重要的根本不用考虑什么托管与非托管,想干啥就干啥。
这样我们就可以随心所欲的在服务器和客户端之间传递任何数据了,当然,服务端必须是C#写的。
服务端是其他语言写的,目前还没研究过,等接触了再补篇吧。
关于序列化的详细内容请自行百度。