一:单进程阻塞

设计流程:

  1. 创建一个socket,绑定端口bind,监听端口listen
  2. 进入while循环,阻塞在accept操作上,等待客户端连接进入,进入睡眠状态,直到有新的客户发起connet到服务器,accept函数返回客户端的socket
  3. 利用fread读取客户端socket当中的数据,收到数据后服务器程序进程处理,然后使用fwrite向客户端发送响应

代码:

<?php
class Worker{
//监听socket
protected $socket = NULL;
//连接事件回调
public $onConnect = NULL;
//接收消息事件回调
public $onMessage = NULL;
public function __construct($socket_address) {
$this->socket=stream_socket_server($socket_address);
} public function start() {
while (true) {
$clientSocket = stream_socket_accept($this->socket);
if (!empty($clientSocket) && is_callable($this->onConnect)) {
//触发连接事件的回掉
call_user_func($this->onConnect, $clientSocket);
}
//读取内容
$buffer = fread($clientSocket, 65535);
if (!empty($buffer) && is_callable($this->onMessage)) {
call_user_func($this->onMessage, $clientSocket, $buffer);
}
fclose($clientSocket);
}
}
} $worker = new Worker('tcp://0.0.0.0:9810'); $worker->onConnect = function ($args) {
echo "新的连接来了.{$args}.PHP_EOL";
};
$worker->onMessage = function ($conn, $message) {
var_dump($conn, $message);
$content="hello word qwe";
$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);
};
$worker->start();

cli下运行:

浏览器:

 缺点:一次只能处理一个连接,不支持多个连接同时处理

二:预派生子进程模式

设计流程:

  1. 创建一个socket,绑定服务器端口(bind),监听端口(listen)
  2. 通过pcntl_fork函数创建N个子进程
  3. 一个子进程创建成功后都去阻塞监听新的客户端连接
  4. 客户端连接时,其中一个子进程被唤醒,处理客户端请求
  5. 请求完成后,等待主进程回收子进程pcntl_wait

通过调用fork函数来创建子进程,会返回两个pid(主进程id、子进程id)

显示规则:

  1. 在父进程:fork函数返回子进程id
  2. 在子进程:fork函数返回0

代码:

<?php

class Worker {
//监听socket
protected $socket = NULL;
//连接事件回调
public $onConnect = NULL;
//接收消息事件回调
public $onMessage = NULL;
public $workerNum = 10; public function __construct($socket_address) {
$this->socket = stream_socket_server($socket_address);
} //创建子进程
public function fork() {
for ($i = 0; $i < $this->workerNum; $i++) {
$pid = pcntl_fork();
if ($pid < 0) {
exit('创建失败');
} else if ($pid > 0) {
//父进程空间,返回子进程id
} else {
//子进程空间,返回父进程id 0
$this->accept();
}
}
$status = 0;
$pid = pcntl_wait($status);
echo "子进程" . $pid . PHP_EOL;
} public function accept(){
while (true) {
$clientSocket = stream_socket_accept($this->socket);
var_dump("正在执行任务的pid为:".posix_getpid());
if (!empty($clientSocket) && is_callable($this->onConnect)) {
call_user_func($this->onConnect, $clientSocket);
} $buffer = fread($clientSocket, 65535);
if (!empty($buffer) && is_callable($this->onMessage)) {
call_user_func($this->onMessage, $clientSocket, $buffer);
}
fclose($clientSocket);
}
} public function start() {
$this->fork();
}
} $worker = new Worker('tcp://0.0.0.0:9801'); $worker->onConnect = function ($args) {
echo "新的连接来了.{$args}.PHP_EOL";
};
$worker->onMessage = function ($conn, $message) {
// var_dump($conn, $message);
$content = "hello word qwe";
$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);
};
$worker->start();

cli执行结果:

 缺点:严重依赖进程的数量解决并发问题,一个客户端连接就需要占用一个进程

三:单进程阻塞复用模型

设计流程:

  1. 保存所有的socket,通过select系统调用,监听socket描述符的可读事件
  2. socket在内核监控,一旦发现可读,会从内核空间传递给用户空间,通过逻辑判断是服务端socket可读,还是客户端socket可读
  3. 如果是服务端socket可读,说明有新的客户端建立,将socket保留到监听数组中
  4. 如果是客户端socket可读,说明当前已经可以去读取客户端发送过来的内容了,读取了内容,响应给客户端

