phpstreamsocket
① php socket 如何实现非阻塞
关于socket的阻塞与非阻塞模式以及它们之间的优缺点,这已经没什么可言的;我打个很简单的比方,如果你调用socket send函数时;
如果是阻塞模式下:
send先比较待发送数据的长度len和套接字s的发送缓冲的长度,如果len大于s的发送缓冲区的长度,该函数返回SOCKET_ERROR;如果len小于或者等于s的发送缓冲区的长度,那么send先检查协议是否正在发送s的发送缓冲中的数据,如果是就等待协议把数据发送完,如果协议还没有开始发送s的发送缓冲中的数据或者s的发送缓冲中没有数据,那么 send就比较s的发送缓冲区的剩余空间和len,如果len大于剩余空间大小,send就一直等待协议把s的发送缓冲中的数据发送完,如果len小于剩余空间大小send就仅仅把buf中的数据到剩余空间里
如果是非阻塞模式下:
在调用socket send函数时,如果能写到socket缓冲区时,就写数据并返回实际写的字节数目,当然这个返回的实际值可能比你所要写的数据长度要小些(On nonblocking stream oriented sockets, the number of bytes written can be between 1 and the requested length, depending on buffer availability on both the client and server computers),如果不可写的话,就直接返回SOCKET_ERROR了,所以没有等待的过程。。
经过上面的介绍后,下面介绍如何设置socket的非阻塞模式:
当使用socket()函数和WSASocket()函数创建套接字时,默认都是阻塞的。在创建套接字之后,通过调用ioctlsocket()函数,将该套接字设置为非阻塞模式。
//-------------------------
// Set the socket I/O mode: In this case FIONBIO
// enables or disables the blocking mode for the
// socket based on the numerical value of iMode.
// If iMode = 0, blocking is enabled;
// If iMode != 0, non-blocking mode is enabled.
u_long iMode = 1; //non-blocking mode is enabled.
ioctlsocket(m_socket, FIONBIO, &iMode); //设置为非阻塞模式
套接字设置为非阻塞模式后,在调用Windows Sockets API函数时,调用函数会立即返回。大多数情况下,这些函数调用都会调用“失败”,并返回WSAEWOULDBLOCK错误代码。说明请求的操作在调用期间内没有时间完成。通常,应用程序需要重复调用该函数,直到获得成功返回代码。 不同的Windows Sockets API函数,在调用失败时返回的WSAEWOULDBLOCK错误代码具有不同的含义
需要说明的是并非所有的 Windows Sockets API 在非阻塞模式下调用,都会返回 WSAEWOULDBLOCK 错误。例如,以非阻塞模式的套接字为参数调用 bind() 函数时,就不会返回该错误代码。当然,在调用 WSAStartup() 函数时更不会返回该错误代码,因为该函数是应用程序第一调用的函数,当然不会返回这样的错误代码。
要将套接字设置为非阻塞模式,除了使用 ioctlsocket() 函数之外,还可以使用 WSAAsyncselect() 和 WSAEventselect() 函数。当调用该函数时,套接字会自动地设置为非阻塞方式:
② PHPSocket缂栫▼杩囩▼
PHP锛孲ocket缂栫▼杩囩▼寰埚氢汉杩树笉鐭ラ亾锛岀幇鍦ㄨ╂垜浠涓璧锋潵鐪嬬湅钖э紒
PHP Socket缂栫▼杩囩▼
銆銆Socket鐢ㄤ簬杩涚▼闂撮氢俊锛岃繘绋嬮棿阃氢俊阃氩父锘轰簬瀹㈡埛绔钬旀湇锷$妯″瀷銆傛ゆ椂锛屽㈡埛绔钬旀湇锷$鏄鍙浠ュ郊姝や氦浜掔殑搴旂敤绋嫔簭銆傚㈡埛绔鍜屾湇锷$涔嬮棿镄勪氦浜挜渶瑕佽繛鎺ャ係ocket缂栫▼璐熻矗镄勫氨鏄涓哄簲鐢ㄧ▼搴忎箣闂村缓绔嫔彲杩涜屼氦浜掔殑杩炴帴銆备笅闱涓哄ぇ瀹朵粙缁崭竴涓婸HP Socket缂栫▼杩囩▼!
銆銆浣跨敤浠g爜
銆銆鐩镄'锛氩紑鍙戜竴涓瀹㈡埛绔鐢ㄤ簬鍙戦乻tring娑堟伅鍒版湇锷$锛屾湇锷$灏嗙浉钖岀殑淇℃伅鍙嶈浆钖庤繑锲炵粰瀹㈡埛绔銆
銆銆PHP链嶅姟鍣
銆銆绗1姝ワ细璁剧疆鍙橀噺锛屽傗滀富链衡濆拰钬灭鍙b
銆銆$host = "127.0.0.1";$port = 5353;// No Timeout set_time_limit(0);
銆銆绔鍙e彿鍙浠ユ槸1024 -65535涔嬮棿镄勪换浣曟f暣鏁般
銆銆绗2姝ワ细鍒涘缓socket
銆銆$socket = socket_create(AF_INET, SOCK_STREAM, 0) or die("Could not create socketn");
銆銆绗3姝ワ细缁戝畾socket鍒扮鍙e拰涓绘満
銆銆鍒涘缓镄剆ocket璧勬簮缁戝畾鍒癐P鍦板潃鍜岀鍙e彿銆
銆銆$result = socket_bind($socket, $host, $port) or die("Could not bind to socketn");
銆銆绗4姝ワ细钖锷╯ocket鐩戝惉
銆銆鍦ㄧ粦瀹氩埌IP鍜岀鍙e悗锛屾湇锷$寮濮嬬瓑寰呭㈡埛绔镄勮繛鎺ャ傚湪娌℃湁杩炴帴涔嫔墠瀹冨氨涓鐩寸瓑涓嫔幓銆
銆銆$result = socket_listen($socket, 3) or die("Could not set up socket listenern");
銆銆绗5姝ワ细鎺ュ弹杩炴帴
銆銆杩欎釜鍑芥暟浼氭帴鍙楁墍寤虹殑socket浼犲叆镄勮繛鎺ヨ锋眰銆傚湪鎺ュ弹𨱒ヨ嚜瀹㈡埛绔痵ocket镄勮繛鎺ュ悗锛岃ュ嚱鏁拌繑锲炲彟涓涓狲ocket璧勬簮锛屽疄闄呬笂灏辨槸璐熻矗涓庣浉搴旂殑瀹㈡埛绔痵ocket阃氢俊銆傝繖閲岀殑钬$spawn钬濆氨鏄璐熻矗涓庡㈡埛绔痵ocket阃氢俊镄剆ocket璧勬簮銆
銆銆$spawn = socket_accept($socket) or die("Could not accept incoming connectionn");
銆銆鍒扮幇鍦ㄤ负姝锛屾垜浠宸茬粡鍑嗗囧ソ浜嗘湇锷$痵ocket 锛屼絾瀹为檯涓婅繖涓鑴氭湰骞舵病链夊仛浠讳綍浜嬫儏銆傛墍浠ヤ负浜嗙户缁瀹屾垚涓婅堪鐩镙囷纴鎴戜滑灏呜诲彇瀹㈡埛绔痵ocket娑堟伅锛岀劧钖庡皢鎺ユ敹鍒扮殑娑堟伅鍙嶈浆钖庡彂锲炵粰瀹㈡埛绔痵ocket銆
銆銆绗6姝ワ细浠庡㈡埛绔痵ocket璇诲彇娑堟伅
銆銆$input = socket_read($spawn, 1024) or die("Could not read inputn");
銆銆绗7姝ワ细鍙嶈浆娑堟伅
銆銆$output = strrev($input) . "n";
銆銆绗8姝ワ细鍙戦佹秷鎭缁椤㈡埛绔痵ocket
銆銆socket_write($spawn, $output, strlen ($output)) or die("Could not write outputn");
銆銆鍏抽棴socket
銆銆socket_close($spawn);socket_close($socket);
銆銆杩椤氨瀹屾垚浜嗘湇锷$銆傜幇鍦锛屾垜浠瀛︿範濡备綍鍒涘缓PHP瀹㈡埛绔銆
銆銆PHP瀹㈡埛绔
銆銆鍓崭袱涓姝ラや笌链嶅姟绔鐩稿悓銆
銆銆绗1姝ワ细璁剧疆鍙橀噺锛屽傗滀富链衡濆拰钬灭鍙b
銆銆$host = "127.0.0.1";$port = 5353;// No Timeout set_time_limit(0);
銆銆娉锛氲繖閲岀殑绔鍙e拰涓绘満搴旇ュ拰链嶅姟绔涓镄勫畾涔夋槸鐩稿悓镄勚
銆銆绗2姝ワ细鍒涘缓socket
銆銆$socket = socket_create(AF_INET, SOCK_STREAM, 0) or die("Could not create socketn");
銆銆绗3姝ワ细杩炴帴鍒版湇锷$
銆銆$result = socket_connect($socket, $host, $port) or die("Could not connect toservern");
銆銆姝ゆ椂鍜屾湇锷$涓嶅悓锛屽㈡埛绔痵ocket涓岖粦瀹氱鍙e拰涓绘満銆傜浉鍙嶏纴瀹冭繛鎺ュ埌链嶅姟绔痵ocket锛岀瓑寰呮帴鍙楁潵镊瀹㈡埛绔痵ocket镄勮繛鎺ャ傝繖涓姝ュ缓绔嬩简瀹㈡埛绔痵ocket鍒版湇锷$痵ocket镄勮繛鎺ャ
銆銆绗4姝ワ细鍐椤叆链嶅姟绔痵ocket
銆銆socket_write($socket, $message, strlen($message)) or die("Could not send data to servern");
銆銆鍦ㄦゆラや腑锛屽㈡埛绔痵ocket镄勬暟鎹琚鍙戦佸埌链嶅姟绔痵ocket銆
銆銆绗5姝ワ细阒呰绘潵镊链嶅姟绔镄勫搷搴
銆銆$result = socket_read ($socket, 1024) or die("Could not read server responsen");echo "Reply From Server :".$result;
銆銆绗6姝ワ细鍏抽棴socket
銆銆socket_close($socket);
銆銆瀹屾暣镄勪唬镰
銆銆链嶅姟绔(server.php)
銆銆// set some variables$host = "127.0.0.1";$port = 25003;// don't timeout!set_time_limit(0);// create socket$socket = socket_create(AF_INET, SOCK_STREAM, 0) or die("Could not create socketn");// bind socket to port$result = socket_bind($socket, $host, $port) or die("Could not bind to socketn");// start listening for connections$result = socket_listen($socket, 3) or die("Could not set up socket listenern");// accept incoming connections// spawn another socket to handle communication$spawn = socket_accept($socket) or die("Could not accept incoming connectionn");// read client input$input = socket_read($spawn, 1024) or die("Could not read inputn");// clean up input string$input = trim($input);echo "Client Message : ".$input;// reverse client input and send back$output = strrev($input) . "n";socket_write($spawn, $output, strlen ($output)) or die("Could not write outputn");// close socketssocket_close($spawn);socket_close($socket);
銆銆瀹㈡埛绔(client.php)
銆銆$host = "127.0.0.1";$port = 25003;$message = "Hello Server";echo "Message To server :".$message;// create socket$socket = socket_create(AF_INET, SOCK_STREAM, 0) or die("Could not create socketn");// connect to server$result = socket_connect($socket, $host, $port) or die("Could not connect to servern"); // send string to serversocket_write($socket, $message, strlen($message)) or die("Could not send data to servern");// get server response$result = socket_read ($socket, 1024) or die("Could not read server responsen");echo "Reply From Server :".$result;// close socketsocket_close($socket);
銆銆寤虹珛涓婅堪鏂囦欢(server.php鍜宑lient.php)钖庯纴镓ц屽备笅镎崭綔锛
銆銆澶嶅埗www鐩褰曚腑镄勮繖浜涙枃浠(锅囱网AMP)锛屽畨缃浜嶤:wamp銆
銆銆镓揿紑Web娴忚埚櫒锛屽湪鍦板潃镙忎腑阌鍏localhost 銆
銆銆鍏堟祻瑙坰erver.php铹跺悗client.php銆
③ 想问下php的socket的工作流程是什么
PHP 使用Berkley的socket库来创建它的连接。你可以知道socket只不过是一个数据结构。你使用这个socket数据结构去开始一个客户端和服务器之间的会话。这个服务器是一直在监听准备产生一个新的会话。当一个客户端连接服务器,它就打开服务器正在进行监听的一个端口进行会话。这时,服务器端接受客户端的连接请求,那么就进行一次循环。现在这个客户端就能够发送信息到服务器,服务器也能发送信息给客户端。
产生一个Socket,你需要三个变量:一个协议、一个socket类型和一个公共协议类型。产生一个socket有三种协议供选择,继续看下面的内容来获取详细的协议内容。
定义一个公共的协议类型是进行连接一个必不可少的元素。下面的表我们看看有那些公共的协议类型。
表一:协议
名字/常量 描述
AF_INET 这是大多数用来产生socket的协议,使用TCP或UDP来传输,用在IPv4的地址
AF_INET6 与上面类似,不过是来用在IPv6的地址
AF_UNIX 本地协议,使用在Unix和Linux系统上,它很少使用,一般都是当客户端和服务器在同一台及其上的时候使用
表二:Socket类型
名字/常量 描述
SOCK_STREAM 这个协议是按照顺序的、可靠的、数据完整的基于字节流的连接。这是一个使用最多的socket类型,这个socket是使用TCP来进行传输。
SOCK_DGRAM 这个协议是无连接的、固定长度的传输调用。该协议是不可靠的,使用UDP来进行它的连接。
SOCK_SEQPACKET 这个协议是双线路的、可靠的连接,发送固定长度的数据包进行传输。必须把这个包完整的接受才能进行读取。
SOCK_RAW 这个socket类型提供单一的网络访问,这个socket类型使用ICMP公共协议。(ping、traceroute使用该协议)
SOCK_RDM 这个类型是很少使用的,在大部分的操作系统上没有实现,它是提供给数据链路层使用,不保证数据包的顺序
表三:公共协议
名字/常量 描述
ICMP 互联网控制消息协议,主要使用在网关和主机上,用来检查网络状况和报告错误信息
UDP 用户数据报文协议,它是一个无连接,不可靠的传输协议
TCP 传输控制协议,这是一个使用最多的可靠的公共协议,它能保证数据包能够到达接受者那儿,如果在传输过程中发生错误,那么它将重新发送出错数据包。
现在你知道了产生一个socket的三个元素,那么我们就在php中使用socket_create()函数来产生一个socket。这个 socket_create()函数需要三个参数:一个协议、一个socket类型、一个公共协议。socket_create()函数运行成功返回一个包含socket的资源类型,如果没有成功则返回false。
Resourece socket_create(int protocol, int socketType, int commonProtocol);
现在你产生一个socket,然后呢?php提供了几个操纵socket的函数。你能够绑定socket到一个IP,监听一个socket的通信,接受一个socket;现在我们来看一个例子,了解函数是如何产生、接受和监听一个socket。
<?php
$commonProtocol = getprotobyname(“tcp”);
$socket = socket_create(AF_INET, SOCK_STREAM, $commonProtocol);
socket_bind($socket, ‘localhost’, 1337);
socket_listen($socket);
// More socket functionality to come
?>
上面这个例子产生一个你自己的服务器端。例子第一行,
$commonProtocol = getprotobyname(“tcp”);
使用公共协议名字来获取一个协议类型。在这里使用的是TCP公共协议,如果你想使用UDP或者ICMP协议,那么你应该把getprotobyname() 函数的参数改为“udp”或“icmp”。还有一个可选的办法是不使用getprotobyname()函数而是指定SOL_TCP或SOL_UDP在 socket_create()函数中。
$socket = socket_create(AF_INET, SOCK_STREAM, SOL_TCP);
例子的第二行是产生一个socket并且返回一个socket资源的实例。在你有了一个socket资源的实例以后,你就必须把socket绑定到一个IP地址和某一个端口上。
④ php用STMP邮件发送提示Connection: stream_socket_client not available, falling back to fsockopen
可以检测下,对外的25端口是否开放,因为stmp服务端口是25