小弟正在做一个flash为客户端,php为服务端的socket小程序现在出现一个问题flash成功连接服务端后!一切都能正常通信但是不能多用户连接服务端(比如我打开两个flash,第二就会连接不上)求助php socket是否能解决多用户连接的问题
望高手相助

解决方案 »

  1.   

    你这个 PHP 程序,是普通的处理响应 HTTP 请求吗?还是真的是个 socket server 呢?如果是后者的话,你的 PHP 程序是如何被启动的呢?你要先把这些说清楚,下面才好讨论。
      

  2.   

    是要真的socket响应
    flash一直跟php保持着连接状态
      

  3.   

    php
    可以用
    set_time_limit(0);
    ignore_user_abort();
    进行永远执行但是还是用java写的好
    可我不会java
      

  4.   

    你只回答了我两个问题中的第一个。第二个问题,你的 PHP 是如何启动的呢?如果是手工预先发一个 HTTP 请求来启动的,那么就需要在 PHP 里用一个固定端口接收并处理多个 socket 连接请求,这个需要一定的技巧。如果是由 FLASH 发送这个 HTTP 请求来启动的,那就可以在 PHP 里使用不同的端口来接收 socket 连接。无论如何,这种方式运行的 PHP 程序,不适合做 socket server。
      

  5.   

    好建议用flash来启动socket服务端但是我不好掌握启动后运行时间(如果永久运行,服务器的开销会很大,直至最后down掉)
    还有当有新用户进入的时候,我需要主动发送信息给其他之前登录的用户,却不太好实现了回答你之前的问题
    php启动是在进入游戏之前先用php连接一下服务端如果不能连接就请求一下(我之前是这样想的)
      

  6.   

    的确,每个 FLASH 自己请求启动一个 PHP 来做 socket server,就只能为这个 FLASH 单独服务,难以实现多个会话的互通。不了解你的实际应用背景,所以不清楚你为什么一定要用 socket,如果你的通信方式能回归到普通的 HTTP 的话,应该是最简单的。一定要用 socket 的话,建议你还是考虑换一种方式吧。不是说用 java 就好,即使用 java,如果是 web 方式部署的话,问题是一样的。PHP 也可以用 CLI 的方式来做。
      

  7.   

    通过HTTP连接, 就是通过IIS,APACHE, 他门有很好的处理多用户的本事,所以在可能的情况下都是用WEBSERVICE,也即是用用IIS,APACHE,所以网上的教材说到FLASH都是说用WEBSERVICE,否则自己搞个SERVER处理得不好就会有倒塞, 
      

  8.   

    主要就是做一个flash游戏,基于web,能够几个人在一个房间内玩要能够即时通知各个用户其他用户的情况,所以必须用socket才能达到即时通信的目的CLI还没有用过,能给我一个例子吗?
    我想知道他是如何工作的,能干什么
      

  9.   

    你这个应用场景,关键就是要“即时通知”,是吧?用 socket 当然能达成目的,但要实现起来并不容易,至少在 web 容器里会比较费劲。其实用 HTTP 长连接也能达到同样的效果,比如你访问开心网,总会有一个未完成的 HTTP 请求,最多 30 秒它就会结束,但马上会再发起一个请求,这就可以实现“即时通知”。相关资料可以搜索“comet 长连接”。(请注意,上面说的 30 秒,不是说每个通知要等 30 秒。如果没有任何通知,则超过 30 秒会改用一个新的连接;但如果有通知的话,会立刻返回)CLI 只是 PHP 程序的另外一种启动运行方式,相当于在服务器上直接运行一个 exe,只不过这个 exe 是用 PHP 编写的。因为没有 web 容器的限制,对于多线程、网络通信等功能使用起来比较自由。
      

  10.   

    怎么还有ignore_user_abort(),php socket server要在命令行下执行,也就是上面说的cli,而不是设为为一个http响应端.
    http://devzone.zend.com/article/1086
      

  11.   

    谢谢maquanphp编写.exe后缀的可执行文件吗?还是说文件也是.php只是启动发式变了而已?http://devzone.zend.com/article/1086上面的写法和手册的socket_select差不多我再尝试下谢谢大家的帮助!小弟学到了很多以前不懂的知识和一些解决方案!再次感谢
      

  12.   

    我用cli方式运行就不会有线程限制了?
    是不是等于我以前写的代码直接运行就可以了?
      

  13.   

    非常感谢大家,已经可以达到多人在线了感觉即时性也非常的好谢谢maquan和BooJS结贴后还能继续聊天吗?还想跟你们聊聊呢?
      

  14.   

    结贴不着急,只要是跟这个话题有关的,尽管继续聊,hehe不过如果是其它问题,最好另外开贴,这样也方便大家阅读。CSDN 是公共场所嘛 :)
      

  15.   

    昨天上传到空间发现用cli运行有限制30秒便超时了!那么我们就必须要写一个守护他的程序当发现超时的时候重新用cli方式运行一次那么用try catch是否能监控到超时的时候运行catch里面的东西呢?
    try{
        我的socket代码
    }
    catch(){
        再次重新请求
    }catch括号里面我该如何写呢?
      

  16.   

    对空间不了解,不知道他们有什么管理限制。PHP 自己的运行时间限制可以通过 set_time_limit() 来设定,缺省就是 30s。用守护程序重新加载的方式,未必能很好地解决你的问题,它会让你的服务端表现很不稳定,客户端开发难度加大。如果你是租用空间的话,超常规的程序一定会有诸多限制,强烈建议你仔细了解一下长连接。
      

  17.   

    通过一天的了解大概了解了什么是comet长连接了!主要就在php端写上
    flush();
    刷新缓冲区,让前台的ajax接收到信息同时php端并没有停止工作当30秒超时的时候,客户端再重新连接一次不知道我的理解是否正确?
      

  18.   

    你理解得没错,大体上就是这个意思。不过你可能太关注实现的细节了。概念上讲,所谓长连接,就是服务器端收到 HTTP 请求后,并不立刻完成它,而是保持一段时间(比如 30 秒),这期间如果有需要即时送达的通知,则立刻输出响应内容并关闭连接。如果平安无事,到时间也要返回(最好不要等到被容器杀掉),这样可以给客户端一个完整的响应,便于客户端继续下面的工作。实现长连接的一个难点在于等待过程中如何获知“需要即时送达的通知”,尤其是在租用空间的 web 容器中,实现起来会很受限制,简单的轮询和延时肯定效率非常差,最好是某种阻塞方式的 IO。
    ————————————————————————————————
    基于CSDN论坛提供的插件扩展功能,自己做了个签名档工具,分享给大家,欢迎技术交流 :)
      

  19.   

    确实在等待过程中,如何知道有新内容需要返回是一个比较麻烦的事情
    做的不好会影响即时性我是这样想的,如果有内容需要返回的时候是读取由另一个页面创建的xml或者php数组之类的(即读取写文件),当连接发起时,记录一下文件当前的修改时间,然后当修改时间发生变化的时候就返回文件里的内容。
    不知道这样会不会出现一些问题,比如修改和连接同时进行(我连接的前一秒,文件被修改了,这样就只有等下次文件修改的时候才会返回信息,出现这样情况的几率不知道有多大)你指的IO操作我不太明白
      

  20.   

    我写过一个socket的小例子。在我的博客里面,你可以去看看。
    http://blog.csdn.net/a82168506/article/details/6307461
      

  21.   

    谢谢楼上这位兄弟,socket的我也写了代码和你的差不多问题就是空间强制设定了30秒超时(set_time_limit(0)没有用),所以没有办法才想看看长连接能不能解决!
      

  22.   

    学习了一下IO
    发现我的这种长连接貌似是阻塞IO吧!如果想做到是非阻塞IO,技巧在哪呢?
      

  23.   

    经过测试发现ajax发起的长连接是非阻塞IO只是浏览器对长连接有限制,当相同浏览起发送两个长连接的时候,有一个是无法连接的!如果实现有新消息立即通知客户我想了如下方法
    还是用读文件的方法
    首先页面打开的时候,由js发起连接同时传送所要读的文件当前的修改时间到达php页面后当发现修改时间和传送过来的时间不一致的时候通知客户端有新消息
    php页面运行到25秒的时候直接返回一个文件的最后修改时间给js,此次长连接结束js接收到php传回的文件修改时间后重新发起连接并传送php传回的文件修改时间我相信这样就不会漏掉需要通知客户端的信息了吧以上是我个人的一个想法请maquan和大家再帮帮我,分享一下你们的处理方法谢谢
      

  24.   

    两个动作之间php页面sleep(1)(返回新消息后连接并没有结束),然后接着检查文件修改时间,直至运行时间大于25秒才结束连接,再由js发起新连接这样应该没有问题吧如果是你,你怎么做呢?如何判断有新的消息需要返回?
      

  25.   

    等偶有时间了,也来弄下flash
      

  26.   

    首先,“返回新消息后连接并没有结束”这个恐怕是有问题的,只有结束掉连接,客户端才会认为“已经完整接受到了一个 HTTP Response”,才会进一步处理。如果不结束连接的话,即使你做了 flush 等等,最多也只是把数据流传到客户端,但客户端并没有完成接收、开始处理,也就是说你这个通知还是没有“即时”。至于“检查是否有通知”的方法,你所用的应该属于“轮询+延迟”的方法,一般来说可以用,但“即时性”和“运行效率”稍差一点点。我总觉得“阻塞 IO”的方式也许会好一点,你可以参考一下 socket_recvfrom(),缺点是资源占用多了一点。租用的空间嘛,没办法,受制于 web 容器。如果是自己的独立服务器,搞个 deamon,用多路异步 IO,想咋玩就咋玩,hehe
    ————————————————————————————————
    基于CSDN论坛提供的插件扩展功能,自己做了个签名档工具,分享给大家,欢迎技术交流 :)
      

  27.   

    http://www.zeitoun.net/articles/comet_and_php/start
    我查找到这个例子他使用了一个js库就可以实现了长连接且内容能正常返回,连接不终止!大家看看这个库我不太熟悉,有熟悉的朋友吗?这就是真正意义上的comet吗?
      

  28.   

    多方查找资料
    发现能参考的很少我现有的水平可能只能这样写了js发起长连接php页面如果有新信息立即返回给js并且结束该连接,JS收到新信息后(进行处理),马上发起新的连接(数据处理之前)如果超过25秒都没有新信息,php结束该连接,js同样再次发起新连接!用socket_recvfrom()我又必须用cli去运行一个服务端了吧,这样的就是阻塞IO了?
    貌似JS也是可以当作socket客户端连接服务端的,如果多个客户端同时去连接,出现了阻塞,不知道客户端的发起的连接会超时吗?会不会在socket服务端断开前一连接时自动连接上呢?理论上讲如果客户端多的情况下,实时性恐怕也无法保证了
      

  29.   

    上面我好像理解错误了,找到了一个最通俗的解释比如进程A要从网络收一个包,如果没有包就等待,这就是阻塞。
    不等待,去做别的事情,等包来了再处理就是不阻塞。要是按这样来看其实是一个阻塞IO,当没有新信息的时候js是一直处理等待状态,直到有新的信息为止
      

  30.   

    在一个通信的两段,读操作和写操作,它们分别使用的是“阻塞”还是“非阻塞”,是各说各的,互不影响。比如你的服务端代码:等待通知如果采用 socket_recvfrom(),那就是“阻塞”的,而输出 HTTP Response 一般来说都是“阻塞”的。而在 js 这边接收 HTTP Response,既然你用了 AJAX,那就一定是“非阻塞”的,这正是 AJAX 的价值所在,否则页面就卡死变白屏了。
    不是这样的。负责长连接的那个 PHP 用 socket_recvfrom() 等待通知数据,处于“阻塞状态”;而产生通知数据的一定是你另外一个常规的 PHP 程序,比如“某用户登录”,由它负责 socket_sendto()。
      

  31.   

    需要担心的不是“实时性”,前面描述的通信模型如果能成功的话,实时性总是能保证的(当然还取决于网络速度)。问题在于当客户端数量多的情况下,资源占用会比较大,包括:·每个客户端会持续占用一个 PHP 长连接进程资源;
    ·每个长连接进程会占用一个 socket 资源;总的说来,局限在 web 容器里,这种模式不适合大规模应用。比如空间服务商限定了你网站的 HTTP 并发连接数,那长连接有可能将你的整个网站拖垮。那还不如回归到“客户端周期性轮询”的模式呢,损失一点“实时性”,换来的是“在线用户数”。
      

  32.   

    我运用了你的方法尝试,发现一个问题每个端口只能创建一个套接字
    如果我使用随机端口的话socket_sendto就不会知道目前有哪些端口需要发送了我是否需要维护一个当前在线的端口表?是保存到数据库比较好,还是存为文件模式呢?
      

  33.   

    用 UDP 广播,具体用法你查一下资料吧。
      

  34.   

    maquan
    广播我已经测试成功但是广播还是需要设置端口我贴上我的代码js请求的phpheader("Cache-Control: no-cache, must-revalidate");
    header("Expires: Mon, 26 Jul 1997 05:00:00 GMT");
    include('config.php');//这里是$add(IP)和$port
    $socket = socket_create(AF_INET, SOCK_DGRAM, SOL_UDP);
    socket_bind($socket, $add, $port);//我尝试将$port写为0但是如果这样创建会失败$port = 0;
    socket_recvfrom($socket, $buf, 10240, 0, $from, $port);
    echo "Received $buf from remote address $from and remote port $port" . PHP_EOL;
    socket_close($socket);
    广播的phpinclude('config.php');
    $sock = socket_create(AF_INET, SOCK_DGRAM, SOL_UDP);
    socket_set_option($sock, SOL_SOCKET, SO_BROADCAST, 1); 
    $msg = rand(1000,9999);
    $len = strlen($msg);
    socket_sendto($sock, $msg, $len, 0,'255.255.255.255', $port);//这里的$port要和上面那文件的$port相同上面的文件才能收到广播,但是不同客户连接后端口必须不一样才能成功创建socket连接,所以要怎么写才能通知该IP下所有端口的用户呢?这种是通知了端口为$port的所有用户吧
    socket_close($sock);
    我该如何写才能是通知所有端口呢?
      

  35.   

    把socket.accept写在while(1)中就行
      

  36.   

    accept是监听有没有用户连接吧我这个是不需要用户连接用sendto根据ip和端口发送给在线的用户
      

  37.   

    再试试 socket_set_option($sock, SOL_SOCKET, SO_REUSEADDR, 1); 
      

  38.   

    还是不行
    socket_sendto函数的端口参数不能为空的话接收端就收不到信息,也就是说不能一次性send信息给所有线上的端口socket_set_option($sock, SOL_SOCKET, SO_REUSEADDR, 1); 应该是写在接收端吧,但是写了也不行你能写个例子给我吗?
      

  39.   

    昨天晚上心血来潮,写了一段 UDP 广播通信的程序,正好可以供你参考 :)recvfrom.php<?php
    header("Cache-Control: no-cache, must-revalidate");
    header("Expires: Mon, 26 Jul 1997 05:00:00 GMT");$socket = socket_create( AF_INET, SOCK_DGRAM, SOL_UDP );
    socket_set_option( $socket, SOL_SOCKET, SO_BROADCAST, 1 );
    socket_set_option( $socket, SOL_SOCKET, SO_REUSEADDR, 1 );
    $result = socket_bind( $socket, '0.0.0.0', 9999 );$addr = '';
    $port = 0;
    $result = socket_recvfrom( $socket, $buf, 10240, 0, $addr, $port );
    socket_close($socket);echo "recvfrom: [({$result}){$buf}] from [{$addr}:{$port}]";
    sendto.php<?php
    $socket = socket_create( AF_INET, SOCK_DGRAM, SOL_UDP );
    socket_set_option( $socket, SOL_SOCKET, SO_BROADCAST, 1 );$msg = date('Y-n-j H:i:s');
    $len = strlen( $msg );
    $result = socket_sendto( $socket, $msg, $len, 0, '255.255.255.255', 9999 );
    socket_close( $socket );echo "sendto: [({$result}){$msg}]";
    以上两个 PHP 程序,recvfrom.php 可以用多个浏览器窗口分别发送请求,都会处在等待状态,然后再用一个浏览器窗口发送 sendto.php 的请求,前面的几个浏览器窗口就同时得到结果了。
    ————————————————————————————————
    基于CSDN论坛提供的插件扩展功能,自己做了个签名档工具,分享给大家,欢迎技术交流 :)
      

  40.   

    非常感谢
    已经测试成功
    现在返回去看SO_REUSEADDR的解释Reports whether local addresses can be reused. 
    才恍然大悟,“是否可重复使用本地地址”
    设置了这个option过后就不会再出现bind时提醒一个IP一个端口只能被建立一个连接了刚测试发现
    recvfrom.phpl里面不用加这句也成功
    socket_set_option( $socket, SOL_SOCKET, SO_BROADCAST, 1 );我认为可以删除,因为广播的页面是sendto.php,recvfrom.php只需要可重复使用本地地址和端口就行了是这样吗?
      

  41.   

    这个我也不是很清楚。UDP 广播包跟普通的 UDP 包一个很明显的区别就是“目标 IP 地址不同”。作为“监听 UDP 包”的 socket,从物理上讲,它肯定能“感知”到广播和非广播的 UDP 包,但它会不会“接收”广播包呢?如果你已经把这个 socket 设成“广播”型了,那肯定就没问题了,但如果不设置呢?它会不会拒收呢?我觉得这个不好说,可能跟具体的系统实现有关吧。虽然实际使用时不写也可以,但我总觉得写上更安全。
      

  42.   

    不知道我理解得对不对,广播(broadcast)仅限于局域网内,即一个网段。要出外网的话,就是多播(multicast)了,需要路由器配合。
      

  43.   

    我没太看懂你的问题所在。据我的理解,UDP 广播包只能发给同网段所有 IP 的同一个端口,而不能发给一个 IP 的多个端口。你不用让每个接收方使用不同端口,那样非乱套不可。就让它们在同一个固定端口做监听,收到的东西如果不是发给自己的,则忽略掉,重新监听。不过我突然相当一个问题,这样用广播包的话,同一个服务器上的其它网站,甚至是同一个机房的其它服务器,是不是都能收到这些数据包了?……看来在数据安全方面还得注意一下,hehe
      

  44.   

    防火墙要添加一个开放的udp端口才行也就是我程序里面进行广播的那个端口
      

  45.   

    学习了一下“百度百科:广播地址”的知识,里面提到“有限广播”和“直接广播”。你这种应用背景,应该是用“有限广播”,那么也就不必要去算什么广播地址了,直接用 255.255.255.255 就好了。
    ————————————————————————————————
    基于CSDN论坛提供的插件扩展功能,自己做了个签名档工具,分享给大家,欢迎技术交流 :)
      

  46.   

    结贴,问题已经完全解决!
    感谢maquan和其他网友的热心帮助!本该多给分的,但是小弟确实是没有积分了
      

  47.   

    确实如此只要开放了udp端口外网直接使用255.255.255.255也是可以广播成功的
      

  48.   


    你好 请问你是怎么实现多人在线的?是用php的socket吗? 我现在也遇到了这个问题! 我的是用cli执行一个socket.php 然后执行一个swf可以连接数据 返回数据。但是两个一起就不行了,就算连接一个swf,当我把这个swf关掉后再打开的话服务器就返回不了数据了! 可以分享一下你的经验吗? 提点一下我! 谢谢!