《Unity3D网络游戏实战》深入了解TCP

从TCP到铜线

应用层

应用层功能是应用程序(游戏程序)提供的功能。在给客户端发送"hello"的例子中,程序把"hello"转化成二进制流传递给传输层(传送给send方)​。操作系统会对二进制数据做一系列加工,使它适合于网络传输。

传输层

收到二进制数据后,传输层协议会对它做一系列加工,并提供数据流传送、可靠性校验、流量控制等功能。

网络层

IP协议会给TCP数据添加本地地址、目的地地址等信息

数据传输流程

TCP是一种面向连接的、可靠的、基于字节流的传输层通信协议,与TCP相对应的UDP协议是无连接的、不可靠的协议,但传输效率比TCP高。

TCP连接的建立

在TCP/IP协议中,TCP协议提供可靠的连接服务,连接是通过三次握手进行初始化的。三次握手的目的是同步连接双方的序列号和确认号并交换TCP窗口的大小信息

连接方调用Connect后,Client(连接方)向Server(监听方)发送一个数据包SYN , SYN包含了序列号seq,这是以后传送数据时要使用的。Server收到数据包后由标志位SYN知道Client请求建立连接,Server将SYN/ACK数据包发送给Client以确认连接请求。Clients收到SYN/ACK数据包后Connect返回,连接成功

TCP的数据传输

发送一个数据后,发送方并不能确保数据被对方接收。于是发送方会等待接收方的回应,如果太长时间没有收到回应,发送方会重新发送数据。发送数据时,TCP会考虑对方缓冲区的容量,当对方缓冲区满时,会暂停发送数据,防止对端溢出。TCP还会根据数据返回的时间判断网络是否拥堵,如果网络拥堵就减慢发送的速度,以求"道路畅通"​。

TCP连接终止

TCP通过"四次挥手"确保双端释放socket资源

  • 第一次挥手:主机1(可以是客户端也可以是服务端)向主机2发送一个终止信号(FIN),此时,主机1进入FIN_WAIT_1状态,它没有需要发送的数据,等待着主机2的回应。
  • 第二次挥手:主机2收到了主机1发送的终止信号(FIN),向主机1回应一个ACK。收到ACK的主机1进入FIN_WAIT_2状态。
  • 第三次挥手:在主机2把所有数据发送完毕后,主机2向主机1发送终止信号(FIN),请求关闭连接。
  • 第四次挥手:主机1收到主机2发送的终止信号(FIN),向主机2回应ACK。然后主机1进入TIME_WAIT状态(等待一段时间,以便处理主机2的重发数据)。主机2收到主机1的回应后,关闭连接。至此,TCP的四次挥手便完成了,主机1和主机2都关闭了连接

常用TCP参数

ReceiveBufferSize

ReceiveBufferSize指定了操作系统读缓冲区的大小,默认值是8192,可以通过 socket. ReceiveBufferSize = 8 这样来指定缓冲区的长度

SendBufferSize

SendBufferSize指定了操作系统写缓冲区的大小,默认值也是8192

NoDelay

指定发送数据时是否使用Nagle算法,对于实时性要求高的游戏,该值需要设置成false。Nagle是一种节省网络流量的机制,默认情况下,TCP会使用Nagle算法去发送数据。

TTL

TTL指发送的IP数据包的生存时间值(Time To Live, TTL )​。TTL是IP头部的一个值,该值表示一个IP数据报能够经过的最大的路由器跳数。发送数据时,TTL默认为64

在网络游戏中,如果某些偏远地区用户时不时无法接收数据,可以尝试增大TTL值(socket.ttl=xxx)来解决问题。

ReuseAddress

ReuseAddress即端口复用,让同一个端口可被多个socket使用。一般情况下,一个端口只能由一个进程独占,假设服务端程序都绑定了1234端口,若开启两个服务端程序,虽然,第一个开启的程序能够成功绑定端口并监听,但第二个程序会提示"端口已经在使用中"​,无法绑定端口。

当服务端程序崩溃,但它持有的Socket不会被立马释放,这时候重启服务器就会遇到"端口已经在使用中"的情形。等到Socket被释放后(这个过程可能要十几分钟时间)​,服务端才能成功重启。

设置端口复用使用socket的SetSocketOption方法,代码如下所示。

cs 复制代码
    Socket  socket=  new  Socket(AddressFamily.InterNetwork,  SocketType.Stream,
ProtocolType.Tcp);
    socket.SetSocketOption(SocketOptionLevel.Socket,  SocketOptionName.ReuseAddress,
true);

