在学习Unity的网络通信的时候,客户端和服务器端使用Socket通信。其中用到了不少Socket的方法。
如果没有可读取的数据,则Receive方法将一直处于阻止状态,直到数据可用,除非使用 Socket::ReceiveTimeout 设置了超时值。 如果超过超时值,Receive 调用将引发 SocketException。
再看看BeginReceive方法,该方法主要参数也有一个用于接受数据的byte数组。还有一个重要的参数是,一个AsyncCallBack委托。我们把它简单的理解为需要传入一个接受数据的回调函数。
我们调用BeginReceive的时候,系统会开启一个独立的线程(使用线程池),用以执行回调函数及对EndReceive的阻塞(block),直到EndReceive从socket的缓冲区中读到数据或者socket引发异常。
简单的理解为,如果在回调函数调用之前使用EndReceive方法,那么线程会被阻塞,直到读到了数据,或者发生错误。所以,最常用的写法是,在回调函数中使用EndReceive方法。EndReceive方法是必须要使用的。EndReiceive方法有一个返回值,代表接受数据的长度。
那么,回调函数何时会调用呢?文档中也没有详细说明。
我做了一些简单的测试。
服务端器端定义了一个很大的byte数组,作为接受数据的buffer。
我向服务器端发送消息,并将消息打印。
客户端向服务器端发送了三次消息,虽然都远远没有达到buffer的长度,但还是执行了回调。这说明了,如果缓冲区够大,接受到一次消息哪怕没填满缓冲区,也执行一次回调。
那么缓冲区如果太小呢。
并将收到的数据从buffer中取出和之前收到的数据,连在一起)直到收到的包体长度大于等于包体中指定的长度。
先调整服务器端接受数据的缓冲区。
我仅向服务器端发送了一次数据。
可以看到,因为缓冲区长度太小,执行了4次回调(并不是一个BeginReceive的回调函数会执行4次,而是因为我执行了4次BeginReiceive)。这说明了,当数据将缓冲区填满之后,会执行回调函数。
总的来说,可以抽象理解为:
BeginReceive方法时(开启新的线程),就用舀水勺去水池(socket接受数据的缓冲区)中舀水,没有水则等待。舀一次之后,就会调用回调函数(不管舀了多少),在回调函数中,(通过EndReceive,知道我们舀了多少水),我们自己决定对水勺中的水做什么(即对数据的处理),然后继续舀水(在回调函数中调用BeginReceive),若池中有数据,继续舀,没有就等待。其中,每次重新去舀水的时候,我们都是用的空的水勺,之前水勺中的水被清空了。
其中如有错误,欢迎指出。