#network-programming

preface

As my working years get longer, the sense of urgency in this field is still the same as when I first started working. There is no doubt that network programming is one of the areas I need to crack next as a server developer:

Learning ideas

The following is a simple way for me to learn network programming, and I will gradually learn knowledge related to network programming according to this plan.

Step 1. Implement TCP Server in native PHP -> implement HTTP protocol in native PHP -> master the use of tcpdump -> deeply understand the TCP connection process step 2. Webserver 2.1 Introduces I/O multiplexing 2.2 Introduces PHP coroutines (yield) 2.3 Performance differences between I/O multiplexing and coroutine versions Step 3. Step 4. PHP C extension to implement a simple WebServerCopy the code

Why do I choose PHP to learn network programming? For me, PHP is the most familiar. Secondly, PHP is relatively simple, and PHP itself has corresponding functions.

Let’s start with part one today.

Step 1. Implement TCP Server in native PHP -> implement HTTP protocol in native PHP -> master the use of tcpdump -> deeply understand the TCP connection process

The body of the

Let’s start with a quick review of common interactions with PHP as a back-end language:

Client – (protocol: HTTP) – > nginx – (protocol:fastcgi) – > php-fpm – (interface: sAPI) – > PHP

Here nginx acts as a Web server and reverse proxy server, converting HTTP to FastCGI. If PHP handles HTTP requests directly, then nginx&php-fpm is unnecessary. Unfortunately, native PHP does not implement the HTTP protocol.

Then you might say, “Native PHP supports TCP, right? Nginx proxys HTTP requests to TCP instead of phP-FPM. Well, yes, that’s right. The interaction described by the friend is as follows:

Client — (protocol: HTTP) — > nginx — (protocol: TCP) — > PHP

That seems fine, and it’s a good idea, but in theory HTTP is still not implemented and the content received should still be a bunch of strings. Let’s give it a try:

