I want to make a socket connection using JavaScript and PHP.
I use JS WebSocket and PHP Socket.
If I run PHP scripts locally on the server, then they communicate with each other and the connection occurs (screenshot 1).
After the script ends on the server, the socket is not immediately available (screenshot 2).
If I open a client script in a browser using WebSocket, then the response is fixed in an incomprehensible encoding and it is not constant in the text, the only thing you can see on it is only the domain name from which the request came (screenshot 3).
Port: 3000, on 80 and 443, socket not pinged.
Question:
What is the reason for the response from the browser in an incomprehensible encoding and is it necessary to pre-prepare the header text in JS for the handshake? I read on this forum that only the protocol can be optionally specified in WebSocket([url], [protocol]).
Thanks in advance for your advice.
Examples:
Answer:
▒▒▒▒▒2B▒~▒˼[▒▒p'▒w▒
▒%▒▒c▒/ e▒O▒&ߍ▒▒L▒}▒▒4▒▒W▒▒▒4▒▒.▒▒ ▒▒̨̩▒+▒/▒,▒0▒▒▒▒/5▒▒▒domain.name▒
▒▒
# 3+)/▒ ▒~l▒se▒▒lxk▒aG?i▒i0
;m^eCG▒▒jI-+JJ
ih2**▒
============================
Server code:
<?php
// set some variables
$url_site = 'domain.name';
$host = gethostbyname($url_site);
$port = getservbyname('https', 'tcp');
$port = 3000;
// don't timeout!
set_time_limit(0);
$flag_handshake = false;
$client = null;
header("Content-Type:application/javascript; charset=utf-8");
// create socket
$socket = socket_create(AF_INET, SOCK_STREAM, SOL_TCP) or die("Could not create socket\n");
echo serialize($socket)." socket_create...\n\n";
// bind socket to port
$result = socket_bind($socket, $host, $port) or die("Could not bind to socket\n");
echo serialize($socket)." socket_bind...\n\n";
// start listening for connections
$result = socket_listen($socket, 0) or die("Could not set up socket listener\n");
echo serialize($socket)." socket_listen...\n\n";
echo "Ready to receive on wss://$host:$port/ ...\n";
do {
if (!$client) {
// accept incoming connections
// client another socket to handle communication
$client = socket_accept($socket) or die("Could not accept incoming connection\n");
echo "Client << $client >> has connected.\n";
}
$bytes = #socket_recv($client, $data, 2048, 0);
if ($flag_handshake == false) {
if ((int)$bytes == 0)
continue;
print("Handshaking headers from client: ". mb_detect_encoding($data) ." - ". $data ."\n");
echo $client ." -->>LEN<<-- ". $bytes;
if (handshake($client, $data, $socket)) {
$flag_handshake = true;
}
}
elseif($flag_handshake == true) {
if ($data != "") {
$decoded_data = unmask($data);
print("< ". $decoded_data ."\n");
$response = strrev($decoded_data);
socket_write($client, encode($response));
print("> ". $response ."\n");
socket_close($client);
$client = null;
$flag_handshake = false;
}
}
} while (true);
// close sockets
socket_close($client);
socket_close($socket);
function handshake($client, $headers, $socket) {
print(" [ HEADER: " . $headers ." ] ");
if (preg_match("/Sec-WebSocket-Version: (.*)\r\n/", $headers, $match)) {
$version = $match[1];
//$version = 333;
} else {
print(" [ The client doesn't support WebSocket ] ");
return false;
//$version = 111;
}
if ($version == 13) {
// Extract header variables
if (preg_match("/GET (.*) HTTP/", $headers, $match))
$root = $match[1];
if (preg_match("/Host: (.*)\r\n/", $headers, $match))
$host = $match[1];
if (preg_match("/Origin: (.*)\r\n/", $headers, $match))
$origin = $match[1];
if (preg_match("/Sec-WebSocket-Key: (.*)\r\n/", $headers, $match))
$key = $match[1];
$acceptKey = $key.'258EAFA5-E914-47DA-95CA-C5AB0DC85B11';
$acceptKey = base64_encode(sha1($acceptKey, true));
$upgrade = "HTTP/1.1 101 Switching Protocols\r\n".
"Upgrade: websocket\r\n".
"Connection: Upgrade\r\n".
"Sec-WebSocket-Accept: $acceptKey".
"\r\n\r\n";
socket_write($client, $upgrade);
return true;
} else {
print("WebSocket version 13 required (the client supports version {$version})");
return false;
}
}
function unmask($payload) {
$length = ord($payload[1]) & 127;
if ($length == 126) {
$masks = substr($payload, 4, 4);
$data = substr($payload, 8);
}
elseif($length == 127) {
$masks = substr($payload, 10, 4);
$data = substr($payload, 14);
}
else {
$masks = substr($payload, 2, 4);
$data = substr($payload, 6);
}
$text = '';
for ($i = 0; $i < strlen($data); ++$i) {
$text .= $data[$i] ^ $masks[$i % 4];
}
return $text;
}
function encode($text) {
$b1 = 0x80 | (0x1 & 0x0f);
$length = strlen($text);
if ($length <= 125)
$header = pack('CC', $b1, $length);
elseif($length > 125 && $length < 65536)$header = pack('CCS', $b1, 126, $length);
elseif($length >= 65536)
$header = pack('CCN', $b1, 127, $length);
return $header.$text;
}
?>
============================
Client code:
<html>
<head>
<meta charset="UTF-8">
</head>
<script>
var socket = new WebSocket('wss://domain.name:3000/');
console.log('New socket ');
console.log(socket);
console.log('Let`s open ');
// Open the socket
socket.onopen = function(event) {
console.log(event);
var msg = 'I am the client.';
console.log('> ' + msg);
// Send an initial message
socket.send(msg);
// Listen for messages
socket.onmessage = function(event) {
console.log('< ' + event.data);
};
// Listen for socket closes
socket.onclose = function(event) {
console.log('Client notified socket has closed', event);
};
// To close the socket....
socket.close()
};
socket.onmessage = function(event) {
console.log(`[message] Data received from server: ${event.data}`);
};
socket.onclose = function(event) {
if (event.wasClean) {
console.log(`[close] Connection closed cleanly, code=${event.code} reason=${event.reason}`);
} else {
console.log('[close] Connection died');
}
};
socket.onerror = function(error) {
console.log(`[error] ${error.message}`);
};
</script>
<body>
<p>Please check the console log of your browser.</p>
</body>
</html>
============================
Sockets seem complex. I just found https://github.com/ghedipunk/PHP-Websockets written in 2012 and got it to work with PHP 8.1 this week.
It seems to work well. Though I haven't really used it to send and receive yet other than tests. Was planning to have the login open a socket and wait for the email confirmation. I even built a way to check the processes for www-data and shell_exec(PHP -q the socket.phpsee img below) to keep the socket open)
May the holy force be with you!
Related
I am working on a free PHP socket testing playground. I am trying to send and receive a close control frame. I would like to trigger a automatic re-connection when the socket closes abnormally, which is anything other than 1000.
My front-end code looks like this:
$socket.close(1000, "Disconnect");
The back-end response I've coded looks like this:
socket_close($client);
On the client, listening on the socket close event ($event.code), I get a status of 1006. It should have been 1000. The code 1006 means the socket_close($client) did not emit a close frame.
Documentation sources for the client:
https://javascript.info/websocket
https://www.rfc-editor.org/rfc/rfc6455#section-7.4.1
For the back-end I am using PHP socket API:
https://www.php.net/manual/en/book.sockets.php
Here's a simpler version of my code. There's the HTML structure, the JS client program, and the PHP CLI backend:
1- HTML
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name='viewport' content='width=device-width, initial-scale=1'>
<meta name='description' content='How to build a heavy-duty web socket server using PHP socket API'>
<meta name='keywords' content='PHP Web Socket Server, Web Socket API, Object Oriented PHP'>
<title>Building a Heavy-Duty Web Socket Server in PHP</title>
<script src="https://code.jquery.com/jquery-3.5.1.min.js" integrity="sha256-9/aliU8dGd2tb6OSsuzixeV4y/faTqgFtohetphbbj0=" crossorigin="anonymous"></script>
<script async defer src="client.js"></script>
<style>
html,
body{
font-family: 'Courier New', Courier, monospace;
font-size: 14px;
}
#wrapper,
#socketLoader{
margin: 0 auto;
}
#wrapper{
max-width: 50em;
}
#socketLoader{
width: 80%;
margin-top: 1em;
}
h1,
#commandCenter{
text-align: center;
}
.open{
color: hsl(218, 90%, 61%);
}
.message{
color: hsl(141, 65%, 41%);
}
.close{
color: hsl(35, 96%, 35%);
}
.error{
color: hsl(0, 91%, 55%);
}
</style>
</head>
<body>
<div id="wrapper">
<h1>HTML5 Client and PHP Socket Server</h1>
<div id="webSocketContainer">
<div id="commandCenter">
<button id="socketMsg">Send Socket Message</button>
<button id="closeBtn">Disconnect</button>
<button id="startBtn">Connect</button>
</div>
<ol id="socketLoader"></ol>
</div>
</div>
<div id="loader" class="loader"></div>
</body>
</html>
2- The JS client
class SocketClient {
// Properties
newResults = [];
// Methods
constructor() {
this.sConnect();
}
sConnect () { // Connect using sockets $protocol is the equivalent of $type in connect()
// Variables
const $rTl = 300; // Response time limit
const $reconnect = 10; // The number of reconnections
let $i = 0;
let $state = 'closing';
// Methods
function connectToServer(){
const $address = "ws://localhost:9000";
let $socket = new WebSocket($address);
$socket.binaryType = "blob";
return $socket;
}
function updateStatus ($status) { // Update the status of a socket connection
switch($status){
case 0:
$state = 'connecting';
break;
case 1:
$state = 'open';
break;
case 2:
$state = 'closing';
break;
case 3:
$state = 'closed';
break;
}
}
function render ($data, $type) {
const $commandPattern = /^\~\w+\~$/g;
if($data.match($commandPattern) == null){
$type = (typeof $type !== 'undefined') ? "class = '"+ $type + "'" : '';
$("#socketLoader").append("<li " + $type + ">" + $data + "</li>");
}
else{
$("#socketLoader").append("<li " + $type + "><em>Performing application command</em></li>");
}
}
function sendData ($data) { // Send data in pieces if necessary
$socket.send($data);
let $count = 0;
const $checkMsg = setInterval(() => { // Keep checking if the message was sent
if ($socket.bufferedAmount == 0 && $state !== 'open'){ // The data was sent
render("Data sent ✓", "open");
clearInterval($checkMsg);
}
else if($count >= $reconnect){ // Stop trying to reconnect after several attempts
clearInterval($checkMsg);
}
else { // Unsubmitted data queue in the buffer
if($state !== 'open'){ // Check if the connection was not closed (is open)
$socket.send($data);
render("<em>resending</em> The client did not reflect the data being sent", "error");
}
}
$count++;
}, $rTl);
}
function closeControlFrame ($message) { // Determine if the server requested to expire the handshake
if($message === '~closehandshake~'){
$socket.close(1000, "Work complete");
}
}
function socketOpen (){
updateStatus($socket.readyState);
render("Connected", "open");
render("Sending data on load", "open");
sendData(">>> This message was sent by the client on load via web socket");
}
function socketMessage ($event) {
if($state !== 'close'){
updateStatus($socket.readyState);
render("Incoming", "message");
render($event.data, "server message");
}
else{
render('The server is offline x_x', 'error');
}
closeControlFrame($event.data);
}
function socketClose ($event) {
let $errorMsg = "";
const $closeMsg = "The socket connection is closed. ";
render($closeMsg, "close");
updateStatus($socket.readyState);
switch ($event.code) {
case 1000:
$errorMsg += "Internal application request";
break;
case 1001:
$errorMsg += "User left or server is down";
break;
case 1002:
$errorMsg += "Protocol error";
break;
case 1003:
$errorMsg += "Wrong data type";
break;
case 1005:
$errorMsg += "Unknown status code";
break;
case 1006:
$errorMsg += "Without sending or receiving a close control frame";
break;
case 1007:
$errorMsg += "Inconsistent data type";
break;
case 1008:
$errorMsg += "Policy breach";
break;
case 1009:
$errorMsg += "Data length exceeds threshold";
break;
case 1010:
$errorMsg += "Handshake error";
break;
case 1011:
$errorMsg += "Internal server error";
break;
case 1012:
$errorMsg += "Server is restarting";
break;
case 1013:
$errorMsg += "Server overload";
break;
case 1014:
$errorMsg += "Bad gateway";
break;
case 1015:
$errorMsg += "TLS handshake error";
break;
}
if($event.wasClean == false){
render("Error " + $event.code + ". Reason: <em>" + $errorMsg + "</em>", "error");
console.warn(">>> Full Reason: ", $event);
}
}
function socketError ($event) {
/**
* Handle errors in almost the same way as in on socket close
*/
updateStatus($socket.readyState);
const $errMsg = (typeof $event.message != 'undefined') ? ': [' + $event.message + ']' : '.';
render("There was an error" + $errMsg, "error");
console.error($event);
}
function socketMsg () {
if($state != 'close'){
let $message = ">>> Message No. " + ($i + 1);
render("The user requested data");
render("(client) Sending : " + $message);
sendData($message);
$i++;
}
else{
render("Your socket server instance is offline.", "close");
}
}
function closeBtn (){
sendData("~shutdown~");
render("Shutting down...");
}
function startBtn (){
$socket = connectToServer();
$socket.onopen = socketOpen;
$socket.onmessage = socketMessage;
$socket.onclose = socketClose;
$socket.onerror = socketError;
}
// Runtime operations
render("Initialization");
// Socket event based operations
let $socket = connectToServer();
$socket.onopen = socketOpen;
$socket.onmessage = socketMessage;
$socket.onclose = socketClose;
$socket.onerror = socketError;
// Application event based operations
$("#socketMsg").on("click", socketMsg);
$("#closeBtn").on("click", closeBtn);
$("#startBtn").on("click", startBtn);
}
};
$(function(){ // Init
new SocketClient();
});
3- The PHP CLI
<?php
error_reporting(E_ALL);
set_time_limit(0);
ob_implicit_flush();
class Socket_Server {
/**
* An initial socket connection may be longer than expected.
* Every other requests should be fast.
*/
// Properties
private $telnet = false;
private $open = false; // Server status (find something using a method for this)
private $time_limit = 0;
private $address = '127.0.0.1';
private $broadcast = 0; // Allow UDP broadcasts
private $port = 9000;
private $socket;
private $header;
private $level = SOL_SOCKET;
private $option_name = SO_REUSEADDR;
private $receiving = SO_RCVTIMEO;
private $sending = SO_SNDTIMEO;
private $seconds = 0;
private $micro_seconds = 800;
private $duration = array('sec' => 0, 'usec' => 800);
private $option_value = 1;
private $length = 634;
private $read_binary = PHP_BINARY_READ; // Safe binary data
private $read_php = PHP_NORMAL_READ;
private $accept; // accept incoming connections on socket instance
private $request;
private $request_check;
private $receive_mode = MSG_PEEK; // Receive data from the beginning of the receive queue without removing it from the queue.
private $domain = AF_INET; // IPv4
private $type = SOCK_STREAM; // Full-duplex
private $protocol = SOL_TCP; // TCP/UDP
private $backlog = 25; // Incoming connection queue
private $clients = array(); // Accept multiple clients
private $sockets = array();
private $blacklist = NULL; // To see if a write will not block
private $whitelist = NULL; // Exceptions
private $timeout = 0; // Watch timeout
// Methods
private function create () {
$socket = socket_create($this->domain, $this->type, $this->protocol);
return $socket;
}
private function bind () { // Bind a name to a socket
return socket_bind($this->socket, $this->broadcast, $this->port);
}
private function listen () {
return socket_listen($this->socket, $this->backlog);
}
private function deliver ($message, $client = false, $format = false) { // Push data to the client
if(isset($message) && !empty($message)){
if (!$this->telnet && $format !== 'text'){
$message = $this->seal($message);
}
if($client && socket_write($client, $message, strlen($message))){
return $this->open = true;
}
else if(socket_write($this->accept, $message, strlen($message))){
return $this->open = true; // If the socket can write, it is open
}
else{
return $this->open = false;
}
}
}
/**
* Dynamically scale the length of the read() and socket_recv()
*/
private function read ($socket) { // Maximum bytes from socket
if(isset($socket)){
return trim(socket_read($socket, $this->length, $this->read_binary));
}
else{
return false;
}
}
private function read_client_response ($client, $debug = false) { // Return the full client response string/object
if(isset($client)){
$html_client_response;
$results_length = socket_recv($client, $results, $this->length, $this->receive_mode);
if($debug == true){
$this->response("> results (length) : " . $results_length);
}
if($results_length == 0){
return false;
}
return $this->unseal($results); // Binary data is expected by default
}
else{
return false;
}
}
private function format_header () { // Format an HTTP response header
$header = array();
$data = preg_split("/\r\n/", $this->header);
foreach($data as $datum) {
$datum = chop($datum);
if(preg_match('/\A(\S+): (.*)\z/', $datum, $matches)) {
$header[$matches[1]] = $matches[2];
}
}
return $header;
}
private function set_header(){ // Use the Sever class to build this dynamically
$nL = "\r\n"; // New line delimiter
$header = $this->format_header();
$secKey = isset($header['Sec-WebSocket-Key']) ? $header['Sec-WebSocket-Key'] : '';
$secAccept = base64_encode(pack('H*', sha1($secKey . '258EAFA5-E914-47DA-95CA-C5AB0DC85B11')));
$data = 'HTTP/1.1 101 Web Socket Protocol Handshake' . $nL;
$data .= 'Upgrade: websocket' . $nL;
$data .= 'Connection: Upgrade' . $nL;
$data .= 'WebSocket-Origin: ' . $this->address . $nL;
$data .= 'WebSocket-Location: ws://'. $this->address. ':' . $this->port. $nL;
$data .= 'Sec-WebSocket-Accept: ' . $secAccept . $nL . $nL;
$this->deliver($data, $this->accept, 'text');
return $data;
}
private function set_close_control_frame(){ // Use the Sever class to build this dynamically
$nL = "\r\n"; // New line delimiter
$header = $this->format_header();
$secKey = isset($header['Sec-WebSocket-Key']) ? $header['Sec-WebSocket-Key'] : '';
$secAccept = base64_encode(pack('H*', sha1($secKey . '258EAFA5-E914-47DA-95CA-C5AB0DC85B11')));
$data = 'HTTP/1.1 101 Web Socket Protocol Handshake' . $nL;
$data .= 'Upgrade: websocket' . $nL;
$data .= 'Connection: Upgrade' . $nL;
$data .= 'WebSocket-Origin: ' . $this->address . $nL;
$data .= 'WebSocket-Location: ws://'. $this->address. ':' . $this->port. $nL;
$data .= 'Expires: Fri, 20 Oct 2000 10:00:00 GMT' . $nL;
$data .= 'Sec-WebSocket-Accept: ' . $secAccept . $nL . $nL;
$this->deliver($data, $this->accept, 'text');
return $data;
}
private function response ($object) { // Output to server
echo $object . "\r\n";
}
private function close ($socket) {
socket_close($socket);
return $this->open = false;
}
private function switch_to () { // Register multiple sockets to allow a multi-client connection
$this->sockets[] = $this->socket;
$this->sockets = array_merge($this->sockets, $this->clients);
}
private function options () { // Reuse previously created socket connection
$options = array ();
$options['receiving'] = socket_set_option($this->socket, $this->level, $this->receiving, $this->duration);
$options['sending'] = socket_set_option($this->socket, $this->level, $this->sending, $this->duration);
$options['reusable'] = socket_set_option($this->socket, $this->level, $this->option_name, $this->option_value);
return $options['reusable'];
}
private function watch () {
return (socket_select(
$this->sockets,
$this->blacklist,
$this->whitelist,
$this->timeout
) < 1);
}
private function clients_no () { // Count the total number of clients
if(isset($this->clients) && isset($this->accept)){
return array_keys($this->clients, $this->accept)[0];
}
return 0;
}
private function unseal ($socket_data) { // Nagle's algorithm for decoding
if(isset($socket_data) && isset($socket_data[1])){
$results = ""; // The string to hold the decoded packets
$length = ord($socket_data[1]) & 127; // Return the ASCII value
if($length == 126) {
$masks = substr($socket_data, 4, 4);
$data = substr($socket_data, 8);
}
else if($length == 127) {
$masks = substr($socket_data, 10, 4);
$data = substr($socket_data, 14);
}
else {
$masks = substr($socket_data, 2, 4);
$data = substr($socket_data, 6);
}
for ($i = 0; $i < strlen($data); ++$i) {
$results .= $data[$i] ^ $masks[$i%4];
}
return $results;
}
return false;
}
private function seal($socket_data) { // Nagle's algorithm for encoding
if($socket_data){
$b1 = 0x80 | (0x1 & 0x0f); // binary string
$length = strlen($socket_data);
if($length <= 125) {
$header = pack('CC', $b1, $length);
}
else if($length > 125 && $length < 65536) {
$header = pack('CCn', $b1, 126, $length);
}
else if($length >= 65536) {
$header = pack('CCNN', $b1, 127, $length);
}
return $header.$socket_data;
}
return false;
}
private function unregister_sockets ($key, $client) {
unset($this->clients[$key]); // Un-register the client
$skey = array_search($this->socket, $this->sockets); // Find the socket the watch array
unset($this->sockets[$skey]); // Un-register the socket watch
$this->close($client);
}
// Wrapper methods
private function run ($property, $message = false) { // Run a specific function
if($property === false){
$this->display_error_message($message);
return true;
}
return false;
}
private function display_error_message ($message) {
$this->response($message . ' ' . socket_strerror(socket_last_error()));
}
// Constructor
function __construct ( ) {
$this->socket = $this->create();
$this->run($this->socket, 'socket_create() failed: reason: ');
$this->run($this->options(), 'socket_set_option() failed: reason ');
$this->run($this->bind(), 'socket_bind() failed: reason: ');
$this->run($this->listen(), 'socket_listen() failed: reason: ');
$this->response(">>> Web socket server initiated"); //////
do {
$this->switch_to();
if($this->watch()){
continue;
}
if(in_array($this->socket, $this->sockets)){ // Handle new connections
$this->response(">>> Handling new connections"); //////
$this->accept = socket_accept($this->socket);
$this->run($this->accept, 'socket_accept() failed: reason: ');
$this->clients[] = $this->accept; // Register the client
$this->header = $this->read($this->accept);
$this->run($this->header, 'The socket header failed: reason: ');
$this->run($this->set_header(), 'The socket header could not be set: reason: ');
/**
* For Telnet tests
*/
if($this->telnet){
$key = array_keys($this->clients, $this->accept);
$this->deliver('Client No. '. ($key[0] + 1));
}
}
foreach($this->clients as $key => $client){
$this->response(">>> For each client"); //////
if(in_array($client, $this->sockets)){
$this->request_check = $this->read_client_response($client, true); // Read the corresponding client
$this->request = $this->read($client); // Read the corresponding client in binary
$this->response(">>> in a watch list"); //////
switch ($this->request_check){
// Get the appropriate message on failure
case $this->run($this->accept, 'socket_read() failed: reason: '):
$this->response("The socket connection accept failed");
case !$this->request_check:
$this->response("The socket connection read failed");
case '':
$this->response("The socket disconnected");
// Perform those actions on failure
case $this->run($this->accept, 'socket_read() failed: reason: '):
case !$this->request_check:
case '':
$this->unregister_sockets($key, $client);
break;
case '~shutdown~':
$this->deliver('~closehandshake~');
$this->unregister_sockets ($key, $client);
break;
// On success
default:
$client_no = $key + 1;
/**
* For Telnet tests
*/
if($this->telnet){
$this->deliver("PHP > Client ID {$client_no} said '$this->request_check'.\n", $client); // Write to specific client
}
else{
$message = '(server) Client ID: ' . $client_no . ' <blockquote>' . $this->request_check . '.' . "\r\n"; // Message to deliver to client
$message .= 'Total number of connections: ' . count($this->clients) . '</blockquote>' . "\r\n";
$this->deliver($message, $client); // This method should be deliver()
}
break;
}
}
}
} while (true);
$this->close($this->socket);
}
}
new Socket_Server();
?>
I tried to change the behavior of the socket server by adding socket_shutdown($client,2 ) and option rows with SO_LINGER and SO_KEEPALIVE (in case the close connection framed needed time), and also tried PHP stream functions like stream_socket_shutdown.
PHP Stream Functions: https://www.php.net/manual/en/ref.stream.php
When the client issued the '~shutdown~' command to the server, I had to emit '~closehandshake~' back to the front-end, then from there, I used the switch ($event.code) to check for if($event.data === '~closehandshake~') which bypasses the 1006 case unless the server never sent it.
The simpler way to check if the $event.data received the custom 'close control frame' i.e. '~closehandshake~' is to update the updateStatus method, and allow it to store custom states like this:
function updateStatus ($status) { // Update the status of a socket connection
switch($status){
case 0:
$state = 'connecting';
break;
case 1:
$state = 'open';
break;
case 2:
$state = 'closing';
break;
case 3:
$state = 'closed';
break;
default: // Typically a user defined status
$state = $status;
break;
}
}
From there you can set a status in the $state variable like this:
function closeControlFrame ($event) { // Determine if the server requested to expire the handshake
if($event.data === '~closehandshake~'){
updateStatus("cleanclosure"); // Confirm a clean close control frame using a custom code
$socket.close(1000, "Work complete");
}
}
Then use the $state variable to check on the status:
if($state !== "cleanclosure"){
switch ($event.code) {
// The rest of the cases should be here as well
}
}
I am trying to integrate paypal payment on a sales site. the client part works well, the client pays and the money is in the seller account. the second part which allows to recover data such as transaction number, amount, customer address, does not work. yet I use the scripts provided by Paypal.
this file index.php to pay works
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"
"http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<title>Radiofil</title>
<link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/4.0.0-beta/css/bootstrap.min.css"
integrity="sha384-/Y6pD6FV/Vv2HJnA6t+vslU6fwYXjCFtcEpHbNJ0lyAFsXTsjBbfaDjzALeQsN6M"
crossorigin="anonymous">
</head>
<body>
<nav class="navbar navbar-expand-lg navbar-dark bg-primary mb-1">
<a class="navbar-brand" href="#">Boutique Tubes</a>
</nav>
<div class="container">
<h1>Récapitulatif</h1>
<table class="table table-striped">
<thead>
<tr>
<th>Tube</th>
<th>Qte</th>
<th>Prix</th>
</tr>
</thead>
<tbody>
<?php
$user_id = 8000;
$RefBonCommande= '20000';
include('../config/dbconn.php');
$query = mysqli_query($dbconn,"SELECT * FROM detailcde WHERE user_id='$user_id' AND RefBonCommande='$RefBonCommande'") or die (mysqli_error());
while ($row3 = mysqli_fetch_array($query, MYSQLI_NUM)) {
$count = mysqli_num_rows($query);
$numerotube=$row3[3];
$qty= $row3[4];
$totalp = $row3[5];
echo "<tr>";
echo "<td>".$numerotube."</td>";
echo "<td>".$qty."</td>";
echo "<td>".$totalp.'€'."</td>";
}
$query1 = mysqli_query($dbconn,"SELECT * FROM commandes WHERE Refcde='$RefBonCommande' AND adherent1='$user_id'") or die (mysqli_error());
$row4 = mysqli_fetch_array($query1);
$port=$row4['FraisTransport'];
$total=$row4['Total'];
echo "<tr>";
echo "<td>".'Total + port '."</td>";
echo "<td>".'1 '."</td>";
echo "<td>".$total.'€'."</td>";
echo "<tr>";
echo "<td><b><font size = 5>".'Commande de l\'adherent RFL n° '.$user_id."</b></br></td>";
?>
</tbody>
</table>
<!--<p class="text-right">
<div id="paypal-button"></div>
<!--Acheter-->
</p>
</div><!-- /.container -->
<script
src="https://www.paypal.com/sdk/js?client-id=client-id¤cy=EUR"> // Required. Replace SB_CLIENT_ID with your sandbox client ID.
paypal.Buttons().render('#paypal-button-container');
// This function displays Smart Payment Buttons on your web page.
</script>
<center>
<div id="paypal-button-container"></div>
</center>
<script>
paypal.Buttons({
createOrder: function(data, actions) {
return actions.order.create({
purchase_units: [{
amount: {
value: <?php echo $total ;?> //'5.60'
}
}]
});
},
onApprove: function(data, actions) {
return actions.order.capture().then(function(details) {
alert('Transaction completed by adherent ' + details.payer.name.given_name);
// Call your server to save the transaction
return fetch('/paypal-transaction-complete', {
method: 'post',
headers: {
'content-type': 'application/json'
},
body: JSON.stringify({
orderID: data.orderID,
payerID: data.payerID
})
});
});
}
}).render('#paypal-button-container');
</script>
</body>
</html>
**the script below does not work**
<!-- language: lang-PHP -->
<?php
require 'vendor/autoload.php';
?>
<script>
// 1. Set up your server to make calls to PayPal
// 1a. Add your client ID and secret
PAYPAL_CLIENT = cliet id';
PAYPAL_SECRET = secret';
// 1b. Point your server to the PayPal API
PAYPAL_OAUTH_API = 'https://api.sandbox.paypal.com/v1/oauth2/token/';
PAYPAL_ORDER_API = 'https://api.sandbox.paypal.com/v2/checkout/orders/';
// 1c. Get an access token from the PayPal API
basicAuth = base64encode(`${ PAYPAL_CLIENT }:${ PAYPAL_SECRET }`);
auth = http.post(PAYPAL_OAUTH_API ,{
headers: {
Accept: `application/json`,
Authorization: `Basic ${ basicAuth }`
},
data: `grant_type=client_credentials`
});
// 2. Set up your server to receive a call from the client
function handleRequest(request, response), {
// 2a. Get the order ID from the request body
orderID = request.body.orderID;
// 3. Call PayPal to capture the order
capture = http.post(PAYPAL_ORDER_API + orderID + '/capture', {
headers: {
Accept: `application/json`,
Authorization: `Bearer ${ auth.access_token }`
}
});
// 4. Save the capture ID to your database
if (!capture.error) {
captureID = capture.purchase_units[0]
.payments.captures[0].id;
database.saveCaptureID(captureID);
}
// 5. Handle any errors from the call
if (capture.error) {
console.error(capture.error);
return response.send(500);
}
// 6. Return a successful response to the client
response.send(200);
}
hello, according to the advice of PHPology I set up the return call URL and of course used the PAYPAL script. I went to the history of IPN messages and I have an HTTP 500 error. I think there may be a syntax error in the PAYPAL script they are specialists in this kind of error.
<?php
function ipn($ipn_data) {
define('SSL_P_URL', 'https://www.paypal.com/cgi-bin/webscr');
define('SSL_SAND_URL', 'https://www.sandbox.paypal.com/cgi-bin/webscr');
$hostname = gethostbyaddr($_SERVER['REMOTE_ADDR']);
if (!preg_match('/paypal\.com$/', $hostname)) {
$ipn_status = 'Validation post isn\'t from PayPal';
if ($ipn_data == true) {
//You can send email as well
}
return false;
}
// parse the paypal URL
$paypal_url = ($_REQUEST['test_ipn'] == 1) ? SSL_SAND_URL : SSL_P_URL;
$url_parsed = parse_url($paypal_url);
$post_string = '';
foreach ($_REQUEST as $field => $value) {
$post_string .= $field . '=' . urlencode(stripslashes($value)) . '&';
}
$post_string.="cmd=_notify-validate"; // append ipn command
// get the correct paypal url to post request to
$paypal_mode_status = $ipn_data; //get_option('im_sabdbox_mode');
if ($paypal_mode_status == true)
$fp = fsockopen('ssl://www.sandbox.paypal.com', "443", $err_num, $err_str, 60);
else
$fp = fsockopen('ssl://www.sandbox.paypal.com', "443", $err_num, $err_str, 60);
$ipn_response = '';
if (!$fp) {
// could not open the connection. If loggin is on, the error message
// will be in the log.
$ipn_status = "fsockopen error no. $err_num: $err_str";
if ($ipn_data == true) {
echo 'fsockopen fail';
}
return false;
} else {
// Post the data back to paypal
fputs($fp, "POST $url_parsed[path] HTTP/1.1\r\n");
fputs($fp, "Host: $url_parsed[host]\r\n");
fputs($fp, "Content-type: application/x-www-form-urlencoded\r\n");
fputs($fp, "Content-length: " . strlen($post_string) . "\r\n");
fputs($fp, "Connection: close\r\n\r\n");
fputs($fp, $post_string . "\r\n\r\n");
// loop through the response from the server and append to variable
while (!feof($fp)) {
$ipn_response .= fgets($fp, 1024);
}
fclose($fp); // close connection
}
// Invalid IPN transaction. Check the $ipn_status and log for details.
if (!preg_match("/VERIFIED/s", $ipn_response)) {
$ipn_status = 'IPN Validation Failed';
if ($ipn_data == true) {
echo 'Validation fail';
print_r($_REQUEST);
}
return false;
} else {
$ipn_status = "IPN VERIFIED";
if ($ipn_data == true) {
echo 'SUCCESS';
print_r($_REQUEST);
}
return true;
}
}
function ipn_response() {
//mail("sobhagya1411#gmail.com","My subject",print_r($request,true));
$ipn_data = true;
if ($this->ipn($_REQUEST)) {
$this->insert_data($_REQUEST);
}
}
function issetCheck($post, $key) {
if (isset($post[$key])) {
$return = $post[$key];
} else {
$return = '';
}
return $return;
}
public function insert_data(){
$post = $_REQUEST;
$item_name= $this->issetCheck($post, 'item_name');
$amount = $this->issetCheck($post, 'mc_gross');
$currency = $this->issetCheck($post, 'mc_currency');
$payer_email = $this->issetCheck($post, 'payer_email');
$first_name = $this->issetCheck($post, 'first_name');
$last_name = $this->issetCheck($post, 'last_name');
$country = $this->issetCheck($post, 'residence_country');
$txn_id = $this->issetCheck($post, 'txn_id');
$txn_type = $this->issetCheck($post, 'txn_type');
$payment_status = $this->issetCheck($post, 'payment_status');
$payment_type = $this->issetCheck($post, 'payment_type');
$payer_id = $this->issetCheck($post, 'payer_id');
$create_date = date('Y-m-d H:i:s');
$payment_date = date('Y-m-d H:i:s');
$paypal_sql = "INSERT INTO ipn_data_tbl (item_name,payer_email,first_name,last_name,amount,currency,country,txn_id,txn_type,payer_id,payment_status,payment_type,create_date,payment_date)
VALUES ($item_name,'$payer_email','$first_name','$last_name','$amount','$currency','$country','$txn_id','$txn_type','$payer_id','$payment_status','$payment_type','$create_date','$payment_date')";
mysql_query($paypal_sql);
}
?>
I solve the problem. Paypal provides scripts that do not work because there are syntax errors. Their explanation of the methods is very obscure. Finally after making some syntax and url modification my php IPN paypal IPN listener software works.
<?php
$charset = "utf-8";
include('../config/dbconn.php');
$deb='je commence';
mysqli_query($dbconn,"INSERT INTO paytest (mess) VALUES ('$deb')");
/* NE RIEN MODIFIER CI-DESSOUS */
// lecture du post de PayPal et ajout de 'cmd'
$req = 'cmd=_notify-validate';
foreach ($_POST as $key => $value) {
$value = trim(urlencode(stripslashes($value)));
$req .= "&$key=$value";
}
//mysqli_query($dbconn,"INSERT INTO paytest (mess) VALUES ('$req')");
// reponse a PayPal pour validation
$header .= "POST /cgi-bin/webscr HTTP/1.1\r\n";
//$header .= "Content-Length: 1028\r\n";
$header .= "Content-Length: " . strlen($req) . "\r\n";
$header .= "Content-Type: application/x-www-form-urlencoded\r\n";
$header .= "Host: www.sandbox.paypal.com\r\n";
$header .= "Connection: close\r\n\r\n";
//$header .= "Host: ipnpb.paypal.com:443\r\n";
$fp = fsockopen ('ssl://www.sandbox.paypal.com', 443, $errno, $errstr, 30);
//$fp = fsockopen ('ipnpb.paypal.com', 443, $errno, $errstr, 30);
mysqli_query($dbconn,"INSERT INTO paytest (mess) VALUES ('fp1')");
mysqli_query($dbconn,"INSERT INTO paytest (mess) VALUES ('$fp')");
// variables
$discount = $_POST['discount'];
$insurance_amount = $_POST['insurance_amount'];
$payer_id = $_POST['payer_id'];
$address_country_code = $_POST['address_country_code'];
$ipn_track_id = $_POST['ipn_track_id'];
$address_zip = $_POST['address_zip'];
$charset = $_POST['charset'];
$payment_gross = $_POST['payment_gross'];
$address_status = $_POST['address_status'];
$shipping_discount = $_POST['shipping_discount'];
$address_street = $_POST['address_street'];
$verify_sign = $_POST['verify_sign'];
$item_name = $_POST['item_name'];
$test_ipn = $_POST['test_ipn'];
$txn_type = $_POST['txn_type'];
$receiver_id = $_POST['receiver_id'];
$payment_fee = $_POST['payment_fee'];
$mc_currency = $_POST['mc_currency'];
$transaction_subject = $_POST['transaction_subject'];
$shipping_method = $_POST['shipping_method'];
$custom = $_POST['custom'];
$protection_eligibility = $_POST['protection_eligibility'];
$address_country = $_POST['address_country'];
$payer_status = $_POST['payer_status'];
$first_name = $_POST['first_name'];
$address_name = $_POST['address_name'];
$mc_gross = $_POST['mc_gross'];
$payment_date = $_POST['payment_date'];
$payment_status = $_POST['payment_status'];
$quantity = $_POST['quantity'];
$business = $_POST['business'];
$item_number = $_POST['item_number'];
$last_name = $_POST['last_name'];
$address_state = $_POST['address_state'];
$txn_id = $_POST['txn_id'];
$mc_fee = $_POST['mc_fee'];
$payment_type = $_POST['payment_type'];
$notify_version = $_POST['notify_version'];
$payer_email = $_POST['payer_email'];
$receiver_email = $_POST['receiver_email'];
$address_city = $_POST['address_city'];
$residence_country = $_POST['residence_country'];
$payment_currency = $_POST['mc_currency'];
$receiver_id = $_POST['receiver_id'];
$num_cart_items = $_POST['num_cart_items'];
mysqli_query($dbconn,"INSERT INTO paytest (mess) VALUES ('$txn_id')");
mysqli_query($dbconn,"INSERT INTO paytest (mess) VALUES ('Date')");
mysqli_query($dbconn,"INSERT INTO paytest (mess) VALUES ('$payment_date')");
$rest = substr($payment_date, 9, 13);
$mois = substr($rest, 0, 3);
$jour = substr($rest, 4, 2);
$an = substr($rest, 8, 4);
$date = $an.'-'.$mois.'-'.$jour;
$date = date_create($date);
$dte = date_format($date,'Y-m-d');
if (!$fp) {
// HTTP ERROR
} else {
fputs ($fp, $header . $req);
fseek($fp, 0);
while (!feof($fp)) {
$res = fgets ($fp, 10024);
//$test=strcmp (trim($res), "VERIFIED");
//if (strcmp ($res, "VERIFIED") == 0) {
if (strcmp (trim($res), "VERIFIED") == 0) {
$deba = 'verifie';
mysqli_query($dbconn,"INSERT INTO paytest (mess) VALUES ('$deba')");
// Envoi du mail
$paypal_sql = "INSERT INTO paiements (payment_id,payment_status,payment_amount,taxe_paypal,payment_currency,payment_date,payer_email)
VALUES ('$txn_id','$payment_status','$mc_gross','$mc_fee','$mc_currency','$dte','$payer_email')";
mysqli_query($dbconn,$paypal_sql);
}
else if (strcmp (trim($res), "INVALID") == 0) {
$debb = 'invalide';
mysqli_query($dbconn,"INSERT INTO paytest (mess) VALUES ('$debb')");
$payment_status = 'INVALID';
$paypal_sql = "INSERT INTO paiements (payment_id,payment_status,payment_amount,taxe_paypal,payment_currency,payment_date,payer_email)
VALUES ('$txn_id','$payment_status','$mc_gross','$mc_fee','$mc_currency','$dte','$payer_email')";
mysqli_query($dbconn,$paypal_sql);
}
}
fclose ($fp);
}
$debc = 'fini';
mysqli_query($dbconn,"INSERT INTO paytest (mess) VALUES ('$debc')");
?>
I want to send a query result from one php file to my javascript, I've used an AJAX which seems to work as it is getting data from my BaseClass.php. However when using JSON_encode, it is not outputting anything at all. So I can't work out how to send a query result from one php file(MySQLDao.php) to my BaseClass.php so I am then able to send it to my Javascript file.
My Code:
BaseClass.php:
<?php
error_reporting(E_ALL); ini_set('display_errors', 1);
require("Conn.php");
require("MySQLDao.php");
//$param=$_REQUEST['action'];
//echo json_encode($_GET);
//echo var_dump(json_encode($_GET));
$handle = fopen("php://input", "rb");
$param = $_REQUEST['action'];
while (!feof($handle)) {
$param .= fread($handle, 8192);
}
fclose($handle);
if (empty($param))
{
$returnValue["status"] = false;
$returnValue["title"] = "Error";
$returnValue["message"] = "No Data Recieved paige" .$param ."...";
echo json_encode($returnValue);
return;
}
else
{
$dao = new MySQLDao();
if ($dao->openConnection() == false)
{
$returnValue["status"] = false;
$returnValue["title"] = "Error";
$returnValue["message"] = "Connection Could Not Be Established Between Server And Database";
ob_clean();
echo json_encode($returnValue);
}
else
{
//Decodes data, dont change
$body = json_decode($param, true);
$recieved = $body["data"];
//Gets the result of a query
$result = $dao->getResults($recieved);
}
$dao->closeConnection();
//Return the result of the query
ob_clean();
echo json_encode("param" .$param);
echo json_encode("body" .$body);
echo json_encode("recieved" .$recieved);
echo json_encode("result" .$result);
exit();
}
?>
output for the above echo statements:
"paramgetResults""body""recieved""result"
MySQLDao.php - this file holds the query result that I want to pass to my js
<?php
error_reporting(E_ALL); ini_set('display_errors', 1);
//Class for holding queries
class MySQLDao
{
var $dbhost = null;
var $dbuser = null;
var $dbpass = null;
var $mysqli = null;
var $dbname = null;
var $result = null;
//constructor
function __construct()
{
$this->dbhost = Conn::$dbhost;
$this->dbuser = Conn::$dbuser;
$this->dbpass = Conn::$dbpass;
$this->dbname = Conn::$dbname;
}
//Attempt a connection to the database
public function openConnection()
{
//Try and connect to the database
$this->mysqli = new mysqli($this->dbhost, $this->dbuser, $this->dbpass, $this->dbname);
//If the connection threw an error, report it
if (mysqli_connect_errno())
{
return false;
}
else
{
return true;
}
}
//Get method for retrieving the database conection
public function getConnection()
{
return $this->mysqli;
}
//Close the connection to the database
public function closeConnection()
{
//If there is a connection to the database then close it
if ($this->mysqli != null)
$this->mysqli->close();
}
//-----------------------------------QUERY METHODS-------------------------------------
public function getResults()
{
$sql = "SELECT room.room_description FROM room WHERE room.room_id = 1";
$result = $this->mysqli->query($sql);
//if (mysql_num_rows($result) == 1) {
// $obj = mysql_fetch_object($result, 'obResults');
// echo($obj);
// return $obj;
//}
echo json_encode($result);
//echo($result);
//return false;
}
}
?>
My AJAX code in my js file:
$.ajax ({
type: "GET",
datatype: "application/json",
url: "BaseClass.php",
data: { action : 'getResults' },
//error: function(err){console.log(err)},
success: function(output) {
console.log(output);
//alert(output);
}
//error, function(err){console.log(err)}
});
Any help is appreciated thanks!
Im using PHP socket and im trying to connect it using js .
my JS code is :
var socket = new WebSocket("ws://localhost:9090/");
setTimeout(
socket.onopen = function()
{
try {
socket.send('\n');
}catch (e){
alert(e);
}
try {
socket.send('{"link":"1"}');
}catch (e){
alert(e);
}
alert("Connected");
// Web Socket is connected, send data using send()
}
, 5000);
And PHP code is :
$host = '127.0.0.1';
$port = '9090';
$null = NULL;
include_once ("TransmissionRPC.php");
$rpc = new TransmissionRPC();
$rpc->return_as_array = true;
$socket = socket_create(AF_INET, SOCK_STREAM, SOL_TCP);
socket_set_option($socket, SOL_SOCKET, SO_REUSEADDR, 1);
socket_bind($socket, 0, $port);
socket_listen($socket);
socket_set_block($socket);
$clients = array($socket);
while(true){
$changed = $clients;
socket_select($changed, $null, $null, 0, 10);
if (in_array($socket, $changed)){
$socket_new = socket_accept($socket);
$clients[] = $socket_new;
print_r($clients);
$header = socket_read($socket_new, 1024);
perform_handshaking($header, $socket_new, $host, $port);
socket_getpeername($socket_new, $ip);
//make room for new socket
$found_socket = array_search($socket, $changed);
unset($changed[$found_socket]);
}
foreach($changed as $changed_socket){
while($input = socket_read($changed_socket, 1024)){
print_r($input);
$received_text = trim($input);
$recived = json_decode($received_text);
if (array_key_exists('session', $recived)){
$session_id = $recived->{'session'};
$info = $rpc->get($session_id, array());
send_message(json_encode($info), 1);
} else if(array_key_exists('link', $recived)) {
$link = $recived->{'link'};
$result = $rpc->sstats();
$test_torrent = "http://www.slackware.com/torrents/slackware64-13.1-install-dvd.torrent";
$result = $rpc->add( $test_torrent, '/home/ftpacc/domains/ali.com/public_html/' );
send_message(json_encode($result), 1);
}
break 2;
}
$buf = #socket_read($changed_socket, 1024, PHP_NORMAL_READ);
if ($buf === false) {
$found_socket = array_search($changed_socket, $clients);
socket_getpeername($changed_socket, $ip);
unset($clients[$found_socket]);
$response = mask(json_encode(array('type' => 'system', 'message' => $ip . ' disconnected \n')));
echo "DC";
}
}
}
function perform_handshaking($receved_header, $client_conn, $host, $port)
{
$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];
}
}
}
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;
}
function send_message($msg, $reciver_id)
{
global $clients;
#socket_send($clients[$reciver_id] , $msg , strlen($msg) , 0);
var_dump($clients);
return true;
}
When im using socket.send('{"link":"1"}'); not working .
Its give me this error :
InvalidStateError: An attempt was made to use an object that is not, or is no longer, usable
How can i fix this problem ?
Notice : Its working when im connecting to socket via TELNET .
First if you could tell me if there is something here that is wrong :
client:
var ws = new WebSocket('ws://localhost:9090/websocket_server.php');// ws://echo.websocket.org/echo
console.log(ws);
ws.onopen = function(e) {
console.log("Connection open...", e);
ws.send("Hello WebSocket!");
};
ws.onmessage = function(e) {
if(typeof e.data === "string"){
console.log("String message received", e, e.data);
} else {
console.log("Other message received", e, e.data);
}
};
ws.onerror = function(e) {
console.log("WebSocket Error: " , e);
};
ws.onclose = function(e) {
console.log("Connection closed", e);
};
server:
<?php
defined('KEY_SUFFIX') ? null : define('KEY_SUFFIX', "258EAFA5-E914-47DA-95CA-C5AB0DC85B11");
error_reporting(E_ALL);
set_time_limit(0);
ob_implicit_flush();
$sock = socket_create(AF_INET, SOCK_STREAM, SOL_TCP);
socket_bind($sock, 'localhost', 9090);
socket_listen($sock);
while (true) {
$client = socket_accept($sock) or die('socket_accept returned false');;
//$buf = socket_read($client, 1024);
$buf = null;$key=null;
while ( $line = socket_read($client, 2048, PHP_NORMAL_READ) ) {
$buf .= $line;
if ( strpos($line, 'Sec-WebSocket-Key')!== false ) {
$key = substr($line, 19);
} else
if ( strpos($line, 'User-Agent')!== false ) {
break;
}
}
//echo $buf;
$sha1 = SHA1($key.KEY_SUFFIX, true);
$accept = base64_encode($sha1);
$write = "HTTP/1.1 101 Switching Protocols\n";
$write .= "Upgrade: websocket\n";
$write .= "connection: Upgrade\n";
$write .= "Sec-Websocket-Accept: $accept\n";
//$write .= "Sec-Websocket-Extensions: extension\n";
socket_write( $client, $write, strlen($write) );
}
socket_close($sock);
?>
I run the php script in this way:
F:\xampp\php\php -q D:\websocket_server.php
Question:
Suppose if It's all correct, I keep looking at chrome debugger network section and It's pending and I expected that after this process I should see an onopen event in js console,
what is going on? Am I expecting wrong?
Should the connection between sever and client go from 0 state to 1 state after this process or It needs more work to establish an open state?
I found the problems:
mistakes:
1: reading the last character in the line of the sec-websocket-key which made the calculation of the sec-websocket-accpet wrong on line 18
2: Didn't know 2 things about response header : one is that you need to put \r\n at the end of each line not just \n and seccond is that you need to put two \r\n\r\n after the last line of header, it just doesn't work if it's not two. (lines: 30-33)
Now it initiates an onopen event in client.
<?php
defined('KEY_SUFFIX') ? null : define('KEY_SUFFIX', "258EAFA5-E914-47DA-95CA-C5AB0DC85B11");
error_reporting(E_ALL);
set_time_limit(0);
ob_implicit_flush();
$sock = socket_create(AF_INET, SOCK_STREAM, SOL_TCP);
socket_bind($sock, 'localhost', 9090);
socket_listen($sock);
while (true) {
$client = socket_accept($sock) or die('socket_accept returned false');;
//$buf = socket_read($client, 1024);
$buf = null;$key=null;
while ( $line = socket_read($client, 2048, PHP_NORMAL_READ) ) {
$buf .= $line;
if ( strpos($line, 'Sec-WebSocket-Key')!== false ) {
$key = substr($line, 19, 24); // <== mistake num 1 here
} else
if ( strpos($line, 'User-Agent')!== false ) {
break;
}
}
//echo $buf;
$sha1 = SHA1($key.KEY_SUFFIX, true);
$accept = base64_encode($sha1);
//mistake num 2 here
$write = "HTTP/1.1 101 Switching Protocols\r\n";
$write .= "Upgrade: websocket\r\n";
$write .= "connection: Upgrade\r\n";
$write .= "Sec-Websocket-Accept: $accept\r\n\r\n";
socket_write( $client, $write, strlen($write) );
}
socket_close($sock);
?>