使用socket_recv()的PHP websocket-我会收到部分帧吗?
我正在用PHP编写一个websocket服务器(使用使用socket_recv()的PHP websocket-我会收到部分帧吗?,php,sockets,websocket,Php,Sockets,Websocket,我正在用PHP编写一个websocket服务器(使用sockets扩展),我需要一些帮助来理解我需要在多大程度上处理零碎的消息 我对websocket信息如何传递的理解如下: 客户端应用程序向客户端API发送一条消息(任意长度) 客户端API将消息拆分为一个或多个帧(也是任意长度),并将其发送到网络层 网络层将数据分成若干个数据包,通过TCP通过网络发送 服务器接收TCP数据包(可能出现故障,但必要时会重新排序),并将其发送到正在相关端口上侦听的应用程序 应用程序调用socket\u recv(
sockets
扩展),我需要一些帮助来理解我需要在多大程度上处理零碎的消息
我对websocket信息如何传递的理解如下:
消息(任意长度)
消息
拆分为一个或多个帧
(也是任意长度),并将其发送到网络层数据包
,通过TCP通过网络发送数据包(可能出现故障,但必要时会重新排序),并将其发送到正在相关端口上侦听的应用程序
socket\u recv()
从套接字读取接收到的数据socket\u recv()
?读取websocket数据流时,我的应用程序将看到哪些数据
具体来说,我需要在多大程度上担心碎片
为了帮助解释我的问题,以下是上述流程的图解形式:
1。网络应用程序(消息):[消息1][消息2]
2.浏览器(框架):[Messag][e_1][Messag][e_2]
3.TCP发送(数据包):[Mess][ag][e_1][Mess][ag][e_2]
4.TCP recv(数据包):[ag][Mess][e_2][ag][Mess][e-1]
5.插座插座:???
如果我在一个循环中调用socket\u recv()
,直到它返回一个长度为零(每次添加到我的内部缓冲区),我是否保证得到一条完整的消息
socketrecv:[消息1]
socketrecv:[消息2]
还是一个完整的框架
socketrecv:[Messag]
socketrecv:[e_1]
socketrecv:[消息]
socketrecv:[e_2]
或者,它实际上是一个任意系列的数据包
,表示迄今为止接收到的任何数据(因此可能是部分帧
,甚至是多个帧
)
socketrecv:[Messag]
socketrecv:e_1][Mess
socketrecv:
socketrecv:ag
socketrecv:e_2]
还是别的什么
我很乐意将各种帧
数据拼接在一起,但如果我能假设每次轮询中接收到的数据的第一个字节(使用套接字_select()
)始终是帧
头,事情就会变得容易得多,在我们开始之前,不必将其作为原始字节流处理,而需要将其重新缝合到帧中。它在互联网上有完整的文档记录。。。TCP可靠且面向连接
您收到的信息完整且顺序正确-或从未收到。消息的每一段都必须由接收者确认,如果不确认,则再次发送该片段(几次…)。消息的重新组装由TCP堆栈完成,因此您不必担心应用程序中的数据包顺序或丢失数据包。。。您要么收到完整消息,要么出现错误
不要误解缓冲区。。。调用socket_recv()时,将提供一个缓冲区,但这与底层TCP堆栈使用的缓冲区不同
UDP是计数器部分,您必须注意所有细节。您得到的数据报可能顺序错误、多次出现、损坏/不完整或有其他缺陷。。。甚至从来没有!意思是:你可能会得到一个包含间隙的序列,你必须接受它 我非常擅长网络,在我的时代我已经写了很多扭曲的网络代码(Python中的网络套接字库)
我在家里有一本书“Unix网络编程第三版”,我瞥了一眼,看看它说了些什么。。。几年前我从图书馆买了这本书,因为据说它是TCP/IP协议栈及其规范的“权威”
来自第2章“传输层”
两台主机之间路径中最小的MTU
称为路径MTU。今天,以太网MTU是1500字节,通常是路径MTU。
...
将IP数据报发送到接口时,如果数据报的大小超过链路MTU,则由IPV4/IPV6堆栈执行分段。碎片在到达最终目的地之前通常不会重新组装。在IPv4上,主机和路由器都可以执行分段。在IPv6上,只有主机可以执行分段。
…
IPv4和IPv6定义了最小重组缓冲区大小,即保证任何实现都必须支持的最小数据报大小。对于IPv4,这是576字节
应用程序
任何应用程序或IPv4主机堆栈都保证在应用程序级别始终接收链路MTU
大小的数据报,即套接字\u recv
您的应用程序可能会收到较少的数据量,因为发送的数据可能较少,这就是为什么套接字服务器有办法知道消息何时结束,而新消息何时开始
典型的套接字服务器
ssize\u t numbytsrcvd=recv(clntSocket、缓冲区、BUFSIZE、0)
if(numbytsrcvd<0)//0表示流结束
出口(1);
在上面的代码段中,进程最多从操作系统接收BUFSIZE
字节。这并不意味着它不会收到更少的信息,或者连接的另一端没有发送更少的信息
整个关于堆栈较低级别发生的事情的讨论实际上对您的目的毫无意义
在PHP中调用socket\u recv
时,它也在做同样的事情,下面是源代码:
if((retval=recv(php_sock->bsd_socket,ZSTR_VAL(recv_buf),len,flags))<1){
zend_string_efree(recv_buf);
payload_length = frame[1] & 0x7f;
if (payload_length < 126)
{
hdr_length = 2;
payload_length = payload_length; // FYI / DUMMY
}
else if (payload_length == 126)
{
payload_length = (frame[2] << 8) + frame[3];
hdr_length = 4;
}
else
....