php socket read 阻塞,php socket编程:客户端异常关闭导致服务端中断的问题

socket_recv和socket_read都可以用于读取socket数据,不过二者有差别,推荐使用socket_recv。

原因如下:

1.socket_recv支持多种flag,用于不同场景

2.socket_recv可以检测socket关闭的情况(例如对端关闭了socket)

返回值:$return_value=socket_recv(...)

含义: >0 表示接收到的字节数;

===0, 发生了错误,socket closed;

===false,无数据,socket not closed。

socket_read不能判断socket是否已经断开。

测试流程:

启动server端,再启动client端:可以正常通信。

kill掉client端,结果server端只能读取到空字符串。

测试代码如下:

file: bug1_server.php

/**

* file: bug1_server.php

* socket server

* 基于php socket函数族

* IO模型:同步阻塞

* 粘包处理:固定长度

* 连接数:1个socket连接

*

* 测试目标:模拟client crash时,server无法判断socket是否断开

* 测试结果:kill杀掉client进程后,server进程socket_last_error()返回为0,无法判断socket是否关闭

*

* @author davidyanxw

* @date 2018.04.27

*/

set_time_limit(0);

//创建服务端的socket套接流,net协议为IPv4,protocol协议为TCP

$socket = socket_create(AF_INET, SOCK_STREAM, SOL_TCP);

// reuse address

socket_set_option($socket, SOL_SOCKET, SO_REUSEADDR, 1);

/*绑定接收的套接流主机和端口,与客户端相对应*/

if (socket_bind($socket, '127.0.0.1', 8801) == false) {

echo 'server bind fail:' . socket_strerror(socket_last_error());

}

//监听套接流

if (socket_listen($socket, 4) == false) {

echo 'server listen fail:' . socket_strerror(socket_last_error());

}

$accept_resource = socket_accept($socket);

if($accept_resource === false) {

echo "accept connection failed".PHP_EOL;

exit;

}

// 读写超时时间:0.8s

socket_set_option($accept_resource, SOL_SOCKET, SO_RCVTIMEO, array("sec" => 0, "usec" => 800000));

socket_set_option($accept_resource, SOL_SOCKET, SO_SNDTIMEO, array("sec" => 0, "usec" => 800000));

// stream固定长度

$len = 100;

//让服务器不停获取客户端传过来的信息

while (true) {

$string_read = socket_read($accept_resource, $len);

if($string_read === false) {

echo "socket error:" . socket_last_error() . ",error msg:" . socket_strerror(socket_last_error()) . PHP_EOL;

break;

}

elseif($string_read == '') {

if(in_array(socket_last_error(), [SOCKET_EPIPE, SOCKET_ECONNRESET])) {

echo "socket error:".socket_last_error().",error msg:".socket_strerror(socket_last_error()).PHP_EOL;

break;

}

if(in_array(socket_last_error(), [SOCKET_EAGAIN])) {

// EAGAIN, retry later

usleep(500);

continue;

}

echo "server receive empty:" . socket_last_error() . ",error msg:" . socket_strerror(socket_last_error()) . PHP_EOL;

}

else {

$string = trim($string_read);

echo 'server receive success,msg:['.$string.'],time:' . microtime(true) . PHP_EOL;

}

} ;

// 先shutdown,后close

@socket_shutdown($accept_resource);

socket_close($accept_resource);

@socket_shutdown($socket);

socket_close($socket);

/**

* 生成php随机串

* @param $length

* @return string

*/

function randomkeys($length){

$output='';

for ($a = 0; $a

$output .= chr(mt_rand(33, 126));

}

return $output;

}

?>

file:bug1_client.php

/**

* file:bug1_client.php

* socket client

* 基于php socket函数族

* IO模型:同步阻塞

* 粘包处理:固定长度

* 连接数:1个socket连接

*

* @author davidyanxw

* @date 2018.04.27

*/

set_time_limit(0);

//创建一个socket套接流

$socket = socket_create(AF_INET, SOCK_STREAM, SOL_TCP);

//接收套接流的最大超时时间(800ms)

//发送套接流的最大超时时间(800ms)

socket_set_option($socket, SOL_SOCKET, SO_RCVTIMEO, array("sec" => 0, "usec" => 800000));

socket_set_option($socket, SOL_SOCKET, SO_SNDTIMEO, array("sec" => 0, "usec" => 800000));

$len = 100;

//连接服务端的套接流,这一步就是使客户端与服务器端的套接流建立联系

if (socket_connect($socket, '127.0.0.1', 8801) == false) {

echo 'connect fail massege:' . socket_strerror(socket_last_error());

} else {

while(1){

$ori_msg = 'Hello, server!'.randomkeys(8);

$message_write = str_pad($ori_msg, $len);

//向服务端写入字符串信息

$sent = @socket_write($socket, $message_write, $len);

if ($sent === false) {

if(in_array(socket_last_error(), [SOCKET_EPIPE, SOCKET_ECONNRESET])) {

echo "socket error:".socket_last_error().",error msg:".socket_strerror(socket_last_error()).PHP_EOL;

break;

}

echo "socket error:".socket_last_error().",error msg:".socket_strerror(socket_last_error()).PHP_EOL;

}

else{

echo 'client write success,msg:['.$ori_msg.'],time:' . microtime(true).PHP_EOL;

}

// break;

}

}

