Diskuze: WebSocket klient pro python


Jindřich Máca:13.9.2018 2:21
Ahoj, mám na úvod otázku trochu bokem, ale tou "Javou" myslíš
JavaScript?
Autorizaci si musis dopsat sam.
Je to uz nejaky cas, co jsem to googloval. Nevim, zda mam puvodni verzi
programku, ale ta upravena vypada nejak takto, v php.
Funguje to tak, ze mas nekonecny cyklus, ktery reaguje na vsechno, co se mu
posle. Proto je nutne nastavit pro beh programu neomezeny limit. A mel bys tam
mit moznost program zastavit. Jinak ho musis pres procesy zhazovat rucne
Co tam uplne nefungovalo, bylo to maskovani pro delsi zpravy. A taky se mi tam
na portu objevovali jakesi zpravicky, nejspis pingy. Tak jsem to dal
neresil.
<?php
header("Content-Type: text/html; charset=UTF-8");
error_reporting(E_ALL);
//set_time_limit(0); // how long run server, 0 = unlimited
//set_time_limit(20);
//set_time_limit(10);
set_time_limit(5);
ob_implicit_flush();
//$MSG = new class_message;
// ------------
class classChat
{
var $WS;
//var $users_db;
function __construct($ws)
{
$this->WS = $ws;
}
private function status($func_name,$item='')
{
echo '<hr>';
echo '<b>CHAT::'.$func_name.'</b> ';
if ($item!='')
{
var_dump($item);
}
}
public function encode($msg_arr)
{
$msg_str = json_encode($msg_arr,JSON_FORCE_OBJECT);
return $msg_str!==false ? $msg_str : '';
}
public function decode($msg_str)
{
return json_decode($msg_str,true);
}
public function createMessage($ws_user,$type,$text) // $text = string/array
{
$msg_arr = array(
'type' => $type,
'socket' => $ws_user->index,
'text' => $text
);
return $msg_arr;
}
/*
public function createUserList() // $text = string/array
{
$msg_arr = array(
'type' => $type,
'socket' => $ws_user->socket,
'text' => $text
);
return $msg_arr;
}
*/
public function sendOne($msg_arr,$socket=null)
{
$this->status('sendOne');
if ($socket!=null)
{
$this->WS->msgSend($socket,$this->encode($msg_arr));
}
}
public function sendAll($msg_arr,$socket_user=null)
{
$this->status('sendAll');
// $user_list = $this->socketsList($socket);
$socket_all = $this->WS->sockets_db->table;
foreach($socket_all as $socket)
{
if ($socket==$this->WS->master ||
($socket_user!=null && $socket_user==$socket)) // skip user, skip master (mastera filtruje i socketSend)
{
continue;
}
//var_dump($socket);
$this->sendOne($msg_arr,$socket);
}
}
// user messages: text, login, logout, userlist... || now only simple text for all sockets
public function receiveUserMessage($ws_user,$msg_str)
{
$this->status('receiveUserMessage',$msg_str);
$msg_arr = $this->decode($msg_str);
if (!(is_array($msg_arr) && $msg_arr['type'] && isset($msg_arr['text'])))
{
$this->status('receiveUserMessage - not my WS message structure',$msg_arr);
return;
}
if ($msg_arr['text']=='serverstop')
{
$this->WS->serverStop();
}
$msg_arr['type'] = 'msg';
$msg_arr['socket'] = $ws_user->index;
// $this->sendAll($msg,$ws_user->socket);
$this->sendAll($msg_arr);
}
public function receiveControlPing($ws_user,$msg_str)
{
$this->status('receiveControlPing',$msg_str);
$msg_arr = $this->createMessage($ws_user,'ping',$msg_str);
// $this->sendAll($msg,$ws_user->socket);
$this->sendAll($msg_arr);
}
public function receiveControlPong($ws_user,$msg_str)
{
$this->status('receiveControlPong',$msg_str);
$msg_arr = $this->createMessage($ws_user,'pong',$msg_str);
// $this->sendAll($msg,$ws_user->socket);
$this->sendAll($msg_arr);
}
public function receiveControlClose($ws_user,$msg_str)
{
$this->status('receiveControlClose',$msg_str);
$msg_arr = $this->createMessage($ws_user,'close',$msg_str);
// $this->sendAll($msg,$ws_user->socket);
$this->sendAll($msg_arr);
}
public function receiveConnect($ws_user)
{
$this->status('receiveConnect',$ws_user);
$msg_arr = $this->createMessage($ws_user,'connect' ,array(
'ip' => $this->WS->socketIp($ws_user->socket),
));
// $this->sendAll($msg_arr,$ws_user->socket);
$this->sendAll($msg_arr);
}
public function receiveDisConnect($ws_user)
{
$this->status('receiveDisConnect',$ws_user);
$msg_arr = $this->createMessage($ws_user,'disconnect',array(
'ip' => $this->WS->socketIp($ws_user->socket),
));
// $this->sendAll($msg_arr,$ws_user->socket);
$this->sendAll($msg_arr);
}
}
// ------------
// -------------
class classSocket {
var
$index, // unique autoincrement
$socket;
}
class classSocketsDb
{
var $table, $structure, $index_auto;
function __construct($structure)
{
$this->table = array();
$this->structure = $structure;
$this->index_auto = 0;
}
function insert($data)
{
$index = $this->index_auto; // count($this->table);
$this->table[$index] = $data; // uloz jen socket
$new_row = new $this->structure;
$new_row->index = $index;
$new_row->socket = $data;
// $this->table[$index] = $new_row;
$this->index_auto++;
return $new_row;
}
function delete($data)
{
$row = $this->select($data);
if ($row)
{
unset($this->table[$row->index]);
return $row;
}
return false;
}
function select($data)
{
foreach($this->table as $index=>$value)
{
if ($value==$data) // $socket
{
$new_row = new $this->structure;
$new_row->index = $index;
$new_row->socket = $value;
return $new_row;
}
}
return false;
}
}
// -----------------
class classWebSocket
{
public $cfg, $cb, $run, $master, $sockets_db;
function __construct($cfg,$cb=array())
{
$url = "ws://".$cfg['server']['host'].":".$cfg['server']['port']."/demo/".$cfg['server']['page'];//RTC-chat/php-ws
$cfg['server']['url'] = $url;
$this->cfg = $cfg;
$this->cb = $cb;
$this->sockets_db = new classSocketsDb('classSocket');
$this->serverCreate();
}
private function errorAdd($str,$socket)
{
if ($socket)
{
$err_code = socket_last_error($socket);
socket_clear_error($socket);
}
else {
$err_code = socket_last_error();
}
$err_msg = socket_strerror($err_code);
$str = '<b>'.$str.'</b>' . ' Socket error ['.$err_code.'] '.$err_msg.'<br>';
echo $str;
return false;
}
private function status($func_name,$item='')
{
echo '<b>WS::'.$func_name.'</b> ';
if ($item!='')
{
var_dump($item);
}
echo '<hr>';
}
public function runCb($name, $socket, $msg='') // call back function
{
//call_user_func
$this->status('runCb',$name);
if (isset($this->cb[$name]))
{
//echo 1;
$cb = $this->cb[$name];
if (isset($cb[0]) && isset($cb[1]) )
{
//echo 2;
list($obj, $func) = $cb;
//var_dump($obj, $func);
call_user_func_array(array($obj, $func), array($socket, $msg));
//&& is_object($func[0]))
// && is_callable($func[0]->$func[1])
// $cb = $func[0]->$func[1];
// $cb($socket, $msg);
//echo 333;
}
}
}
//Unmask incoming framed message
public function unmask($str)
{
$this->status('unmask');
$byte1 = ord($str[1]);
$masked = $byte1 >> 7;
$length = $byte1 & 127;
if ($masked==1)
{
//var_dump('/masked');
if($length == 126)
{
var_dump('unmask 0');
$mask = substr($str, 4, 4);
$str = substr($str, 8);
}
elseif($length == 127)
{
var_dump('unmask 1');
$mask = substr($str, 10, 4);
$str = substr($str, 14);
}
else {
var_dump('unmask 2');
$mask = substr($str, 2, 4);
$str = substr($str, 6);
}
$out = "";
$i_end = strlen($str);
for ($i = 0; $i < $i_end; ++$i)
{
$out .= $str[$i] ^ $mask[$i % 4];
}
return $out;
}
//var_dump('/not masked');
if ($length == 126)
{
return substr($str, 4);
}
if ($length == 127)
{
return substr($str, 10);
}
return substr($str, 2);
}
//Encode message for transfer to client.
public function mask($str,$masking=true)
{
$this->status('mask');
// server must send non-masked data
$byte0 = 0x80 | (0x1 & 0x0f); // fin + text frame
$length = strlen($str);
if($length <= 125)
$header = pack('CC', $byte0, $length);
elseif($length > 125 && $length < 65536)
$header = pack('CCn', $byte0, 126, $length);
elseif($length >= 65536)
$header = pack('CCNN', $byte0, 127, $length);
return $header.$str;
/*
// masking
$mask = 0;
if ($masking)
{
$mask = array();
$range = range(0x00,0xFF);
$index = array_rand($range,4);
$i_end = 4;
for ($i = 0; $i < $i_end; ++$i)
{
$mask[$i] = $range[$index[$i]];
}
$i_end = strlen($str);
for ($i = 0; $i < $i_end; ++$i)
{
$str[$i] = $str[$i] ^ $mask[$i % 4];
}
$byte1_masking = 0x80; // masking bit
$byte0 = 0x80 | (0x1 & 0x0f); // fin bit + opcode text frame
//var_dump($byte0,$mask);
$length = strlen($str);
if($length <= 125)
$header = pack('CCCCCC', $byte0, $length | $byte1_masking,$mask[0],$mask[1],$mask[2],$mask[3]);
elseif($length > 125 && $length < 65536)
$header = pack('CCnCCCC', $byte0, 126 | $byte1_masking, $length,$mask[0],$mask[1],$mask[2],$mask[3]);
elseif($length >= 65536)
$header = pack('CCNNCCCC', $byte0, 127 | $byte1_masking, $length,$mask[0],$mask[1],$mask[2],$mask[3]);
// return $header.$text;
return $header.$str;
}
*/
}
public function createHandshake($socked, $received_header, $cfg)
{
$this->status('createHandshake','<pre>'.$received_header.'</pre>');
// read header
$headers = array();
$lines = preg_split("/\r\n/", $received_header); //preg_quote // $lines = preg_split("~".$cfg['header_sep']."~", $received_header); //preg_quote
foreach($lines as $line)
{
$line = chop($line);
if (preg_match('/\A(\S+): (.*)\z/', $line, $matches))
{
$headers[$matches[1]] = $matches[2];
}
}
// write header
$sec_key = isset($headers['Sec-WebSocket-Key']) ? $headers['Sec-WebSocket-Key'] : '';
$guid = '258EAFA5-E914-47DA-95CA-C5AB0DC85B11'; // globally unique identifier / RFC 6455 WebSocket Protocol
$accept_key = base64_encode(pack('H*', sha1($sec_key . $guid))); // $accept_key = base64_encode(sha1($sec_key . '258EAFA5-E914-47DA-95CA-C5AB0DC85B11',true));
$upgrade = array(
'HTTP/1.1 101 Web Socket Protocol Handshake',
'Upgrade: websocket',
'Connection: Upgrade',
'Sec-WebSocket-Accept: '.$accept_key,
'WebSocket-Origin: ' .$cfg['server']['host'],
'WebSocket-Location: ' .$cfg['server']['url'],
'',
'' );
$upgrade = implode($cfg['handshake']['sep'], $upgrade);
return $upgrade;
}
public function socketIp($socket)
{
if ($socket)
{
socket_getpeername($socket, $ip) or $this->errorAdd("socket_getpeername()",$socket); //WS
return $ip;
}
return false;
}
public function socketReceive($socket,$buffer)
{
//https://tools.ietf.org/html/rfc6455#section-5.2
$this->status('socketReceive',array($socket)); //,$buffer
$byte0 = ord($buffer[0]);
$op_code = $byte0 & 0x0F;
// $ws_user = new classSocket;
$ws_user = $this->sockets_db->select($socket); // mozna by to chtelo pockat na dalsi cyklus s delete/close
switch ($op_code)
{
case 0x01 :// frame-opcode-non-control: Text frame (10000001):
$this->runCb('receiveUserMessage' ,$ws_user,$this->unmask($buffer));
break;
// other may be not masked?
case 0x08 :// frame-opcode-control Close connection (10001000):
$this->runCb('receiveControlClose',$ws_user,$this->unmask($buffer));
break;
case 0x09 :// frame-opcode-control Ping (10001001):
$this->runCb('receiveControlPing' ,$ws_user,$this->unmask($buffer));
break;
case 0x0A :// frame-opcode-control Pong (10001010):
$this->runCb('receiveControlPong' ,$ws_user,$this->unmask($buffer));
break;
}
return true;
}
public function socketConnect($cfg)
{
$this->status('socketConnect');
$socket = socket_accept($this->master) or $this->errorAdd("socket_accept()"); //accept new socket
if ($socket)
{
$header = socket_read($socket, 1024) or $this->errorAdd("socket_read()",$socket); //read data sent by the socket
$msg = $this->createHandshake($socket, $header, $cfg); //perform websocket handshake
$len = strlen($msg);
$this->socketSend($socket, $msg, $len);
$ws_user = $this->sockets_db->insert($socket);
$this->runCb('receiveConnect',$ws_user);
}
return false;
}
public function socketDisconnect($socket)
{
$this->status('socketDisconnect',$socket);
$ws_user = $this->sockets_db->delete($socket); // mozna by to chtelo pockat na dalsi cyklus s delete/close
$this->runCb('receiveDisConnect',$ws_user);
@socket_close($socket);
}
public function socketDisconnectAll()
{
$this->status('socketDisconnectAll',$socket);
foreach ($this->sockets_db->table as $socket)
{
@socket_close($socket);
}
@socket_close($this->master);
}
public function socketSend($socket=null,$msg_str='',$len=0) // kvuli rychlosti pro cyklus davam $len mimo funkci
{
$this->status('socketSend',array($socket,$msg_str,$len));
if ($socket && $socket!=$this->master)
{
socket_write($socket,$msg_str,$len);
}
}
public function msgSend($socket,$msg_str)
{
$this->status('msgSend',$msg_str);
$msg_str = $this->mask($msg_str);
$len = strlen($msg_str);
$this->socketSend($socket,$msg_str,$len);
}
public function serverCreate()
{
$cfg = $this->cfg['server'];
$socket = socket_create(AF_INET, SOCK_STREAM, SOL_TCP) or $this->errorAdd("socket_create()" ,$socket);
socket_set_option($socket, SOL_SOCKET, SO_REUSEADDR, 1) or $this->errorAdd("socket_set_option()",$socket);
socket_bind($socket, $cfg['host'], $cfg['port']) or $this->errorAdd("socket_bind()" ,$socket);
socket_listen($socket, $cfg['conn_max']) or $this->errorAdd("socket_listen()" ,$socket);
$this->master = $socket;
$this->sockets_db->insert($socket);
//socket_set_nonblock($socket);
$str = "Server Started : ".date('Y-m-d H:i:s')."\n";
$str .= "Master socket : ".$socket."\n";
$str .= "Listening on : address = ".$cfg['host'].", port = ".$cfg['port']."\n";
$str .= $this->cfg['server']['url']."\n\n";
echo nl2br($str);
}
public function serverStop()
{
$this->status('serverStop');
$this->run = false;
}
public function serverRestart()
{
$this->serverStop();
$this->run = true;
while ($this->run)
{
$this->serverRun();
}
}
public function serverRun()
{
$write_null = null;
$except_null = null;
$socket_all = $this->sockets_db->table;
//var_dump($changed);
socket_select($socket_all,$write_null,$except_null,0,10); // read changed sockets to $socket_all
if ($socket_all === false)
{
errorAdd('socket_select()'); return; // error
}
foreach($socket_all as $socket)
{
if ($socket==$this->master)
{
$this->socketConnect($this->cfg);
continue;
}
$bytes = @socket_recv($socket,$buffer,$this->cfg['server']['buffer_max'],0);
if ($bytes === false)
{
$this->errorAdd('bytes==false',$socket);
$this->socketDisconnect($socket); // odpojit socket, kdyz nastane chyba
continue;
}
elseif ($bytes==0)
{
continue;
$user = $this->socketDisconnect($socket);// odpojit socket, kdyz na nej nic nechodi, uzivatel se odpojil
}
$this->run = $this->socketReceive($socket, $buffer);
}
//echo '.';
}
}
$cfg = array(
'server' => array(
'host' => 'localhost', // host
'port' => '9000', // port
'page' => 'ws_server_min.php', // page / program filename
'conn_max' => 20,
'buffer_max' => 2048,
'url' => ''
),
'handshake' => array(
'sep' => PHP_EOL ? PHP_EOL : "\r\n"
)
);
echo '<title>WS server</title>';
$WS = new classWebSocket($cfg);
$CHAT = new classChat($WS);
$WS->cb['receiveUserMessage'] = array($CHAT,'receiveUserMessage');
// other CB not need for chat
$WS->cb['receiveConnect'] = array($CHAT,'receiveConnect');
$WS->cb['receiveDisConnect'] = array($CHAT,'receiveDisConnect');
$WS->cb['receiveControlPing'] = array($CHAT,'receiveControlPing');
$WS->cb['receiveControlPong'] = array($CHAT,'receiveControlPong');
$WS->cb['receiveControlClose'] = array($CHAT,'receiveControlClose');
echo 'Starting...';
$WS->serverRestart();
echo '<br>Stoping...';
$WS->socketDisconnectAll();
$WS->serverStop();
// chyba Trusted = spatny handshake
Js cast vypada nejak takto pro Firefox
// https://github.com/stephenlb/webrtc-sdk/blob/gh-pages/js/webrtc.js
// error 'isTrusted' = not connect / bad handshake
//alert(JSON);
function $(id) {return document.getElementById(id);}
function writeStatus(text) {var o = $("status"); o.innerHTML = (window.performance.now() / 1000).toFixed(3) + ' ' + text + "\n" + o.innerHTML;}
function isObject(data)
{
return typeof data==='object' || typeof(data)==='array';
}
function isSet(obj,index)
{
return (index in obj) || typeof obj.index!=='undefined' ? true : false;
//return true;
}
function render(tpl,data)
{
var i, str;
str = tpl;
for (i in data)
{
str = str.replace('{'+i+'}',data[i]);
}
return str;
}
function fill(value)
{
var form;
form = $('form_login');
form.user_name.value = value;
form.user_psw.value = value;
form = $('form_message');
form.user1.value = value;
}
// ---
var CFG = {};
CFG.ws = {}; // web socket server
CFG.ws.server_url = 'ws://localhost:9000/demo/ws_server_min.php';
CFG.ws.server_opt = null;
CFG.chat = {};
// ---
function classChatMessage()
{
this.type = '';
this.text = '';
this.users = '';
}
function classChatUser()
{
this.socket = '';
this.ip = '';
this.name = '';
this.time = '';
this.status = ''; // from message
}
var CHAT = {};
// private
CHAT.cfg = CFG.chat;
CHAT.obj = {};
CHAT.data = {};
CHAT.func = {};
CHAT.func.status = function(text) {writeStatus('CHAT ' + text);};
CHAT.func.error = function(text) {writeStatus('CHAT error ' + text);};
CHAT.func.encode = function(obj) {return JSON.stringify(obj);}
CHAT.func.decode = function(str) {return JSON.parse(str);}
CHAT.func.construct = function(ws,cfg) // public
{
CHAT.WS = ws;
CHAT.data.userlist = {};
CHAT.obj.form = $('form_message');
CHAT.obj.screen = $("messages");
CHAT.obj.userlist = $("userlist");
}
CHAT.func.findUserData = function(msg_arr)
{
var obj, chat_user;
obj = msg_arr.text;
chat_user = new classChatUser;
chat_user.socket = isSet(msg_arr,'socket') ? msg_arr.socket : '';
chat_user.ip = isSet(obj,'ip') ? obj.ip : '';
chat_user.name = isSet(obj,'name') || (isSet(obj,'users') && isSet(obj.users,0)) ? msg_arr.text.users[0] : '';
chat_user.status = isSet(msg_arr,'type') ? msg_arr.type : '';
chat_user.time = new Date();
return chat_user;
}
CHAT.func.userlist = {};
CHAT.func.userlist.updateUser = function(id,chat_user)
{
var i;
if (!isSet(CHAT.data.userlist,id))
{
CHAT.data.userlist[id] = new classChatUser;
}
for (i in chat_user)
{
value = chat_user[i];
if (value!=='')
{
CHAT.data.userlist[id][i] = value;
}
}
CHAT.func.userlist.show();
}
CHAT.func.userlist.show = function()
{
CHAT.func.status('userlist.show');
var i,j, str, arr, list, list_row, tpl;
tpl = '#{socket} {name} {ip}';
arr = [];
list = CHAT.data.userlist;
for (i in list)
{
arr[arr.length] = render(tpl,list[i]);
}
arr.sort();
str = arr.join("\n");
CHAT.obj.userlist.innerHTML = str;
};
CHAT.func.form = {};
CHAT.func.form = {};
CHAT.func.message = {};
CHAT.func.message.create = function(type,text,users)
{
var new_msg = new classChatMessage;
new_msg.type = type && type!==null ? 'msg' : '';
new_msg.text = text && text!==null ? text : '';
new_msg.users = users && users!==null ? users : '';
return new_msg;
}
CHAT.func.message.screen = function(chat_msg_arr)
{
CHAT.obj.screen.innerHTML = CHAT.func.encode(chat_msg_arr) + CHAT.obj.screen.innerHTML;
}
CHAT.func.message.receive = function(chat_msg_arr)
{
CHAT.func.message.screen(chat_msg_arr);
}
CHAT.func.message.send = function(chat_msg) // chat_msg = object or string
{
var new_msg;
new_msg = new classWsMessage();
new_msg.type = 'msg';
new_msg.text = chat_msg && chat_msg!==null ? chat_msg : '';
CHAT.func.send(new_msg);
}
CHAT.func.receive = function(msg_str)
{
var msg_arr,chat_user;
CHAT.func.status('receive');
msg_arr = CHAT.func.decode(msg_str);
if (!msg_str || msg_str=='' || !isObject(msg_arr))
{
CHAT.func.status('receive - message-unknown');
return;
}
if (!(msg_arr && msg_arr.type && msg_arr.socket && msg_arr.text)) // && isObject(msg.users)
{
CHAT.func.status('receive - message-no-my-structure'); // handshake
CHAT.func.message.screen(msg_str);
return;
}
CHAT.func.status('receive ' + msg_arr.type);
switch (msg_arr.type)
{
case 'msg':
CHAT.func.userlist.updateUser(msg_arr.socket,CHAT.func.findUserData(msg_arr));
CHAT.func.message.receive(msg_arr);
break;
case 'connect':
case 'connected':
case 'disconnect':
CHAT.func.userlist.updateUser(msg_arr.socket,CHAT.func.findUserData(msg_arr));
CHAT.func.message.screen(msg_arr);
break;
case 'ping':
case 'pong':
case 'close': // connect close
CHAT.func.message.screen(msg_arr);
break;
default: break;
}
}
CHAT.func.connectionOpen = function() // send user name to all
{
var form, new_chat_msg;
form = CHAT.obj.form;
new_chat_msg = CHAT.func.message.create(
'connected',
'',
[form['user1'].value]
);
CHAT.func.message.send(new_chat_msg)
form.message.focus();
}
CHAT.func.send = function(msg_arr)
{
if (msg_arr.type=='' || msg_arr.text=='')
{return;}
CHAT.WS.cmd.send(CHAT.func.encode(msg_arr));
}
CHAT.func.formSend = function()
{
var form, new_chat_msg;
form = CHAT.obj.form;
new_chat_msg = CHAT.func.message.create(
'msg',
form['message'].value,
[form['user1'].value,form['user2'].value]
);
CHAT.func.message.send(new_chat_msg);
form.message.focus();
}
CHAT.func.formRead = function(event)
{
var form, key;
event = event || window.event;
key = event.keyCode || event.which || event.charCode || event.code;
if (key==13) // enter key
{CHAT.func.formSend();}
}
CHAT.func.sendServerStop = function(msg_arr)
{
CHAT.func.message.send('serverstop'); // my stop php web-socket server
}
// public
CHAT.cmd = {};
CHAT.cmd.formSend = CHAT.func.formSend;
CHAT.cmd.formRead = CHAT.func.formRead;
CHAT.cmd.sendServerStop = CHAT.func.sendServerStop;
// ---
function classTimer()
{
var that = this;
this.timer = null;
this.interval;
this.repeat_cycles;
this.repeat_index;
this.callback = null;
this.construct = function(func,inverval,repeat)
{
that.timer = null;
that.interval = inverval ? inverval : 150;
that.repeat_cycles = repeat ? repeat : 0;
that.callback = {
'open' : func['open' ] ? func['open' ] : function(){},
'run' : func['run' ] ? func['run' ] : function(){},
'close': func['close'] ? func['close'] : function(){}
}
}
this.open = function()
{
if (that.timer==null)
{
that.callback.open();
that.repeat_index = that.repeat_cycles;
that.timer = setInterval(that.run,that.interval);
return true;
}
return false;
}
this.run = function()
{
that.callback.run();
if (that.repeat_cycles>0)
{
that.repeat_index--;
if (that.repeat_index==0)
{
that.close();
}
}
}
this.close = function()
{
if (that.timer!=null)
{
clearInterval(that.timer);
that.timer = null;
that.callback.close();
return true;
}
return false;
}
}
function classWsMessage()
{
this.type = null;
this.text = null;
}
var WS;
//
// init
WS = {}
// private
WS.cfg = {}
WS.api = window.WebSocket;
WS.data = {}
WS.func = {};
WS.func.status = function(text) {writeStatus('WS ' + text);};
WS.func.error = function(text) {writeStatus('WS error ' + text);};
WS.func.construct = function(cfg) // public
{
WS.cfg.server_url = cfg.server_url ? cfg.server_url : '';
WS.cfg.server_opt = cfg.server_opt ? cfg.server_opt : null;
WS.data.cb = {}; // call back functions / CHAT
WS.data.conn = null;
WS.data.close_timer = new classTimer();
WS.data.close_timer.construct({'run':WS.func.connCloser},500,5);
WS.data.close_notclosed = false;
WS.data.send_timer = new classTimer();
WS.data.send_timer.construct({'run':WS.func.msgSender},150,5);
WS.data.send_buff = [];
};
WS.func.onopen = function(event)
{
WS.func.status('onopen');
// WS.func.msgSendTimer();
WS.func.callCb('connectionOpen');
};
WS.func.onclose = function(event)
{
WS.func.status('onclose');
WS.data.close_notclosed = false;
WS.func.callCb('connectionClose');
};
WS.func.onerror = function(event)
{
event = event || window.event;
WS.func.error(JSON.stringify(event.data || event));
};
WS.func.onmessage = function(event)
{
WS.func.status('onmessage - receive');
var msg_str;
event = event || window.event;
// msg = event && event.data ? (event.data!='' ? WS.decode(event.data) : '') : '';
msg_str = event && event.data ? event.data : '';
WS.func.callCb('receive',msg_str);
// WS.cb.receive(msg)
// CHAT.receive(msg);
};
WS.func.callCb = function(name,data)
{
//alert(WS.data.cb.toSource());
if (WS.data.cb[name])
{
WS.data.cb[name](data);
}
}
WS.func.msgSendTimer = function(msg)
{
if (WS.conn && WS.conn.readyState==1)
{
WS.status('msgSendTimer - send');
WS.conn.send(msg);
return;
}
WS.data.send_buff[WS.data.send_buff.length] = msg; // add new message to buffer
WS.data.send_timer.open();
WS.func.callCb('send',msg);
}
WS.func.msgSender = function()
{
var i, i_end, buff;
if (WS.data.conn)
{
if (WS.data.conn.readyState==1)
{
buff = WS.data.send_buff;
i_end = buff.length;
for (i=0;i<i_end;i++)
{
WS.func.status('msgSender - send');
if (buff[i]!='')
{
WS.data.conn.send(buff[i]);
}
}
if (WS.data.send_buff.length!=buff.length) // skip, if added new message
{
return;
}
WS.data.send_buff = []; // clear, if all sended
WS.data.send_timer.close();
}
WS.func.status('msgSender - waiting to send');
return;
}
WS.func.status('msgSender - not connection');
}
WS.func.connCloseTimer = function()
{
if (WS.data.close_notclosed)
{
WS.func.status('connCloseTimer');
WS.data.close_timer.open();
}
}
WS.func.connCloser = function()
{
if (WS.data.conn)
{
if (WS.data.conn.readyState==1)
{
WS.func.status('connCloser - close');
WS.data.conn.close();
WS.data.close_notclosed = false;
return;
}
WS.func.status('connCloser - waititng to close');
WS.data.close_notclosed = true;
return;
}
WS.data.close_notclosed = false;
WS.func.status('connCloser - not connection');
WS.data.close_timer.close();
}
WS.func.connect = function()
{
var conn;
WS.func.connCloseTimer();
WS.func.status('connect');
// WS.conn = new WS.api(WS.server_url+'?'+query, WS.server_opt); // + CFG.server_ex.room
conn = new WS.api(WS.cfg.server_url, WS.cfg.server_opt); // + CFG.server_ex.room
conn.onopen = WS.func.onopen; // connection open
conn.onclose = WS.func.onclose; // connection close
conn.onerror = WS.func.onerror;
conn.onmessage = WS.func.onmessage; // receive message
WS.data.conn = conn;
};
WS.func.support = function()
{
var api;
api = 'WebSocked';
if (WS.api) {WS.func.status(api+' supported');} else {WS.func.error(api+' not supported');}
api = 'JSON';
if (JSON) {WS.func.status(api+' supported');} else {WS.func.error(api+' not supported');}
}
// public
WS.cmd = {};
WS.cmd.support = function() {WS.func.support();}
WS.cmd.connect = function() {WS.func.connect();}
WS.cmd.disconnect = function() {WS.func.connCloseTimer();}
WS.cmd.send = function(msg_str) {WS.func.msgSendTimer(msg_str);}
//PEERS.construct();
$('server').value = CFG.ws.server_url;
//$('server_data').value = CFG.server_data.url;
////WS.cmd.support();
////RTC.cmd.support();
WS.func.construct(CFG.ws);
CHAT.func.construct(WS,CFG.chat);
WS.data.cb['receive'] = CHAT.func.receive;
WS.data.cb['connectionOpen'] = CHAT.func.connectionOpen;
fill('peter');
WS.cmd.connect();
//WS.cmd.disconnect();
Tohle by mohl byt puvodni php kod, asi, mozna jen mirne upravyny
<title>WS server</title>
<?php
//$MSG = new class_message;
class class_room
{
private $id, $name, $desc, $users;
private function msgMessage($data)
{
var_dump('msgMessage',$data);
if (is_object($data))
{
$msg = array(
'type' => 'msguser',
'room' => isset($data->room) ? $data->room : '', //property_exists(), array_key_exists()
'name1' => isset($data->name1) ? $data->name1 : '',
'name2' => isset($data->name2) ? $data->name2 : '',
'message' => isset($data->message) ? $data->message : '',
);
send_message(mask(json_encode($msg)));
}
}
private function msgConnect($ip)
{
var_dump('msgConnect',$ip);
$msg = array(
'type' => 'msgsystem',
'key' => 'userjoin',
// 'room' => 'msgsystem',
'ip' => $ip
); //prepare json data
send_message(mask(json_encode($msg))); //notify all users about new connection
}
private function msgDisconnect($ip)
{
var_dump('msgDisconnect',$ip);
$msg = array(
'type' => 'msgsystem',
'key' => 'userleave',
// 'room' => 'msgsystem',
'ip' => $ip
);
send_message(mask(json_encode($msg)));
}
public function __construct($id=0,$name='',$desc='')
{
if ($name=='') {$name = $id;}
$this->id = $id;
$this->name = $name;
$this->desc = $desc;
$this->users = array();
}
public function join($ip,$name='?')
{
//if (isset($this->users[$name]))
// {
// }
//$this->users[$name] = $name;
$this->msgConnect($ip);
}
public function leave($ip)
{
//unset($this->users[$name]);
$this->msgDisconnect($ip);
}
public function message($msg)
{
$this->msgMessage($msg);
}
public function users()
{
return $this->users;
}
}
$ROOM = new class_room(1,'room1','bez popisu');
function send_message($msg)
{
global $clients;
foreach($clients as $changed_socket)
{
@socket_write($changed_socket,$msg,strlen($msg));
}
return true;
}
//Unmask incoming framed message
function unmask($text) {
$length = ord($text[1]) & 127;
if($length == 126) {
$masks = substr($text, 4, 4);
$data = substr($text, 8);
}
elseif($length == 127) {
$masks = substr($text, 10, 4);
$data = substr($text, 14);
}
else {
$masks = substr($text, 2, 4);
$data = substr($text, 6);
}
$text = "";
for ($i = 0; $i < strlen($data); ++$i) {
$text .= $data[$i] ^ $masks[$i%4];
}
return $text;
}
//Encode message for transfer to client.
function mask($text)
{
$b1 = 0x80 | (0x1 & 0x0f);
$length = strlen($text);
if($length <= 125)
$header = pack('CC', $b1, $length);
elseif($length > 125 && $length < 65536)
$header = pack('CCn', $b1, 126, $length);
elseif($length >= 65536)
$header = pack('CCNN', $b1, 127, $length);
return $header.$text;
}
//handshake new client.
function perform_handshaking($receved_header,$client_conn, $cfg)
{
$headers = array();
$lines = preg_split("/\r\n/", $receved_header);
foreach($lines as $line)
{
$line = chop($line);
if(preg_match('/\A(\S+): (.*)\z/', $line, $matches))
{
$headers[$matches[1]] = $matches[2];
}
}
// $secKey = $headers['Sec-WebSocket-Key'];
$secKey = isset($headers['Sec-WebSocket-Key']) ? $headers['Sec-WebSocket-Key'] : '';
$secAccept = base64_encode(pack('H*', sha1($secKey . '258EAFA5-E914-47DA-95CA-C5AB0DC85B11')));
//hand shaking header
$upgrade = array(
"HTTP/1.1 101 Web Socket Protocol Handshake",
"Upgrade: websocket",
"Connection: Upgrade",
"WebSocket-Origin: ".$cfg['host'],
"WebSocket-Location: ".$cfg['server_url'],
"Sec-WebSocket-Accept:$secAccept",
"",
"" );
$upgrade = implode($cfg['header_sep'], $upgrade);
socket_write($client_conn,$upgrade,strlen($upgrade));
}
//set_time_limit(0); // how long run server
//set_time_limit(120); // how long run server
set_time_limit(5); // how long run server
$cfg = array(
'host' => 'localhost', //host
'port' => '9000', //port
'null' => NULL, //null var
'header_sep' => PHP_EOL ? PHP_EOL : "\r\n"
);
$cfg['server_url'] = "ws://".$cfg['host'].":".$cfg['port']."/demo/ws_server.php";//RTC-chat/php-ws
//echo bin2hex($header_sep[0]).bin2hex($header_sep[1]);
//Create TCP/IP sream socket
$socket = socket_create(AF_INET, SOCK_STREAM, SOL_TCP);
socket_set_option($socket, SOL_SOCKET, SO_REUSEADDR, 1);//reuseable port
socket_bind($socket, 0, $cfg['port']);//bind socket to specified host
socket_listen($socket);//listen to port
$clients = array($socket);//create & add listning socket to the list
//start endless loop, so that our script doesn't stop
while (true) {
//manage multipal connections
$changed = $clients;
//returns the socket resources in $changed array
socket_select($changed, $null, $null, 0, 10);
//check for new socket
if (in_array($socket, $changed)) {
$socket_new = socket_accept($socket); //accpet new socket
$clients[] = $socket_new; //add socket to client array
$header = socket_read($socket_new, 1024); //read data sent by the socket
perform_handshaking($header, $socket_new, $cfg); //perform websocket handshake
socket_getpeername($socket_new, $ip); //get ip address of connected socket
// $response = mask(json_encode(array('type'=>'system', 'message'=>$ip.' connected'))); //prepare json data
// send_message($response); //notify all users about new connection
$ROOM->join($ip);
//make room for new socket
$found_socket = array_search($socket, $changed);
unset($changed[$found_socket]);
}
//loop through all connected sockets
foreach ($changed as $changed_socket) {
//check for any incomming data
while(socket_recv($changed_socket, $buf, 1024, 0) >= 1)
{
$received_text = unmask($buf); //unmask data
$msg = json_decode($received_text); //json decode
$ROOM->message($msg);
break 2; //exist this loop
}
$buf = @socket_read($changed_socket, 1024, PHP_NORMAL_READ);
if ($buf === false) { // check disconnected client
// remove client for $clients array
$found_socket = array_search($changed_socket, $clients);
socket_getpeername($changed_socket, $ip);
unset($clients[$found_socket]);
//notify all users about disconnected connection
// $response = mask(json_encode(array('type'=>'system', 'message'=>$ip.' disconnected')));
// send_message($response);
$ROOM->leave($ip);
}
}
}
// close the listening socket
socket_close($socket);
js, ktery to obsluhuje by mel vypadat podobne jako v dokumentaci. A nevim,
zda to nefunguje jen ve firefoxu. Ale nejspis by to slo obsluhovat normalne pres
ajax.
https://developer.mozilla.org/…PI/WebSocket
// Create WebSocket connection.
const socket = new WebSocket('ws://localhost:8080');
// Connection opened
socket.addEventListener('open', function (event) {
socket.send('Hello Server!');
});
// Listen for messages
socket.addEventListener('message', function (event) {
console.log('Message from server ', event.data);
});
Jo, jeste, jak to funguje.
Server
- spustis si nekonecnou smycku na konkretnim portu
- smycka posloucha, co se na portu sustne. Obvykle je to ping nebo je tak novy user a nebo se zasila zprava. Musis zjistit, co to je a podle toho zareagovat.
-- ping - muzes ignorovat nebo zareagovat pigme tomu, kdo pingal
-- novy user - pridat sho do tabulky socketu a posles mu hanshake a on ti na nej
odpovi, pokud odpovi spravne, tak s nim navazes komunikaci, pokud ne, ukoncis s
nim komunikaci, odstranis z tabulky
-- zprava, zjistis si socket usera, zpravu desifrujes (unmasky) a zareagujes,
posles treba na vsechny sockety
(Pozor, prvni socket je pro server, tak tam mu nic posilat nemusis. S timhle
socketem vsichni komunikuji)
A to je cele.
Pokud chces registraci, autentizaci, musis si to tam implementovat. Tohle
resi jen vytvareni a ukoncovni komunikace.
A protoze to bezi neustale, tak by bylo dobre mit tam moznost poslat na socket
serveru zpravu, kterou to ukoncis. Pokud ne, tak treba u win to musis zhodit z
procesu, jinak xamp server odmita spustit novy server se stejnymi nastavenimi
portu a tak
To maskovani se tam dela kvuli poruchovosti signalu xorovanim. Bezny texh
obsahuje spoustu nul a to maskovani by melo zajistit vetsi stridani nul a
jednicek, aby to hw mohl spravne dekodovat na nuly a jednicky. Cili, neni tam
implemnetovane ani sifrovani zprav. To si proste musi zaridit user.
Na klienske casti, js/html to funguje tak, ze se ohlasis serveru.
Server ti posle hanshake.
Ty ho nejak zakodujes podle pravidel web-socket komunikace a posles zpet.
A server ti odpovi, zda prijal, nebo dostanes odpoved, ze tvuj socket byl
odpojen.
Pokud je vse ok, muzes na socket zasilat zpravy a server je nebude
zahazovat.
WebSocket.open - otevres spojeni se serverem
WebSocket.onopen - odpovis na handshake, pripadne muzes hned posilat zpravu
WebSocket.send - odesles message
WebSocket.onmessage - prijeti zpravy
WebSocket.close - odpojeni
Spoustis to opakovane pres interval, kontrolujes socket. Kdyz dlouho nereagujes, server te odpoji. Takze bys mel treba cas od casu poslat prazdnou zpravu.
Karel Panský:13.9.2018 8:47
Děkuji za odpovědi, ale to opět není to, co potřebuji. Odesílání i
příjem zpráv ve Vašich případech opět probíhá pomocí stejné url.
Jinak opravdu mi jde o spojení Python - Java, jde o zařízení, které je
naprogramované v pythonu a potřebuje na dálku komunikovat pomocí WebSocketu
se serverem napsaným v Javě. Vím, opravdu neobvyklé spojení, proto jsem
taky skončil tady.
Jinak s WebSocketem nejsem žádný nováček. Spojení klient server (JavaScript, Java) je v pohodě. Na problém jsem narazil až mezi těmito jazyky
Aha, tak jestli to cte moderator, tak sim, at odstrani vsechen ten php kod.
Jsem netusil, ze to vlozi bez rolovani cele Snad si priste vzpomenu a dam kod
na externi server, radeji.
Mi slo hlavne o popsani principu. Kdezto to vypada, ze chces kompletni reseni bez prace. Jestli to php vubec nejak funguje, tak by nemel byt problem neco podobneho napsat v pythonu.
Tak nakonec jsem to musel obejít tak, že jsem napsal server i klienta v pythonu a výpočetní logiku předávám přes kafku do java aplikace. Jiný řešení jsem nenašel.
+5 Zkušeností

Zobrazeno 9 zpráv z 9.