代码:

<?php

class Worker {
//监听socket
protected $socket = NULL;
//连接事件回调
public $onConnect = NULL;
//接收消息事件回调
public $onMessage = NULL;
public $workerNum = 4 ;
public $allSocket; public function __construct($socket_address) {
$this->socket = stream_socket_server($socket_address);
stream_set_blocking($this->socket,0);
$this->allSocket[(int)$this->socket]=$this->socket;
} public function fork() {
// for ($i = 0; $i < $this->workerNum; $i++) {
// $pid = pcntl_fork();
// if ($pid < 0) {
// exit('创建失败');
// } else if ($pid > 0) {
//
// } else {
$this->accept();
// }
// }
// $status = 0;
// $pid = pcntl_wait($status);
// echo "子进程" . $pid . PHP_EOL;
} public function accept(){
while (true) {
$write =$except =[];
$read= $this->allSocket;
stream_select($read,$write,$except,60);
foreach($read as $index =>$val){
if ($val == $this->socket){
$clientSocket = stream_socket_accept($this->socket);
var_dump(posix_getpid());
if (!empty($clientSocket) && is_callable($this->onConnect)) {
call_user_func($this->onConnect, $clientSocket);
}
$this->allSocket[(int)$clientSocket]=$clientSocket;
}else{
$buffer = fread($val, 65535);
if (empty($buffer)){
if (feof($val) || is_resource($val)){
fclose($val);
unset($this->allSocket[(int)$val]);
continue;
}
}
if (!empty($buffer) && is_callable($this->onMessage)) {
call_user_func($this->onMessage, $val, $buffer);
}
}
} }
} public function start() {
$this->fork();
}
} $worker = new Worker('tcp://0.0.0.0:9800'); $worker->onConnect = function ($args) {
echo "新的连接来了.{$args}.PHP_EOL";
};
$worker->onMessage = function ($conn, $message) {
// var_dump($conn, $message);
$content = "hello word qwe";
$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);
};
$worker->start();

缺点:select模式本身缺点(循环遍历处理事件、内核空间传递数据的消耗)、单线程对于大量任务处理乏力

