先声明用于网络练接的Socket,以及用于接收网络数据的缓存区
//用于网络连接的Socket
private Socket _socket;
//缓冲区大小
private const int buffSize = 1024*1024;
//用户存放接收到的网络数据
private readonly byte[] packBuffer = new byte[buffSize];
连接方法。
iport 是服务器的ip与端口
/// <summary>
/// 连接
/// </summary>
public void Connection(IPEndPoint iport)
{
_socket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
try
{
//连接服务器
_socket.Connect(iport);
//开始接收数据
_socket.BeginReceive(packBuffer, 0, buffSize, SocketFlags.None, OnReceived, _socket);
}
catch (Exception e)
{
Debug.LogError("链接失败!" + e.ToString());
_socket = null;
}
finally
{
}
}
接收网络数据,如果接收到网络数据将会自动调用 OnReceived方法。
OnRecived方法不是主线程执行,而是子线程执行的,所以要注意变量的使用
/// <summary>
/// 接收到数据
/// </summary>
/// <param name="ia"></param>
public void OnReceived(IAsyncResult ia)
{
//接收到的数据长度
int len = 0;
try
{
len = _socket.EndReceive(ia);
if (len <= 0) //如果接收到的长度小于等于0
{
DisConnection(true); //主动断开连接
Debug.LogWarning("网络中断!");
return;
}
//packStrBuffer 是接收缓冲区StringBuilder对象。
//把接收到的数据拷贝到 packStrBuffer 中等待处理
packStrBuffer.Append(Encoding.UTF8.GetString(packBuffer, 0, len));
string message = packStrBuffer.ToString();
//注意一次OnRecived的调用并不意味着接收到一个网络数据包。
//1.可能是两个网络数据包 粘包现象
//2.可能是一个完整的包
//3.可能是一个包的一部分。
//一般来说会在每个包的前面描述出包的数据大小,用于分割包裹。
//此处我用换行符来分割每一个包 newline = ' \r\n'; 如果你跟我一样设计的话,那么数据包中不能出现换行符,否则将会导致包解析错误。
//找到换行符位置
int pidx = message.IndexOf(newline);
while (pidx >= 0) //有换行符,表明接收到一个完整的包
{
//对数据进行分割出一个完整的包裹也就是 packMessage;
//应把 packMessage 放入队列中留待处理,此处就不作处理了
//此处不宜直接把包裹拿来解析,因为此处是子线程,应缓存到队列中,主线程获取后再作处理。
string packMessage = message.Substring(0, pidx);
//把数据从缓冲区中移除,方便下次包数据解析
packStrBuffer.Remove(0, pidx + newline.Length);
message = packStrBuffer.ToString();
pidx = message.IndexOf(newline);
}
//包数据处理完成后,继续去接收数据
_socket.BeginReceive(packBuffer, 0, buffSize, SocketFlags.None, OnReceived, _socket);
}
catch (Exception e)
{
Debug.LogError("网络连接中断:" + e.Message);
}
finally
{
}
}
主动断开连接
public void DisConnect(){
if(_socket == null) return;
_socket.Disconnect(true);
_socket.Close();
_socket = null;
}
发送数据
///<summary>
/// 发包
/// </summary>
/// <param name="message"></param>
private void SendPack(string message)
{
try
{
//此处增加newline 是因为我的设计是以换行符来进行包裹分割
_socket.Send(Encoding.UTF8.GetBytes(message + newline));
}
catch (Exception e)
{
Debug.LogError("网络已中断!");
}
}
以上是Unity Socket TCP 网络链接的简单实现。
应用在游戏中还需要考虑到
1.自动重连的问题。
2.网络中断检测问题,游戏时候客户端网络中断,服务端是无法知道客户端的断开。所以会加入心跳包的设计,客户端每隔一段时间就向服务器发送一个心跳包,服务器如果有一段时间未接收到客户端发送过来的心跳包,则认为客户端连接中断。关闭与客户端的连接。
3.数据量的问题,此处base64的设计并不好,因为进行base64编码后,数据量会增大,并且多一层转换,效率也有所损耗。在进行收发时,一般需要对发送的数据进行压缩处理,接收到数据包后再进行解压处理,这样可以减少服务器带宽,提升网络传输速率。