本篇附带源代码,带有处理拆包分包粘包,也会撰明具体内容。
首先对于tcp就是挥手机制,三次握手四次挥手机制。
一、三次握手
具体过程为简单解释为:
1、客户端请求服务器链接,等待服务器确认。(服务器如果确认请求,就会给这个客户端随机分配一个固定且唯一的ID号码)
2、服务器收到请求后,会向客户端发送应答,进行确认。(服务器会返回给你分配的id编码,代表下一次服务器希望收到以此为地址的固定的客户端序号,如果未来涉及断线重连这里也是非常重要的信息,要进行留存)
3、客户端收到信息后,会再次向服务器进行回复,发出确认。(就此服务器确认链接被确认,客户端服务器建立起链接)
【啰嗦一下:】
【握手为什么是三次?(防止已过期的链接请求报文突然又传输到服务器从而产生错误)、(三次才能确认双方接收和发送能力都正常)、(告知对方自己的初始序号值,并确认双方的序号值是否正确)以及(节省服务器性能【如果长时间服务器分配出id等待联接,会造成资源上的浪费,同时也可能被不法之人利用危害数据安全】)
为啥是三次不是四次?
已经可以确认链接了,无需第四次握手,浪费资源
更多可访问:详解 TCP 三次握手、四次挥手,附带精美图解和超高频面试题 - HYN的技术笔记 - SegmentFault 思否
】
二、四次挥手
即终止链接,这与tcp的机制有关,详细可看上面的链接。这里我们只做简略描述。
1、客户端发送关闭请求。
2、服务端收到后发回确认请求。
3、这个阶段下,服务器会发送最后的一些数据信息【这里面会有一些你没发完的一些数据信息,包括还未处理的内容信息】,最后会带一个结束的标识,代表服务器将这些没发完的内容发完了。
4、此时该服务器对此客户端的服务已经基本结束了,此时会发送最后的关闭信息。
5、客户端收到确认信息,再次发送确认信息,确认关闭,此时客户端自己就被关闭了,服务器在接受到指令后也就被关闭了。
这是基础内容,然后需要知道的是虽然tcp不会丢包,但是会有沾包的问题。
这个导致的问题有很多,可能是发送方造成的,也可能是接收方造成的。
1、发送方的tcp为提高传输效率,要收集到一定的数据后才可以发送,这就可能导致接受到原本是一大段的内容信息,可能是一段一段的。
2、mss规定,如果数据包过长就会被芬开传输,这样接收方就受到了粘包数据
3、接收方接收数据不及时,卡顿造成的
4、网络传输制式不统一【跨国业务中能遇到,甚至无法链接】
因此值得注意的是一般在unity开发过程中,并不太会使用tcp,一般在小游戏中往往会比较能够见到,相对来说kcp会更多,其次是http web【UnityWebRequest】(udp:往往信息传输的都是请求更新,更新完后的东西都会在本地处理一锤子买卖,干完就不认人【tps:有些境外赌博产品就是这么干的,id号就在本地保存,抓包一抓一个准】)
先上源代码吧,解析放在后面:
客户端 unity ,c#的也可根据这个改改就能用: 下面这个是核心部分
TcpClient
using System;
using System.Collections;
using System.Collections.Generic;
using System.Net.Sockets;
using System.Text;
using UnityEngine;public class Client
{static Client instance=new Client();public static Client Instance => instance;private TcpClient _client;public void Start(){_client = new TcpClient();Connect();}public async void Connect()//连接{try{//后端给的ip和服务器端口(这里暂时写的是本机ip)await _client.ConnectAsync("127.0.0.1",7788);Debug.Log("连接成功!");Receive();//连接}catch (System.Exception e){Console.WriteLine(e.Message);//打印错误}}public async void Send(byte[] data)//发送消息{try{await _client.GetStream().WriteAsync(data,0,data.Length);Debug.Log("发送成功!");}catch (Exception e){Debug.Log(e.Message);_client.Close();}}byte[] data = new byte[4096];int msgLenth = 0;//记录长度public async void Receive()//接收数据{try{while (_client.Connected){byte[] buff = new byte[4096];int lengh=await _client.GetStream().ReadAsync(buff,0,buff.Length);if (lengh>0){Debug.Log($"接收到数据了:{lengh}");MassengerHelper.Instance.CopyToData(buff,lengh);Debug.Log(Encoding.UTF8.GetString(buff,0,lengh));Array.Copy(buff, 0, data, msgLenth, lengh);// JsonHelper.ToObject<JsonHelper.JsonTest>(buff);msgLenth += lengh;Handle();}else{_client.Close();}}}catch (Exception e){Debug.Log(e.Message);_client.Close();}}private void Handle(){//注意:这里只是举个例子,一般不会这么小,空间大小一般遵循二进制准则为1024//然后在这里我们对消息体进行封装,减少了传输,即:客户端和服务端都拿了一个密码本//双方约定传输内容,比如1,为登录,2为退出。。。//包体大小(4) 协议ID(4) 包体(byte[])if (msgLenth >= 8){byte[] _size = new byte[4];Array.Copy(data, 0, _size, 0, 4);int size = BitConverter.ToInt32(_size, 0);//本次要拿的长度var _length = 8 + size;if (msgLenth >= _length){//拿出idbyte[] _id = new byte[4];Array.Copy(data, 4, _id, 0, 4);int id = BitConverter.ToInt32(_id, 0);//包体byte[] body = new byte[size];Array.Copy(data, 8, body, 0, size);if (msgLenth > _length){for (int i = 0; i < msgLenth - _length; i++){data[i] = data[_length + i];}}msgLenth -= _length;Debug.Log($"收到客户端请求:{id}");}}}}
然后这里有个关联代码:
public class MassengerHelper
{private static MassengerHelper instance=new MassengerHelper();public static MassengerHelper Instance => instance;byte[] data = new byte[4096];int msgLenth = 0;public void CopyToData(byte[] buffer,int length){Array.Copy(buffer,0,data,msgLenth,length);msgLenth += length;Handle();}private void Handle(){//包体大小(4bit)协议(4)包体(byte[])if (msgLenth>=8) {byte[] _size = new byte[4];Array.Copy(data,0,_size,0,4);int size=BitConverter.ToInt32(_size,0);//获知包体具体大小//本次要拿的长度var _length = 8 + size;if (msgLenth>=_length) {//拿出idbyte[] _id = new byte[4];Array.Copy(data,4,_size,0,4);int id = BitConverter.ToInt32(_id,0);//包体byte[] body = new byte[size];Array.Copy(data,8,body,0,size);if (msgLenth>_length) {for (int i = 0; i < msgLenth-_length; i++){data[i] = data[_length];}}msgLenth -= _length;Debug.Log($"收到客户请求:{id}");//添加监听MessanagerCenter.Instance.Dispatch(id, body);}}}public void SendToSever(int id,string str){Debug.Log("ID"+id);var body = Encoding.UTF8.GetBytes(str);byte[] send_buff = new byte[body.Length+8];int size = body.Length;var _size = BitConverter.GetBytes(size);var _id = BitConverter.GetBytes(id);Array.Copy(_size,0,send_buff,0,4);Array.Copy(_id,0,send_buff,4,4);Array.Copy(body,0,send_buff,8,body.Length);Client.Instance.Send(send_buff);}
}
具体详细等我哪天有时间再更新吧,如果有错误欢迎大佬提出意见