unity学习

Unity3D Socket通信的简单实现

2017-06-19  本文已影响1005人  RE_my_world

Socket的定义

socket英文的含义为插座、孔,在我们的网络应用中通常称为套接字,大致理解为在tcp/ip网络抽象层中使用套接字ip+端口的网络通信协议,可认为是介于传输层与应用层中抽象出的socket层,我们可以使用它的接口来解决复杂的网络请求。

GitHub

Tcp/IP

GitHub GitHub
定义:

ransmission Control Protocol/Internet Protocol的简写,中译名为传输控制协议/因特网互联协议,又名网络通讯协议,是Internet最基本的协议、Internet国际互联网络的基础,由网络层的IP协议和传输层的TCP协议组成。

组成:

由四部分組成,从低至高分別为链路层、网络层、传输层和应用层

Socket通信过程

GitHub

名词解析:

IPv4中规定IP地址长度为32,即有232-1(符号表示升幂,下同)个地址

IPv6是Internet Protocol Version 6的缩写,其中Internet Protocol译为“互联网协议”。IPv6是IETF(互联网工程任务组,Internet Engineering Task Force)设计的用于替代现行版本IP协议(IPv4)的下一代IP协议。

IPv6中IP地址的长度为128,即有2^128-1个地址。

IPv6具有更高的安全性。在使用IPv6网络中用户可以对网络层的数据进行加密并对IP报文进行校验,极大的增强了网络的安全性。

代码实现


string[]handled=tcp.Split(newChar[]{':'});

stringhost=handled[0];

inthostPort=handled.Length>1?int.Parse(handled[1]):80;

//地址封装

IPEndPointport=newIPEndPoint(Dns.GetHostAddresses(handled[0])[0],hostPort);

//创建socket资源,新实例初始化 Socket 类使用指定的地址族、 套接字类型和协议。

socket=newSocket(

    AddressFamily.InterNetwork,

    System.Net.Sockets.SocketType.Stream,

    ProtocolType.Tcp

);


...

//连接服务器

IAsyncResultresult=socket.BeginConnect(

    port, ConnectCallBack, socket

);

//5s内判断是否连接成功

boolisSucced=result.AsyncWaitHandle.WaitOne(5000,true);

...

void ConnectCallBack(IAsyncResultresult){

    Socketsocket=(Socket)result.AsyncState;

    //异步挂载,等待数据接收
         socket.BeginReceive(receiveData,0,receiveData.Length,SocketFlags.None,newAsyncCallback(RecivieCallBack),socket);

}


void ConnectCallBack(IAsyncResultresult){

    Socketsocket=(Socket)result.AsyncState;

    socket.BeginReceive(receiveData,0,receiveData.Length,SocketFlags.None,newAsyncCallback(RecivieCallBack),socket);

}

