#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:
- Be able to read messages sent for requests
- 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 ~