/首页
/开源
/关于
PHP网络编程-socket基础篇(八)
发表@2020-01-04 21:02:00
更新@2023-09-24 09:31:08
大家好,我还是那个文风浮夸词藻华丽、内容正规内涵犀利、写出一篇文章往那里一放,就能吸引极少数泥腿子的老李。 在我们历尽铅华去伪存真孜孜不倦连续七个章节连拼带凑说完关于PHP与进程操控的基础章节后,我们终于可以进入到网络socket阶段了。 正当我准备开始憋正文如何开篇的时候,住在我隔壁的中年男人又开始陷入无限痛苦之中并在失控中开始暴躁吼叫。第一次听到他吼的时候还是上次老赵来我这里的时候,当时就听到嗷的一嗓子老赵筷子上夹着的猪肉吓得一哆嗦自己就掉进盘子里了。TA们是一对被辅导小孩儿写作业而逼疯的夫妇。一般流程是这样shai儿的,他媳妇先上阵辅导一波儿,但是一般在5-10分钟内情绪就会立马失控并冲出跑道,然后他媳妇退下后他立马替补上去,大约在10-20分钟后这老哥也开始逐渐失控,此时小孩儿也会失控并发出类似于大马猴子那种“ 细长而又高频 ”的哭声,到这会儿如果不出意外的话,这老哥就会打开窗子抽烟(我们都是朝南大主卧,即便是冬季,阳光足的时候不开窗子通风的话,整个屋子热气袭人)。这才刚过才完年第三天,你这就又开始吼孩子了,辅导个作业整的全家鬼哭狼嚎,小孩子委屈地坐在桌子旁不断啜泣,暴躁老哥一脸怒气然后站在窗子这儿“ baba ”地抽闷烟,有一有二无再三再四,这次实在是连我都看不过眼了,于是走到我这儿窗户旁边朝隔壁方向说:“ 先生您好,请您不要在室内抽烟,吸烟有害健康 ”。 在被回复了一声“ 玩蛋去 ”后,我将不得不在隔壁的狂风暴雨声中完成我们新征程的第一个辉煌篇章。 如果不出意外,这个周日或下周一的早上,还会有一波儿怒吼,下篇文章时候我录音让你们感受一下。 众所周知(依然是大概不超过十个人),我是一个相对来说比较务实的人,这个[ 相对 ]是比永强那种[ 能坐公交就一定不会坐地铁 ]的务实还是稍微有些不一样的。比如说我们写了七个章节的PHP与进程以及WM进程源码分析,那么我就得一定得山寨模仿一个出来,所以我已经写好了一个已经实现了start stop以及reload的Core文件,github地址在下面:https://github.com/elarity/Yaw 其实这里说到务实,我还是忍不住想多聊聊务实,比如柱子,也很务实,凡事都是[ 不见兔子不撒鹰、见了兔子也尽量不撒鹰 ]的那种。老赵在微信群里发红包,如果柱子看到了这个红包他一定会去拼一波儿手气;如果大家接着让柱子发红包,他就会变成一个类似于[ 你们下次发的红包我不抢了,就当我给你们发红包了 ]这样的逻辑鬼才。我认为这也是务实的一种友好体现。 都已经快一千字了,我们还在鬼扯,我自己都看不过眼了。从这行开始我们聊聊关于PHP操控socket的内容,socket翻译过来叫做套接字,这里先简单说下[ socket与TCP、UDP ]的关系。首先要意识到一点,那就是无论是活在理论中的七层网络模型还是现实中一统浆糊的四层模型,socket都不是其中的一层,socket与网络模型没啥关系: - 在现实中的四层网络模型中,从下往上数到第三层便是传输层 - 在理论中的七层网络模型中,从下往上数到第四层便是传输层 传输层中其实就是TCP和UDP两个协议所在地,而socket按我个人理解,就是对TCP、UDP的上层“ 操作封装 ”,但socket不是独立的一层。 在PHP里,操作socket有两大系函数,一系是stream前缀的系列,另一系是socket前缀的系列,我截个图你们感受一下:   stream系其实就是PHP中流的概念,因为一般说来诸位腿子们用PHP都在搞Web堆网页所以对流接触的并不多,流对各种协议都做了一层抽象封装,比如[ http:// ]、[ file:// ]、[ ftp:// ]、[ php://input ]等等,也就说流系列函数提供了统一的函数来处理各种各样的花式协议。在Workerman里,就是使用stream系函数实现的tcp、udp等服务器。 socket系应该是就PHP直接对底层socket API的一层粗暴封装,说白了就是把C语言版本的socket()、accpet()、listen()拿过来很粗暴的处理一下就算能用了,这里要补充的是之前我在写advance-php系列的时候是妄加猜测的主观认为socket系函数对直接对socket API的封装,这次我决定验证一下,主要是怕被打脸:   我们这儿呢,为了保证自主产权独立性,就和Workerman劈开一下吧:我们将会选用socket系函数进行基础解析并实现。 So, Let‘s ROCK~ 其实socket用我同事的话说就是[ 非常简单 ]的(用TA话说就是天天socket socket,就那点儿东西),你就记住了一个流程四个步骤:create -> bind -> listen -> accept 先摆一个非常粗暴的案例,你们先感受下,请留意代码里的注释: ```php 0 ) { $recv_content = $recv_content.$content; } } echo "从客户端获取:{$recv_content}"; // 向客户端发送一个helloworld $msg = "helloworld\r\n"; socket_write( $connection_socket, $msg, strlen( $msg ) ); socket_close( $connection_socket ); } socket_close( $listen_socket ); ``` 那么MSG_OOB和MSG_PEEK是什么意思?MSG_OOB其实叫做外带数据,有时候有些地方叫做紧急数据。我举个例子,由于TCP数据有些时候会被分拆成好几个数据坨(假如说3个),然后服务器方收到数据后需要按照序号去排列好手这坨数据坨,如果此时你有个紧急的数据期望能够送到服务器,那么发送方只需要在send数据数据时加上一个MSG_OOB的标记,服务器就会优先接受这个数据,不用等候服务器按顺序收好那3个普通数据坨坨。不过,我是没怎么用过,只知道TA的用法和存在含义。MSG_PEEK则有这样一个作用,假设你有一坨数据" lalala-password ",如果服务器在recv()接受数据时发现数据包中带有MSG_PEEK标记,那么TA会先读取完毕lalala遇到-停止(这个-是你规定自定义的),虽然你读取数据了,但是这坨数据不会从TCP接受缓冲区被清除掉,TA还会留在那里,等你下次再次使用recv()接受,TA就会接着从-位置读取剩下的“ password ”。这个功能主要用于预探测功能,我意思是先读取一次数据,可以从第一次读取的数据(本次数据中可以包含关于剩余那坨数据的主要信息),然后根据本次数据的信息来让程序做决定下次recv()是执行还是不执行,如果执行了,是否需要走什么其他特殊逻辑。 已经快五千字了,我知道你们快顶不住了,然而我要告诉你们:这些还是经过了PHP处理过后的socket API,我给你们看下UNP中最原始API的。 PHP的socket_recv()选项有如下四个项,且每项之间均可以使用|(或运算)来搭配使用同时获得多个特性: - MSG_OOB - MSG_PEEK - MSG_WAITALL - MSG_DONTWAIT 而recv()则拥有五个配置项: - MSG_OOB - MSG_PEEK - MSG_WAITALL - MSG_DONTWAIT - MSG_DONTROUTE 而当你使用recvmsg()的时候,配置项则达到了十一项...如果再结合上TCP/IP,如果没有毅力一般人怕是坚持不下来的,而且觉得不就一个[ socket ]不就一个[ create-bind-listen-accpet ]的佬们,似乎应该好好正视一下。 所以,知足吧,今天先说到这里,明天接着继续。