private void RecivieCallBack(IAsyncResultresult){

    Socketsocket=(Socket)result.AsyncState;

    //接收长度

    intlength=socket.EndReceive(result);

    if(length<=0){

        Console.Log(LogType.SOCKET,"server does not return data, but not disconnect");

    return;

    }

    //保持数据同步,对二进制字节拆包处理

    lock(receiveData){

    SplitePackage(receiveData,length);

    }

    try{

    if(respsonList.Count>0){

          respsonList.ForEach(packet=>{

              sub.OnNext(packet);

          });

          respsonList.Clear();

      }

        socket.BeginReceive(receiveData,0,receiveData.Length,SocketFlags.None,newAsyncCallback(RecivieCallBack),socket);

     }

    catch(Exceptione){

    //Debug.LogError (e.ToString());

}


private void SaveBreak(byte[]data,intindex){

    streamLen=data.Length-index;

    byte[]breakData=newbyte[streamLen];

    Array.Copy(data,index,breakData,0,breakData.Length);

    stream.SetLength(streamLen);

    stream.Write(breakData,0,breakData.Length);

    isBreakPacket=true;

}

private void EndBreak(){

    if(stream!=null){

    stream.Close();

    stream=newMemoryStream();

    streamLen=0;

    isBreakPacket=false;

    }

}

//拆包

public void SplitePackage(byte[]data,intbufferLen){

    //包头位置

    intindex=0;

    //断包判断

    byte[]bytes=newbyte[bufferLen];

    Array.Copy(data,bytes,bufferLen);

    //处于断包状态

    if(isBreakPacket){

    byte[]packetData=stream.ToArray();

    byte[]newData=newbyte[streamLen+bufferLen];

    Array.Copy(packetData,0,newData,0,streamLen);

    index+=streamLen;

    Array.Copy(bytes,0,newData,index,bufferLen);

    bytes=newData;

    index=0;

    EndBreak();

    }

    //循环拆包(粘包处理)

    while(index

    //断包头

    if((index+4)>=bytes.Length){

    SaveBreak(bytes,index);

    return;

    }

    //拆包头

    byte[]head=newbyte[4];

    Array.Copy(bytes,index,head,0,4);

    index+=4;

    Array.Reverse(head);

    intcount=BitConverter.ToInt32(head,0);

    //异常处理

    if(count>8192){

    EndBreak();

    HandleError("包头过长");

    return;

    }

    //断内容处理

    if((index+count)>bytes.Length){

    index-=4;

    SaveBreak(bytes,index);

    return;

    }

    //正常包处理

    byte[]callbackData=newbyte[count];
  
    Array.Copy(bytes,index,callbackData,0,count);

    stringiContent=Encoding.UTF8.GetString(callbackData);

    if(count==0){

    EndBreak();

    HandleError("接收数据为空");

    }

    respsonList.Add(iContent);

    index+=callbackData.Length;

    }

}


public void SendMessage(stringstr)

{

    if(socket!=null&&!socket.Connected){

    EndBreak();

    HandleError("socket is not connecting || socket is not existed");

    return;

    }

    byte[]msg=Encoding.UTF8.GetBytes(str);

    intmesgLength=msg.Length;

    byte[]head=BitConverter.GetBytes(mesgLength);

    byte[]sendData=newbyte[mesgLength+head.Length];
  
    Array.Reverse(head);//服务器与客户端的包头的解析顺序是相反的

    Array.Copy(head,sendData,head.Length);

    Array.Copy(msg,0,sendData,head.Length,msg.Length);

    try{

        IAsyncResultasyncSend=socket.BeginSend(sendData,0,sendData.Length,SocketFlags.None,newAsyncCallback(sendCallback),socket);

        boolsuccess=asyncSend.AsyncWaitHandle.WaitOne(7000,true);

        if(!success){

        throw new ApplicationException();

    }

    else{

    Console.Log(LogType.SOCKET_SEND,str);

    }

    catch{

    Console.Log(LogType.SOCKET,"send message exceptionally");

    }

}

//请求成功

private void sendCallback(IAsyncResultasyncSend)

{

...

}


public void Closed(){

    try{

        if(socket!=null&&socket.Connected){

        //先停止接收和发送的服务

        socket.Shutdown(SocketShutdown.Both);

        //关闭socket资源

        socket.Close();

        if(disposable!=null){

        disposable.Dispose();

        }

        socket=null;

      }

    }

    //必须加上,客户端使用轮询处理断线重连时候有可能多次调用,异步导致报错使程序直接卡死

    catch{

    }

}

常见问题

优化改进

参考链接

百度

百度

微软官网

Socket/TCP粘包、多包和少包, 断包


第一次写技术文章,可能有些地方表达并不具体,请小伙伴们谅解。本人unity菜鸟一枚,如果由不懂的地方可以留言,我们可以一起分享心得。如果效果不错我会在闲暇之余继续修仙写文章,如果觉得还不错就顺手点个赞吧~


上一篇下一篇

猜你喜欢

热点阅读