1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
|
HTTP
/1
.1 200 OK
Content-Type: text
/plain
Transfer-Encoding: chunked
25
This is the data
in
the first chunk
1C
and this is the second one
3
con
8
sequence
0
|
针对以上三种情况,分别要做处理,出现粘包和缺包时,我们需要缓存尚未处理的部分留待下次接收到数据时一并处理。
根据如上流程图实现的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
137
138
139
|
<?php
/**
* Created by PhpStorm.
* User: Jenner
* Date: 2015/8/10
* Time: 14:00
*
* 简单http server端协议解析,实现粘包、缺包,拆包
*/
$server
=
new
Server();
//注册响应回调函数
$server
->registerHandler(
function
(
$connection
,
$header
,
$body
){
echo
"get request: "
. time() . PHP_EOL;
echo
"header: "
. PHP_EOL .
$header
. PHP_EOL;
echo
"body: "
. PHP_EOL .
$body
. PHP_EOL;
$response
=
"HTTP/1.1 200 OK\r\n"
;
$response
.=
"Date: Mon, 10 Aug 2015 06:22:08 GMT\r\n"
;
$response
.=
"Content-Type: text/html;charset=utf-8\r\n\r\n"
;
socket_write(
$connection
,
$response
,
strlen
(
$response
));
});
//启动server
$server
->start();
/**
* 简易http server 支持http协议解析
* Class Server
*/
class
Server
{
/**
* @var string 字符流缓存
*/
protected
$cache
=
""
;
/**
* @var http请求处理器
*/
protected
$handler
;
/**
* 启动server
*/
public
function
start()
{
$socket
= socket_create(AF_INET, SOCK_STREAM, SOL_TCP);
socket_bind(
$socket
,
'0.0.0.0'
, 1212);
socket_listen(
$socket
);
while
(
$connection
= socket_accept(
$socket
)) {
echo
"connected"
. PHP_EOL;
while
(true){
$bytes
= socket_read(
$connection
, 1);
echo
"read: "
.
$bytes
. PHP_EOL;
if
(
empty
(
$bytes
)) {
sleep(1);
continue
;
}
if
(
$this
->parse(
$connection
,
$bytes
)){
break
;
}
echo
"parse"
. PHP_EOL;
usleep(100);
}
}
}
/**
* 注册处理器
* @param $handler
*/
public
function
registerHandler(
$handler
)
{
$this
->handler =
$handler
;
}
/**
* http协议解析
* @param $connection
* @param $data
* @return bool
*/
protected
function
parse(
$connection
,
$data
)
{
$data
=
$this
->cache .
$data
;
var_dump(
$data
);
$header
=
$body
=
""
;
if
(
strstr
(
$data
,
"\r\n\r\n"
) === false) {
$this
->cache =
$data
;
echo
"cached"
. PHP_EOL;
return
false;
}
$http_info
=
explode
(
"\r\n\r\n"
,
$data
, 2);
$header
=
$http_info
[0];
$body
=
count
(
$http_info
) > 1 ?
$http_info
[1] : 0;
$content_length
=
$this
->getContentLength(
$header
);
if
(
$content_length
== 0) {
// 正好是一个http帧
call_user_func(
$this
->handler,
$connection
,
$header
, null);
socket_close(
$connection
);
return
true;
}
elseif
(
$content_length
>
strlen
(
$body
)){
// 缺少body部分
$this
->cache =
$data
;
return
false;
}
else
{
// 发生粘包,摘出当前包,缓存剩余部分
$body
=
substr
(
$body
, 0,
$content_length
);
$this
->cache =
substr
(
$body
,
$content_length
);
call_user_func(
$this
->handler,
$connection
,
$header
,
$body
);
return
false;
}
}
/**
* 获取content-length
* @param $headers
* @return int
*/
protected
function
getContentLength(
$headers
)
{
$headers
=
explode
(
"\r\n"
,
$headers
);
foreach
(
$headers
as
$header
) {
if
(
stristr
(
"content-length"
,
$headers
) === false)
continue
;
$content_length
=
intval
(
explode
(
":"
,
$header
, 2)[1]);
return
$content_length
;
}
return
0;
}
}
|
客户端代码如下:
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
|
<?php
/**
* Created by PhpStorm.
* User: Jenner
* Date: 2015/8/10
* Time: 14:19
*
* 简单http socket客户端,实现一次HTTP请求
*/
$socket
= socket_create(AF_INET, SOCK_STREAM, SOL_TCP);
if
(socket_connect(
$socket
,
"127.0.0.1"
, 1212) === false){
echo
"ERROR:"
. socket_strerror(socket_last_error(
$socket
)) . PHP_EOL;
exit
;
}
$request_headers
=
array
(
"GET / HTTP/1.1"
,
"Host: xxx.xxx"
,
"Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8"
,
);
$request
= implode(
"\r\n"
,
$request_headers
);
// if comment the follow line, you will read nothing. because the request format is error.
$request
.=
"\r\n\r\n"
;
var_dump(
$request
);
if
(socket_write(
$socket
,
$request
,
strlen
(
$request
)) === false){
echo
"ERROR:"
. socket_strerror(socket_last_error(
$socket
)) . PHP_EOL;
exit
;
}
echo
"the server will not response until time is out"
. PHP_EOL;
$response
= socket_read(
$socket
, 1024);
if
(
$response
=== false ||
empty
(
$response
)){
echo
"ERROR: timeout or socket error"
. PHP_EOL;
exit
;
}
echo
"response:"
. PHP_EOL;
var_dump(
$response
);
socket_close(
$socket
);
|
源码地址:https://github.com/huyanping/learning-http-protocol
原创文章,转载请注明: 转载自始终不够
本文链接地址: 简易HTTP协议解析
转载请注明:始终不够 » 简易HTTP协议解析