Step 1: Start an Nginx service
PHP implements a SIMPLE TCP server. The code is as follows
<? php $server = socket_create(AF_INET, SOCK_STREAM, SOL_TCP); Socket_bind ($server, '127.0.0.1', '8889'); socket_listen($server); while (true) { $client = socket_accept($server); if (! $client) { continue; } $request = socket_read($client, 1024); Var_dump ($request); socket_close($client); }Copy the code
Step 3: The Nginx reverse proxy sends HTTP requests to the TCP server
upstream tcp_server { ip_hash; Server 127.0.0.1:8889 max_fails fail_timeout = 3 = 5; } server { listen 80; server_name test.local; access_log /tmp/logs/nginx/test.access.log main; location / { proxy_set_header X-Forwarded-For $remote_addr; proxy_set_header Host $http_host; proxy_pass http://tcp_server; }}Copy the code

Test.local /? Aaa =1/ Look at the printed result and the previous guess:

string(127) "GET /? Http_forwarded-for: http_forwarded-for: http_forwarded-for: http_forwarded-for: http_forwarded-for: http_forwarded-for: http_forwarded-for: 0.0.0.0 Accept: */*"Copy the code

So we need to implement HTTP, and now that we’ve implemented HTTP, we can use HTTP as the Web server directly.

Client – (protocol: HTTP) – > PHP

Isn’t it! After that, nginx’s role is to load balance, and you can use PHP to load balance yourself.

Native PHP implements TCP Server

Let’s see how to create a simple TCP Server in PHP:








The main PHP functions involved are as follows:

socket_create

socket_listen

socket_accept

socket_recv || socket_read

socket_write

socket_close
Copy the code

Code:

<? php $socket = socket_create(AF_INET, SOCK_STREAM, SOL_TCP); Socket_bind ($sockets, '127.0.0.1', '8889'); socket_listen($socket); while (true) { // accept $client = socket_accept($server); if (! $client) { continue; } $request = socket_read($client, 1024); socket_close($client); echo socket_strerror(socket_last_error($server)) . "\n"; }Copy the code

Run the above code from the command line, and then test the small TCP connection with the nc command:

(Tigerb) ➜ Demo Git :(Master) Qualify nC-Z-V 127.0.0.1 8889 found 0 associations Found 1 connections: 1: Flags =82<CONNECTED,PREFERRED> outif lo0 SRC 127.0.0.1 port 60668 DST 127.0.0.1 port 8889 rank info not available TCP AUX Info available Connection to 127.0.0.1 port 8889 [TCP/DDI-TCP-2] Succeeded!Copy the code

Nothing wrong, TCP Server is up.

Native PHP implements the HTTP protocol

The simple TCP Server above is basically out, we need to make PHP directly a Web Server, think of the Web Server is based on HTTP protocol, HTTP protocol is based on TCP protocol implementation. That is to say, we can implement the HTTP protocol on the basis of the above TCP Server. Let’s improve the flow chart by adding the HTTP part (orange) as follows








The process of implementing the HTTP protocol is as follows:

  1. Be able to read messages sent for requests
  2. It can return information to clients like browsers that they understand

A protocol is nothing more than a specification agreed upon by both parties. The request and response format in HTTP/1.1 is basically as follows

Request:

<HTTP Method> <url> <HTTP Version>
<KEY>:<VALUE>\r\n
...
\r\n
Copy the code

Response:

<HTTP Version> <HTTP Status> <HTTP Status Description>
<KEY>:<VALUE>\r\n
...
\r\n
Copy the code

So in simple terms, our PHP code just according to the above specification to parse and return the corresponding content, simple code example is as follows:

/ * * * PHP implementation simple HTTP protocol * / class HttpProtocol {/ * * * * * @ original request string var string * / public $originRequestContentString = ' '; Private $originRequestContentList = []; private $originRequestContentList = []; Private $originRequestContentMap = []; private $originRequestContentMap = []; ** @var array */ private $responseHead = ['HTTP '=> 'HTTP/1.1 200 OK', 'content-type' => 'content-type: Text/HTML ', 'server' => 'server: PHP /0.0.1',]; Private $responseBody = ''; $responseData = $responseData; Public function Request ($content = "") {if (empty($content)); { // exception } $this->originRequestContentList = explode("\r\n", $this->originRequestContentString); if (empty($this->originRequestContentList)) { // exception } foreach ($this->originRequestContentList as $k => $v) { if ($v === ") {// Filter empty continue; If ($k === 0) {if ($k === 0) {if ($k === 0) {if ($k === 0) {if ($k === 0) {if ($k === 0) { $http_version) = explode(' ', $v); $this->originRequestContentMap['Method'] = $http_method; $this->originRequestContentMap['Request-Uri'] = $http_request_uri; $this->originRequestContentMap['Version'] = $http_version; continue; } list($key, $val) = explode(': ', $v); $this->originRequestContentMap[$key] = $val; Public function responseBody ($responseBody) {$responseBody = $responseBody ($responseBody) count($this->responseHead); $finalHead = ''; foreach ($this->responseHead as $v) { $finalHead .= $v . "\r\n"; } $this->responseData = $finalHead . "\r\n" . $responseBody; }}Copy the code

We just insert code after socket_read

while (true) {
    // accept
    $client = socket_accept($server);
    if (! $client) {
        continue;
    }
    $request = socket_read($client, 1024);

    /**
     * HTTP 
     */
    $http = new HttpProtocol;
    $http->originRequestContentString = $request;
    $http->request($request);
    $http->response("Hello World");
    socket_write($client, $http->responseData);
    
    socket_close($client);
    echo socket_strerror(socket_last_error($server)) . "\n";
}
Copy the code

Finally visit http://127.0.0.1:8889/ and the result is as follows, or the browser will open the page and output “Hello World”.

(tigerb) ➜ demo git:(master) qualify curl "http://127.0.0.1:8889/" -vv * Trying 129.0.0.1... * TCP_NODELAY set * Connected to 127.0.0.1 (127.0.0.1) port 8889 (#0) > GET/HTTP/1.1 > Host: 127.0.0.1:8889 > user-agent: curl/7.54.0 > Accept: */* > < HTTP/1.1 200 OK < content-type: text/ HTML < Server: PHP /0.0.1 * no chunk, no close, no size. Assume close to signal end < * Closing connection 0 Hello World%Copy the code

conclusion

So far we use PHP to simply set up a Web server, on this basis PHP can directly interact with the client. Finally, we will use this simple Web Server to analyze the TCP connection process by capturing packets using tcpdump. Waiting for ~