LingerState

LingerState的功能是设置套接字保持连接的时间

客户端调用Close()关闭Socket连接(客户端或服务端关闭连接都是同样的流程,服务端主动关闭连接同理)​,这时,客户端会给服务端发送FIN信号(①) ​,然后进入等待。当服务端收到FIN信号时,会返回一个长度为0 的数据,然后向客户端回应**信息(②)**​。这也是为什么关闭连接时,对端Receive会收到0个数据。如果服务端不做处理,客户端将会持续等待。

服务端中,会使用下面的代码处理客户端主动关闭连接,即在收到长度为0的消息后,调用**clientfd.Close()**关闭连接。

cs 复制代码
    public static void ReceiveCallback(IAsyncResult ar){
        ......
        int count = clientfd.EndReceive(ar);
        //客户端关闭
        if(count == 0){
            clientfd.Close();
            ......
            return;
        }
        ......
    }

服务端在调用Close后,它向客户端发送FIN信号(③) ​,然后等待客户端回应。当服务端收到客户端的回应信息时,它会释放socket资源,真正完成关闭连接的流程。对客户端来说,它在收到服务端的FIN(③)信号后,会进入一个称为TIME_WAIT的状态,等待一段时间后(Windows下默认为4分钟)​,才会释放socket资源,真正完成关闭连接的流程。TIME_WAIT状态的意义在于,如果网络状况不好,服务端迟迟没有收到客户端回应的信号(④)​,那它会重发FIN信号(③)​,客户端socket需要维持一段时间,以回应重发的信号,确保对方有很大概率能够收到回应信号(④)​。

这种机制可以让服务端在关闭连接前处理尚未完成的事情,例如,假设收到客户端FIN信号时,服务端socket处于图片显示的状态,即发送缓冲区还有尚未发送的数据,那么直接调用Close关闭连接,缓冲区中的数据将被丢弃。这种关闭方式很暴力,因为对端可能还需要这些数据

cs 复制代码
    socket.LingerState = new LingerOption (true, 10);

其中的LingerOption带有两个参数。第一个参数是LingerState.Enabled ,代表是否启用LingerState,只有设置为true才能生效。第二个参数是LingerState.LingerTime ,指定超时时间。如果超时时间大于0(比如10秒)​,操作系统会尝试发送缓冲区中的数据,但如果网络状况不好,超过10秒还没有发完,它还是会强制关闭连接。如果LingerState.LingerTime设置为0 ,系统会一直等到数据发完才关闭连接,无论等待多长时间。开启LingerOption能够在一定程度上保证发送数据的完整性

心跳机制

TCP有一个连接检测机制,就是如果在指定的时间内没有数据传送,会给对端发送一个信号(通过SetSocketOption的KeepAlive选项开启)​。对端如果收到这个信号,回送一个TCP的信号,确认已经收到,这样就知道此连接通畅。如果一段时间没有收到对方的响应,会进行重试,重试几次后,会认为网络不通,关闭socket。

cs 复制代码
    Socket.SetSocketOption(SocketOptionLevel.Socket,  SocketOptionName.KeepAlive,
true)
相关推荐
hellojackjiang20111 小时前
即时通讯框架MobileIMSDK的H5端开发快速入门
网络·即时通讯·im开发
有时间要学习1 小时前
Linux——应用层自定义协议与序列化
linux·服务器·网络
Tony聊跨境1 小时前
什么是 SSL 代理?
网络·网络协议·ssl
我叫啥都行2 小时前
计算机基础知识复习9.7
运维·服务器·网络·笔记·后端
Monodye2 小时前
【Java】网络编程:TCP_IP协议详解(IP协议数据报文及如何解决IPv4不够的状况)
java·网络·数据结构·算法·系统架构
汀、人工智能3 小时前
报错error: RPC failed,curl 16 Error in the HTTP2 framing layer解决方法
网络·git·网络协议·rpc
qq 1778036223 小时前
智能新时代,游戏盾守护顺畅体验
运维·服务器·网络·游戏·云计算·ddos·ip
2401_847056554 小时前
Altium Designer脚本工具定制
网络·数据库
就这个java爽!5 小时前
JAVA网络编程【基于TCP和UDP协议】超详细!!!
java·开发语言·网络·tcp/ip·udp·eclipse·idea
KookeeyLena75 小时前
动态IP与静态IP:哪种更适合用户使用?
网络·网络协议·tcp/ip