在 php 中创建并打开了 websocket 服务端,然而客户端提示 403 错误是怎么回事

2015-05-07 14:52:21 +08:00
 flowfire

找了一些websocket的资料,但是无论是使用他们提供的代码还是我自己写的代码,在客户端连接的时候都显示403错误、、
我用的是 apache2.4 x64 for windows + php
代码应该是没错的。。。
然后在浏览器中打开 server.php
在另一个窗口中使用 var ws = new WebSocket("ws://path:port")
返回的错误为
Error during WebSocket handshake: Unexpected response code: 403
求解。。。

4312 次点击
所在节点    PHP
9 条回复
feiyuanqiu
2015-05-08 04:34:05 +08:00
websocket 需要先握手建立连接之后才能通信,你握手那里是怎么处理的呢
server.php 只是一个验证性的 demo 吧? 如果不长,可以发出来看看
ericls
2015-05-08 04:57:26 +08:00
看看服务器那边的日志呢?
flowfire
2015-05-08 12:30:49 +08:00
@feiyuanqiu
代码。。。。我一开始就是嫌太长了所以没发。。。。

<?php
set_time_limit(0);
class ws{
public $sock;
public $socks;
public $users;
function send($socket,$msg){
if($socket==="all"){
foreach ($this->user as $key => $value) {
socket_write($this->user[$key]['client'],$msg,strlen($msg));
}
}
socket_write($socket,$msg,strlen($msg));
}
function handshake($socket,$data){
$secretkey = substr($buffer,strpos($data,'Sec-WebSocket-Key:')+18);
$skey = trim(substr($secretkey,0,strpos($secretkey,"\r\n")));
$newskey = base64_encode(sha1($key."258EAFA5-E914-47DA-95CA-C5AB0DC85B11",true));//生成返回的握手包key,后面的字符串是固定的,不知道谁规定的。。。
$httpheader = "HTTP/1.1 101 Switching Protocols\r\n";
$httpheader .= "Upgrade: websocket\r\n";
$httpheader .= "Connection: Upgrade\r\n";
$httpheader .= "Sec-WebSocket-Accept: ".$newskey."\r\n\r\n";
send($socket,$httpheader);
$key = search($socket);
$this->users[($key-1)]["new"]=false;
return true;
}
function search($socket){
foreach ($this->socks as $key => $value) {
if($socket===$value)
return $key;
}
}
function close($socket){
socket_close($socket);
$key = search($socket);
unset($this->socks[$key]);
if($key!==0)
unset($this->user[($key-1)]);
return true;
}
function __construct($ip,$port){
$this->sock = socket_create(AF_INET,SOCK_STREAM,SOL_TCP);
socket_bind($this->sock,$ip,$port);
socket_listen($this->sock);
$this->socks[] = $this->sock;
while(1){
$sockscache = $this->socks;
socket_select($sockscache,$write = null,$expect =null,null);
foreach($sockscache as $sockcache){
if($sockcache === $this->sock){
$client = socket_accept($this->sock);
$this->socks[] = $client;
$this->users[] = array("client"=>$client,"name"=>"","new"=>true);
}else{
$length = socket_recv($sockcache,$data,2048,0);
$key = search($sockcache);
if($length<7){
$name = $this->users[($key-1)]["name"];
send($sockcache,"$name 已经退出。");
close($sockcache);
}else{
if($this->user[($key-1)]["new"]){
handshake($sockcache,$data);
}else{
//信息处理
echo $data;
die();
}
}
}
}
}
}
}
$websocket = new ws("127.0.0.1","1077");
feiyuanqiu
2015-05-08 16:44:13 +08:00
嗯调了一下,怎么说呢,这个代码里面有很多小问题,比如:
1、类方法调用没有用 this
2、$this->user,$this->users 混乱
3、handshake 方法里面的变量 $buffer 没有声明赋值,实际上它应该是参数 $data
4、handshake 方法里面的 $skey 变量在后面的调用中写错了
...

后面的我都没记录了,都是小问题,改了就能运行了:



