-
Notifications
You must be signed in to change notification settings - Fork 0
/
block_server_multi_conn_opt.php
139 lines (119 loc) · 4.62 KB
/
block_server_multi_conn_opt.php
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
<?php
/**
*
* socket server
* 基于php socket函数族
* IO模型:同步阻塞
* 粘包处理:自定义包头,包头内容是包体长度
* 连接数: 多进程多连接。
* 多进程:Leader-Follower模型
* 父进程先fork指定数量子进程,子进程负责accept连接
*
* 测试结果: 单cpu,10进程cpu load12, 20进程cpu load25
*
* @author davidyanxw
* @date 2018.04.27
*/
include "vendor/helper.php";
// 开启异步信号
if (function_exists('pcntl_async_signals')) {
// for php 7.1
pcntl_async_signals(true);
} else {
// for php 4.3.0+ (up to 7.0)
declare(ticks = 1);
}
// 脚本总超时
set_time_limit(0);
// 内存限制
ini_set('memory_limit', '64M');
//创建服务端的socket套接流,net协议为IPv4,protocol协议为TCP
$socket = socket_create(AF_INET, SOCK_STREAM, SOL_TCP);
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());
/*这里的127.0.0.1是在本地主机测试,你如果有多台电脑,可以写IP地址*/
}
//监听套接流
if (socket_listen($socket, 4) == false) {
echo 'server listen fail:' . socket_strerror(socket_last_error());
exit;
}
$len = 100;
$len_header = 4;
$max_process_num = 20;
$pid = posix_getpid();
// 忽略子进程信号
pcntl_signal(SIGCHLD, SIG_IGN);
//让服务器无限获取客户端传过来的信息
for($i=0;$i<$max_process_num;$i++){
$file_log = "/tmp/server.log.$pid.".date("Ymd");
$pid = pcntl_fork();
if($pid == -1) {
redirectIO("pcntl fork fail!".PHP_EOL, false, $file_log);
}
elseif($pid) {
// process parent
// wait child process
pcntl_wait($status, WNOHANG);
}
else {
$pid_son = posix_getpid(); // server子进程pid
$file_log = "/tmp/server.log.$pid_son.".date("Ymd");
while(true) {
/**
* accept socket
* $accept_resource: socket for recv or send msg
*/
redirectIO("start accept socket".PHP_EOL, false, $file_log);
$accept_resource = socket_accept($socket);
if($accept_resource === false) {
redirectIO("accept connection failed".PHP_EOL, false, $file_log);
continue;
}
// $accept_resource, recv or send timeout time: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));
// send or recv msg
while(true) {
// recv msg
redirectIO("server: start read ".PHP_EOL, false, $file_log);
/*读取客户端传过来的资源,并转化为字符串*/
$string = biz_read($accept_resource, $len_header);
if($string === false) {
redirectIO(errorMsg(), false, $file_log);
break;
}
else {
$string = trim($string);
$io_msg = 'server receive is :' . microtime(true) . '[' . $string . ']' . PHP_EOL;//PHP_EOL为php的换行预定义常量
redirectIO($io_msg, false, $file_log);
}
// send msg
redirectIO("server: start write ".PHP_EOL, false, $file_log);
// 向服务端写入字符串信息
$ori_client = "Hello client!content:abc" . PHP_EOL . "def" . PHP_EOL . "ghi" . PHP_EOL . "jkl" . randomkeys(5);
$sent = biz_write($accept_resource, $ori_client);
if ($sent === false) {
if(in_array(socket_last_error(), [SOCKET_EPIPE, SOCKET_ECONNRESET])) {
redirectIO(errorMsg(), false, $file_log);
break;
}
$io_msg = 'server write fail|'.socket_last_error().'|'.(microtime(true)-$time_start).'|' . socket_strerror(socket_last_error()).PHP_EOL;
redirectIO($io_msg, false, $file_log);
}
else {
redirectIO("server write sucess,msg:[$ori_client]".PHP_EOL, false, $file_log);
}
}
@socket_shutdown($accept_resource);
socket_close($accept_resource);
}
exit(0); // 子进程通讯结束
}
}
// close
// 不能shutdown socket,子进程通信需要
//@socket_shutdown($socket);
socket_close($socket);