swoole(3)网络服务模型(单进程阻塞、预派生子进程、单进程阻塞复用模型)的更多相关文章

  1. IO模式设置网络编程常见问题总结—IO模式设置,阻塞与非阻塞的比较,recv参数对性能的影响—O_NONBLOCK(open使用)、IPC_NOWAIT(msgrcv)、MSG_DONTWAIT(re

    非阻塞IO 和阻塞IO: 在网络编程中对于一个网络句柄会遇到阻塞IO 和非阻塞IO 的概念, 这里对于这两种socket 先做一下说明:       基本概念: 阻塞IO:: socket 的阻塞模式 ...

  2. Linux非阻塞IO(二)网络编程中非阻塞IO与IO复用模型结合

    上文描述了最简易的非阻塞IO,采用的是轮询的方式,这节我们使用IO复用模型.   阻塞IO   过去我们使用IO复用与阻塞IO结合的时候,IO复用模型起到的作用是并发监听多个fd. 以简单的回射服务器 ...

  3. 网络编程并发 多进程 进程池,互斥锁,信号量,IO模型

    进程:程序正在执行的过程,就是一个正在执行的任务,而负责执行任务的就是cpu 操作系统:操作系统就是一个协调.管理和控制计算机硬件资源和软件资源的控制程序. 操作系统的作用: 1:隐藏丑陋复杂的硬件接 ...

  4. 阻塞与非阻塞、同步与异步 I/O模型

    I/O模型 Linux 下的五种I/O模型 阻塞I/O(blocking I/O) 非阻塞I/O (nonblocking I/O) I/O复用(select 和poll) (I/O multiple ...

  5. 同步I/O、异步I/O与阻塞I/O、非阻塞I/O的区别

    一.I/O I/O (Input/Output,输入/输出)即数据的读取(接收)或写入(发送)操作. 通常用户进程中的一个完整I/O分为两阶段:用户进程空间<-->内核空间.内核空间< ...

  6. python 全栈开发,Day44(IO模型介绍,阻塞IO,非阻塞IO,多路复用IO,异步IO,IO模型比较分析,selectors模块,垃圾回收机制)

    昨日内容回顾 协程实际上是一个线程,执行了多个任务,遇到IO就切换 切换,可以使用yield,greenlet 遇到IO gevent: 检测到IO,能够使用greenlet实现自动切换,规避了IO阻 ...

  7. (原创)JAVA阻塞队列LinkedBlockingQueue 以及非阻塞队列ConcurrentLinkedQueue 的区别

    阻塞队列:线程安全 按 FIFO(先进先出)排序元素.队列的头部 是在队列中时间最长的元素.队列的尾部 是在队列中时间最短的元素.新元素插入到队列的尾部,并且队列检索操作会获得位于队列头部的元素.链接 ...

  8. socket阻塞与非阻塞,同步与异步I/O模型

    作者:huangguisu 原文出处:http://blog.csdn.NET/hguisu/article/details/7453390 socket阻塞与非阻塞,同步与异步 1. 概念理解 在进 ...

  9. Python学习笔记整理总结【网络编程】【线程/进程/协程/IO多路模型/select/poll/epoll/selector】

    一.socket(单链接) 1.socket:应用层与TCP/IP协议族通信的中间软件抽象层,它是一组接口.在设计模式中,Socket其实就是一个门面模式,它把复杂的TCP/IP协议族隐藏在Socke ...

随机推荐

  1. linux操作提示:“Can't open file for writing”或“operation not permitted”的解决办法

    在linux上使用vi命令修改一个文件内容的时候,发现无法保存,每次写完使用":q!"命令可以正常退出但是使用":wq!"命令保存文件并退出时出现一下信息提示: ...

  2. 【Java杂货铺】JVM#虚拟机加载机制

    代码编译的结果从本地机器码变为字节码,是储存格式发展的一小步,却是编程语言发展的一大步--<深入理解Java虚拟机> 虚拟机把描述类的数据从Class文件加载到内存,并对数据进行校验.转化 ...

  3. [SDOI2019]热闹又尴尬的聚会(图论+set+构造)

    据说原数据可以让复杂度不满的暴力O(Tn^2)过掉……O(Tn^2)方法类似于codeforces一场div2的E题 有一种比较好的方法:每次找出原图G中度最小的点加入q,然后将相邻的点加入新图G'. ...

  4. zabbix server in not running

    修改配置文件 vi zabbix.conf.php 修改lochlhost为 自己服务器的IP地址 修改$DB['SERVER']   = '192.168.30.6'; 修改$ZBX_SERVER  ...

  5. RELAX NG

    RELAX NG (读作"relaxing"), 是一种基于语法的XML模式语言,可用于描述.定义和限制XML词汇表. 最初的XML模式语言是DTD,但是因为DTD语法丑陋, 表达 ...

  6. HashMap相关知识

    HashMap的工作原理是近年来常见的Java面试题.几乎每个Java程序员都知道HashMap,都知道哪里要用HashMap,知道Hashtable和HashMap之间的区别,那么为何这道面试题如此 ...

  7. 最大流/最小割模板(isap) POJ1273

    isap模板核心代码: //d[]为距离标号数组,d[i]表示节点i到汇点的距离 //gap[]为GAP优化数组,gap[i]表示到汇点距离为i的节点个数 int dfs(int k,int flow ...

  8. C语言返回值

    C语言函数在结束的时候可以用一个数字表示运行的结果,这个数字就叫做函数的返回值. 主函数应该有一个返回值.如果这个返回值是0就表示程序希望计算机认为它正常结束,如果不是0就表示程序希望计算机认为它是出 ...

  9. deeplearning.ai 构建机器学习项目 Week 2 机器学习策略 II

    1. 误差分析(Error analysis) 误差分析的目的是找到不同误差源的比重,从而指引我们接下来往哪个方向努力改进.NG建议手工统计随机100个错误的误差源,比如对于猫分类器,错误的照片可能是 ...

  10. [LC] 191. Number of 1 Bits

    Write a function that takes an unsigned integer and return the number of '1' bits it has (also known ...