@socket_shutdown($socket);

socket_close($socket);

/**

* 生成php随机串

* @param $length

* @return string

*/

function randomkeys($length){

$output='';

for ($a = 0; $a

$output .= chr(mt_rand(33, 126));

}

return $output;

}

?>

正确的代码是:(file: debug1_server.php)

/**

* file: debug1_server.php

* socket server

* 基于php socket函数族

* IO模型:同步阻塞

* 粘包处理:固定长度

* 连接数:1个socket连接

*

* 测试目标:模拟client crash时,server无法判断socket是否断开

* 测试结果:kill杀掉client进程后,server进程socket_last_error()返回为0,无法判断socket是否关闭

*

* @author davidyanxw

* @date 2018.04.27

*/

set_time_limit(0);

//创建服务端的socket套接流,net协议为IPv4,protocol协议为TCP

$socket = socket_create(AF_INET, SOCK_STREAM, SOL_TCP);

// reuse address

socket_set_option($socket, SOL_SOCKET, SO_REUSEADDR, 1);

/*绑定接收的套接流主机和端口,与客户端相对应*/

if (socket_bind($socket, '127.0.0.1', 8801) == false) {

echo 'server bind fail:' . socket_strerror(socket_last_error());

}

//监听套接流

if (socket_listen($socket, 4) == false) {

echo 'server listen fail:' . socket_strerror(socket_last_error());

}

$accept_resource = socket_accept($socket);

if($accept_resource === false) {

echo "accept connection failed".PHP_EOL;

exit;

}

// 读写超时时间:0.8s

socket_set_option($accept_resource, SOL_SOCKET, SO_RCVTIMEO, array("sec" => 0, "usec" => 800000));

socket_set_option($accept_resource, SOL_SOCKET, SO_SNDTIMEO, array("sec" => 0, "usec" => 800000));

// stream固定长度

$len = 100;

//让服务器不停获取客户端传过来的信息

while (true) {

/* 使用socket_recv */

$len_read = socket_recv($accept_resource, $string_read, $len, 0);

if ($len_read === false) {

// no data

echo "no data".PHP_EOL;

continue;

}

elseif($len_read === 0 ) {

// socket closed

echo "socket error:" . socket_last_error() . ",error msg:" . socket_strerror(socket_last_error()) . PHP_EOL;

break;

}

else {

$string = trim($string_read);

echo 'server receive success,msg:['.$string.'],time:' . microtime(true) . PHP_EOL;

}

} ;

// 先shutdown,后close

@socket_shutdown($accept_resource);

socket_close($accept_resource);

@socket_shutdown($socket);

socket_close($socket);

/**

* 生成php随机串

* @param $length

* @return string

*/

function randomkeys($length){

$output='';

for ($a = 0; $a

$output .= chr(mt_rand(33, 126));

}

return $output;

}

?>

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
好的,以下是一个简单的安卓10的Java层阻塞socket通信服务端客户端的实现代码: 服务端代码: ```java import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; import java.net.ServerSocket; import java.net.Socket; public class Server { public static void main(String[] args) { try { ServerSocket serverSocket = new ServerSocket(12345); // 监听端口号为12345 System.out.println("Server started, listening on port 12345..."); while (true) { Socket socket = serverSocket.accept(); // 阻塞等待客户端连接 System.out.println("Client connected: " + socket.getRemoteSocketAddress()); InputStream inputStream = socket.getInputStream(); OutputStream outputStream = socket.getOutputStream(); byte[] buffer = new byte[1024]; int len; while ((len = inputStream.read(buffer)) != -1) { // 阻塞等待客户端数据 String msg = new String(buffer, 0, len); System.out.println("Received from client: " + msg); outputStream.write(("Server received: " + msg).getBytes()); // 发送数据给客户端 outputStream.flush(); } socket.close(); // 关闭连接 System.out.println("Client disconnected: " + socket.getRemoteSocketAddress()); } } catch (IOException e) { e.printStackTrace(); } } } ``` 客户端代码: ```java import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; import java.net.Socket; import java.net.UnknownHostException; public class Client { public static void main(String[] args) { try { Socket socket = new Socket("localhost", 12345); // 连接到本地的12345端口 System.out.println("Connected to server: " + socket.getRemoteSocketAddress()); InputStream inputStream = socket.getInputStream(); OutputStream outputStream = socket.getOutputStream(); String msg = "Hello, server!"; outputStream.write(msg.getBytes()); // 发送数据给服务端 outputStream.flush(); byte[] buffer = new byte[1024]; int len = inputStream.read(buffer); // 阻塞等待服务端数据 String responseMsg = new String(buffer, 0, len); System.out.println("Received from server: " + responseMsg); socket.close(); // 关闭连接 System.out.println("Disconnected from server."); } catch (UnknownHostException e) { e.printStackTrace(); } catch (IOException e) { e.printStackTrace(); } } } ``` 这里实现的是简单的“Hello world”级别的通信,你可以根据实际需求进行修改和扩展。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值