附上代码
服务端:
<?php
error_reporting(E_ERROR);
class ws
{
public $sock;
public $socks;
public $users;

function send($socket, $msg)
{
if ($socket === "all") {
foreach ($this->users as $key => $value) {
socket_write($this->users[ $key ]['client'], $msg, strlen($msg));
}
}
socket_write($socket, $msg, strlen($msg));
}

function handshake($socket, $data)
{
$buffer = $data;
$secretkey = substr($buffer, strpos($data, 'Sec-WebSocket-Key:') + 18);
$skey = trim(substr($secretkey, 0, strpos($secretkey, "\r\n")));
//生成返回的握手包key,后面的字符串是固定的,不知道谁规定的。。。
$newskey = base64_encode(sha1($skey . "258EAFA5-E914-47DA-95CA-C5AB0DC85B11", TRUE));
$httpheader = "HTTP/1.1 101 Switching Protocols\r\n";
$httpheader .= "Upgrade: websocket\r\n";
$httpheader .= "Connection: Upgrade\r\n";
$httpheader .= "Sec-WebSocket-Accept: " . $newskey . "\r\n\r\n";
$this->send($socket, $httpheader);
$key = $this->search($socket);
$this->users[$key-1]["new"] = FALSE;

return TRUE;
}

function search($socket)
{
return array_search($socket, $this->socks);
}

function close($socket)
{
socket_close($socket);
$key = $this->search($socket);
unset($this->socks[ $key ]);
if ($key !== 0) {
unset($this->users[$key-1]);
}

return TRUE;
}

function decode($buffer) {
$decoded = null;
$len = ord($buffer[1]) & 127;
if ($len === 126) {
$masks = substr($buffer, 4, 4);
$data = substr($buffer, 8);
} else if ($len === 127) {
$masks = substr($buffer, 10, 4);
$data = substr($buffer, 14);
} else {
$masks = substr($buffer, 2, 4);
$data = substr($buffer, 6);
}
for ($index = 0; $index < strlen($data); $index++) {
$decoded .= $data[$index] ^ $masks[$index % 4];
}
return $decoded;
}

function __construct($ip, $port)
{
f('start...');
$this->sock = socket_create(AF_INET, SOCK_STREAM, SOL_TCP);
f('create socket...');
socket_set_option($this->sock, SOL_SOCKET, SO_REUSEADDR, 1);
f('set option...');
socket_bind($this->sock, $ip, $port);
f('bind socket...');
socket_listen($this->sock);
f('listen socket...');
$this->socks[] = $this->sock;
while (1) {
$write = NULL;
$expect = NULL;
$sockscache = $this->socks;
if (socket_select($sockscache, $write, $expect, 0) === false) {
die("socket_select() failed, reason: " . socket_strerror(socket_last_error()) . "\n");
}

foreach ($sockscache as $sockcache) {
if ($sockcache === $this->sock) {
if (($client = socket_accept($this->sock)) !== false) {
f('accept socket...');
$this->socks[] = $client;
$this->users[] = array("client" => $client, "name" => "", "new" => TRUE);
}
} else {
$length = socket_recv($sockcache, $data, 2048, 0);
f('receive socket...');
$key = $this->search($sockcache);
if ($length < 7) {
$name = $this->users[$key-1]["name"];
$this->send($sockcache, "$name 已经退出。");
$this->close($sockcache);
f('close socket...');
} else {
if ($this->users[$key-1]["new"]) {
$this->handshake($sockcache, $data);
f('handshake...');
} else {
//信息处理
$data = $this->decode($data);
echo $data;
die();
}
}
}
}
usleep(500);
}
}
}

function f($msg = '')
{
static $start = false;
if (!$start) {
ob_start();
$start = true;
}
echo date('Y-m-d H:i:s') . ":{$msg}\n";
ob_flush();
}

$websocket = new ws('127.0.0.1', '10077');


客户端:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Document</title>
</head>
<body>
<script type="text/javascript">
//<![CDATA[
var ws = new WebSocket('ws://localhost:10077/phpstorm/test2.php');

ws.onopen = function (event) {
var data = event.data;
console.log('onopen:', data, event);
};
ws.onerror = function (event) {
var data = event.data;
console.log('onerror:', data, event);
};
ws.onclose = function (event) {
var data = event.data;
console.log('onclose:', data, event);
};
ws.onmessage = function (event) {
var data = event.data;
console.log('onmessage:', data, event);
};
//]]>
</script>
</body>
</html>
flowfire
2015-05-08 18:03:05 +08:00
@feiyuanqiu 我自己再去查一遍好了。。。。
我这两天才刚学websocket。。。。有些太长但是不涉及socket函数的代码基本上都是复制别人的- -
其他的。。。大概是手抖写错了吧。。。
flowfire
2015-05-08 18:17:13 +08:00
@feiyuanqiu 但是我用上别人提供的代码也是403错误啊。。。他说已经测试成功了
flowfire
2015-05-08 18:17:40 +08:00
@ericls 我去找找。。。。服务器日志太长。。。不怎么想看啊。。。
flowfire
2015-05-08 18:32:10 +08:00
@feiyuanqiu 我大概找到原因了。。。。我果然是傻得。。。请求ws连接不能带path。。。。直接ip:portj就好了
flowfire
2015-05-08 18:33:56 +08:00
@flowfire 不对。。。是我端口位置写错了。。。

这是一个专为移动设备优化的页面(即为了让你能够在 Google 搜索结果里秒开这个页面),如果你希望参与 V2EX 社区的讨论,你可以继续到 V2EX 上打开本讨论主题的完整版本。

https://www.v2ex.com/t/189187

V2EX 是创意工作者们的社区,是一个分享自己正在做的有趣事物、交流想法,可以遇见新朋友甚至新机会的地方。

V2EX is a community of developers, designers and creative people.

© 2021 V2EX