(a) how to realize a single process blocking network server

  php, socket, swoole

图片描述

summarize

In order to better understand, network programming and write a high-performance service, we need to spend some time to understand the whole process of the server processing the client and understand some key terms. We originally wanted to add some basic theoretical knowledge in this article, fearing that too much space is not conducive to reading, so we will reissue some basic knowledge later and then get down to business.

Theory

This article mainly introduces the basic steps of realizing a network server, and the code will be reproduced once in the practice.

clipboard.png

First step

We need to create a socket, bind server port (bind), listen port (listen), and use stream_socket_server function in PHP to complete the above three steps.

Second step

Enter while loop, block on accept operation, wait for client connection to enter. At this time, the program will go to sleep until a new client initiates connect to the server, and the operating system will wake up the process. The accept function returns the socket of the client connection

Third step

Fread is used to read the data in the socket of the client. After receiving the data, the server program processes it and then uses fwrite to send a response to the client. Long-connected services will continue to interact with clients, while short-connected services will normally close upon receiving a response.

Practice

Here we use code to implement the next basic process. Before we start writing code, we will introduce some php functions that may be used in our code for your understanding.

Function

stream_socket_server
stream_socket_accept
call_user_func
is_callable
fread

Click on the function for usage

Code

Cut the crap and start straight away ~

<?  php
 class Worker{
 //Monitor socket
 protected $socket = NULL;
 //Connection Event Callback
 public $onConnect = NULL;
 //Receive Message Event Callback
 public $onMessage = NULL;
 public function __construct($socket_address) {
 
 }
 
 public function run(){
 
 }
 }
 
 
 
 $worker = new Worker('tcp://0.0.0.0:9810');
 //A connection event callback was registered in advance
 $worker->onConnect = function ($data) {
 Echo' new connection is coming', $data, PHP_EOL;
 };
 //An event callback to receive a message was registered in advance
 $worker->onMessage = function ($conn, $message) {
 };
 $worker->run();

According to the previous process, we need to monitor the port+address

public function __construct($socket_address) {
 //Listening Address+Port
 $this->socket=stream_socket_server($socket_address);
 }

The next step is to block the accept operation and wait for the client connection to enter. At this time, the program will go to sleep until a new client initiates connect to the server, and the operating system will wake up the process.

public function run(){
 While (true) {// loop monitor
 $client = stream_socket_accept($this->socket);  //Blocking Monitoring on Server Side
 }
 }

When a new connection enters the wake-up process and triggers a connection event callback

public function run(){
 While (true) {// loop monitor
 $client = stream_socket_accept($this->socket);  //Blocking Monitoring on Server Side
 if(!  Empty ($ client)&&is _ callable ($ this-> onconnect)) {//socket connection succeeded and is our callback
 //Callback of the connection that triggered the event
 call_user_func($this->onConnect,$client);
 }
 }
 }

The connection callback here actually triggers the following code here that has prepared the class library before.

$worker->onConnect = function ($data) {
 Echo' connection event:', $data, PHP_EOL;
 };

When the connection is successful, fread is used to obtain the content of the client and trigger the event of receiving the message.

public function run(){
 While (true) {// loop monitor
 $client = stream_socket_accept($this->socket);  //Blocking Monitoring on Server Side
 if(!  Empty ($ client)&&is _ callable ($ this-> onconnect)) {//socket connection succeeded and is our callback
 //Callback of the connection that triggered the event
 call_user_func($this->onConnect,$client);
 }
 //read client content from connection
 $buffer=fread($client,65535);  //Parameter 2: Maximum number of bytes read in buffer
 //Data is normally read.  Triggering a message receiving event to respond
 if(!  empty($buffer) && is_callable($this->onMessage)){
 //Message receiving event of trigger time
 call_user_func($this->onMessage,$this,$client,$buffer);  //current object, current connection, received message of "event to receive message"
 }
 }
 }

At this point, the basic network service reception is basically completed, and a response is required to the request. Take http request as an example, here is a method of HTTP response (http://127.0.0.1:9810)

class Worker{
 ...
 ...
 ...
 public function  send($conn,$content){
 $http_resonse = "HTTP/1.1 200 OK\r\n";
 $http_resonse .= "Content-Type: text/html;  charset=UTF-8\r\n";
 $http_resonse .= "Connection: keep-alive\r\n";
 $http_resonse .= "Server: php socket server\r\n";
 $http_resonse .= "Content-length: ".strlen($content)."\r\n\r\n";
 $http_resonse .= $content;
 fwrite($conn, $http_resonse);
 }
 }

Respond to http requests when triggering receive message events

$worker->onMessage = function ($server,$conn, $message) {
 Echo' Message from Client:', $message,PHP_EOL;
 $server->send($conn,' message from server');
 };

This is the end ~, complete codeThrough train

Disadvantages

Only one connection can be processed at a time, and simultaneous processing of multiple connections is not supported.