Tp5使用命令行模式,自定义指令

https://blog.csdn.net/benben0729/article/details/82286450

第一步,创建一个自定义命令类文件,新建application/common/command/Hello.php

<?phpnamespace app\common\command; use think\console\Command;use think\console\Input;use think\console\input\Argument;use think\console\input\Option;use think\console\Output; class Hello extends Command{    protected function configure()    {        $this->setName('hello')        	->addArgument('name', Argument::OPTIONAL, "your name")            ->addOption('city', null, Option::VALUE_REQUIRED, 'city name')        	->setDescription('Say Hello');    }     protected function execute(Input $input, Output $output)    {    	$name = trim($input->getArgument('name'));      	$name = $name ?: 'thinkphp'; 		if ($input->hasOption('city')) {        	$city = PHP_EOL . 'From ' . $input->getOption('city');        } else {        	$city = '';        }                $output->writeln("Hello," . $name . '!' . $city);    }}

这个文件定义了一个叫hello的命令,并设置了一个name参数和一个city选项。

第二步,配置application/command.php文件

<?phpreturn [    'app\common\command\Hello',];

V5.1.24+版本开始,你可以定义为下面的方式提高性能。

<?phpreturn [	// 指令名 =》完整的类名    'hello'	=>	'app\common\command\Hello',];

第三步,测试-命令帮助-命令行下运行

php think

输出

Think Console version 0.1 Usage:  command [options] [arguments] Options:  -h, --help            Display this help message  -V, --version         Display this console version  -q, --quiet           Do not output any message      --ansi            Force ANSI output      --no-ansi         Disable ANSI output  -n, --no-interaction  Do not ask any interactive question  -v|vv|vvv, --verbose  Increase the verbosity of messages: 1 for normal output, 2 for more verbose output and 3 for debug Available commands:  build              Build Application Dirs  clear              Clear runtime file  hello              Say Hello   help               Displays help for a command  list               Lists commands make  make:controller    Create a new resource controller class  make:model         Create a new model class optimize  optimize:autoload  Optimizes PSR0 and PSR4 packages to be loaded with classmaps too, good for production.  optimize:config    Build config and common file cache.  optimize:schema    Build database schema cache. 

第四步,运行hello命令

php think hello

输出

Hello thinkphp!

添加命令参数

php think hello liuchen

输出

Hello liuchen!

添加city选项

php think hello liuchen --city shanghai

输出

Hello thinkphp!From shanghai

注意看参数和选项的调用区别

在控制器中调用命令

支持在控制器的操作方法中直接调用命令,例如:

<?phpnamespace app\index\controller; use think\Console;use think\Controller; class Index extends Controller{    public function hello($name)    {        $output = Console::call('hello ' . $name);         return $output->fetch();    }}

访问该操作方法后,例如:

http://tp5.com/index/index/hello/name/thinkphp

页面会输出

Hello thinkphp!

一旦在控制器中调用命令,并不会生成cli命令行日志,而是在普通的web日志中记录。

快速生成指令(V5.1.24+

V5.1.24+版本开始,你可以通过命令行指令快速生成一条指令,包括指令类文件,例如:

php think make:command First first

First表示类名,实际生成的指令类则是app\command\First(自动生成的指令类的命名空间默认都是 app\command)。

first表示指令名,建议统一使用小写,如果不传入该参数,则默认用类名的小写作为指令名。

为了让指令可以无需定义自动调用,需要在你的应用配置的console.php配置文件中,增加下面的配置参数:

'auto_path' => env('app_path') . 'command/',

配置后, 你可以在命令行测试下刚才新生成的指令。

php think first

输出结果为:

first

你可以编辑app\command\First类,完成实际的指令任务。

注意,如果你生成了指定命名空间的指令,但又不是你配置的自动加载目录,那么仍然需要在command.php 文件中定义指令。

如果需要生成一个指定的命名空间,可以使用:

php think make:command app\index\Second second

转自:看云https://www.kancloud.cn/manual/thinkphp5_1/354146

java显示声音波形图示例

这篇文章主要介绍了java显示声音波形图示例,需要的朋友可以参考下

package _tmp;
import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Graphics;
import java.awt.Image;
import java.awt.Toolkit;
import java.awt.image.BufferedImage;
import java.io.File;
import java.io.IOException;
import java.util.ArrayDeque;
import java.util.Deque;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.Timer;
import java.util.TimerTask;
import javax.sound.sampled.AudioFormat;
import javax.sound.sampled.AudioInputStream;
import javax.sound.sampled.AudioSystem;
import javax.sound.sampled.SourceDataLine;
import javax.swing.JFrame;
import javax.swing.SwingUtilities;
public class SoundTest {

 public static class WaveformGraph extends JFrame {
  private Deque deque = new LinkedList();
  private Timer timer;
  private Image buffered;
  private Image showing;

  public WaveformGraph(int width, int height) {
   setSize(width, height);
   timer = new Timer();
   buffered = new BufferedImage(width, height, BufferedImage.TYPE_4BYTE_ABGR);
   timer.schedule(new TimerTask() {
    @Override public void run() {
     Graphics g = buffered.getGraphics();
     g.setColor(Color.WHITE);
     g.fillRect(0, 0, getWidth(), getHeight());
     g.setColor(Color.BLACK);

     g.translate(10, getHeight()/2);
     synchronized (deque) {
      float heightRate = 1;
      if(deque.size() > 1) {
       Iterator iter = deque.iterator();
       Short p1 = iter.next();
       Short p2 = iter.next();
       int x1 = 0, x2 = 0;
       while(iter.hasNext()) {
        g.drawLine(x1, (int)(p1heightRate), x2, (int)(p2heightRate));

        p1 = p2;
        p2 = iter.next();
        x1 = x2;
        x2 += 1;
       }
      }
     }
     g.dispose();

     SwingUtilities.invokeLater(new Runnable() {
      @Override public void run() {
       showing = buffered;
       repaint();
       showing = null;
      }
     });
    }
   }, 100, 100);
  }
  @Override
  public void paint(Graphics g) {
   super.paint(g);
   if(buffered!=null) {
    g.drawImage(buffered, 0, 0, null);
   }
  }

  public void put(short v) {
   synchronized (deque) {
    deque.add(v);
    if(deque.size() > 500) {
     deque.removeFirst();
    }
   }
  }

  public void clear() {
   deque.clear();
  }
 }

 public static void main(String[] args) throws Exception {
//  record();
  WaveformGraph waveformGraph = new WaveformGraph(500, 300);
  waveformGraph.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
  waveformGraph.setVisible(true);

  AudioInputStream ais = AudioSystem.getAudioInputStream(new File(“C:\Documents and Settings\wml\My Documents\My Music\苏仨 – 失眠症.wav”));
  printFormat(ais.getFormat());
  
  SourceDataLine player = AudioSystem.getSourceDataLine(ais.getFormat());

  player.open();
  player.start();
  byte[] buf = new byte[4];
  int len;
  while((len=ais.read(buf))!=-1) {

   if(ais.getFormat().getChannels() == 2) {
    if(ais.getFormat().getSampleRate() == 16) {
     waveformGraph.put((short) ((buf[1] << 8) | buf[0]));//左声道
//     waveformGraph.put((short) ((buf[3] << 8) | buf[2]));//右声道
    } else {
     waveformGraph.put(buf[1]);//左声道
     waveformGraph.put(buf[3]);//左声道

//     waveformGraph.put(buf[2]);//右声道
//     waveformGraph.put(buf[4]);//右声道
    }
   } else {
    if(ais.getFormat().getSampleRate() == 16) {
     waveformGraph.put((short) ((buf[1] << 8) | buf[0]));
     waveformGraph.put((short) ((buf[3] << 8) | buf[2]));
    } else {
     waveformGraph.put(buf[1]);
     waveformGraph.put(buf[2]);
     waveformGraph.put(buf[3]);
     waveformGraph.put(buf[4]);
    }
   }

   player.write(buf, 0, len);
  }

  player.close();
  ais.close();
 }
 public static void printFormat(AudioFormat format) {
  System.out.println(format.getEncoding() + ” => “
    + format.getSampleRate()+” hz, “
    + format.getSampleSizeInBits() + ” bit, “
    + format.getChannels() + ” channel, “
    + format.getFrameRate() + ” frames/second, “
    + format.getFrameSize() + ” bytes/frame”);
 }
// public static void record() throws LineUnavailableException,
//   InterruptedException {
//  AudioFormat audioFormat = new AudioFormat(AudioFormat.Encoding.PCM_SIGNED, 48000F, 16, 1, 2, 48000F, false);
//  Info recordDevInfo = new DataLine.Info(TargetDataLine.class, audioFormat);
//
//  final TargetDataLine recordLine = (TargetDataLine) AudioSystem.getLine(recordDevInfo);
//  final SourceDataLine playLine = AudioSystem.getSourceDataLine(audioFormat);
//  
//  recordLine.open(audioFormat, recordLine.getBufferSize());
//  playLine.open(audioFormat, recordLine.getBufferSize());
//  
//  Thread recorder = new Thread() {
//   public void run() {
//    recordLine.start();
//    playLine.start();
//    
//    FloatControl fc = (FloatControl) playLine.getControl(FloatControl.Type.MASTER_GAIN);
//    double value = 2;
//    float dB = (float) (Math.log(value == 0.0 ? 0.0001 : value) / Math.log(10.0) * 20.0);
//    fc.setValue(dB);
//    
//    try {
//     AudioInputStream in = new AudioInputStream(recordLine);
//     byte[] buf = new byte[recordLine.getBufferSize()];
//     int len;
//     while((len=in.read(buf)) != -1) {
//      playLine.write(buf, 0, len);
//     }
//    } catch (IOException e) {
//     e.printStackTrace();
//    } finally {
//     recordLine.stop();
//     playLine.stop();
//    }
//   };
//  };
//  recorder.start();
//  recorder.join();
// }
}

Java实现Shazam声音识别算法的实例代码

https://www.jb51.net/article/147139.htm

Shazam算法采用傅里叶变换将时域信号转换为频域信号,并获得音频指纹,最后匹配指纹契合度来识别音频。这篇文章给大家介绍Java实现Shazam声音识别算法的实例代码,需要的朋友参考下吧

Shazam算法采用傅里叶变换将时域信号转换为频域信号,并获得音频指纹,最后匹配指纹契合度来识别音频。

1、AudioSystem获取音频

奈奎斯特-香农采样定理告诉我们,为了能捕获人类能听到的声音频率,我们的采样速率必须是人类听觉范围的两倍。人类能听到的声音频率范围大约在20Hz到20000Hz之间,所以在录制音频的时候采样率大多是44100Hz。这是大多数标准MPEG-1 的采样率。44100这个值最初来源于索尼,因为它可以允许音频在修改过的视频设备上以25帧(PAL)或者30帧( NTSC)每秒进行录制,而且也覆盖了专业录音设备的20000Hz带宽。所以当你在选择录音的频率时,选择44100Hz就好了。

定义音频格式:

123456789public static float sampleRate = 44100;public static int sampleSizeInBits = 16;public static int channels = 2; // doublepublic static boolean signed = true; // Indicates whether the data is signed or unsignedpublic static boolean bigEndian = true; // Indicates whether the audio data is stored in big-endian or little-endian orderpublic AudioFormat getFormat() {return new AudioFormat(sampleRate, sampleSizeInBits, channels, signed,bigEndian);}

调用麦克风获取音频,保存到out中

123456789101112131415161718public static ByteArrayOutputStream out = new ByteArrayOutputStream();1try {AudioFormat format = smartAuto.getFormat(); // Fill AudioFormat with the settingsDataLine.Info info = new DataLine.Info(TargetDataLine.class, format);startTime = new Date().getTime();System.out.println(startTime);SmartAuto.line = (TargetDataLine) AudioSystem.getLine(info);SmartAuto.line.open(format);SmartAuto.line.start();new FileAnalysis().getDataToOut("");while (smartAuto.running) {checkTime(startTime);}SmartAuto.line.stop();SmartAuto.line.close();} catch (Throwable e) {e.printStackTrace();}

获取到的out数据需要通过傅里叶变换,从时域信号转换为频域信号。

傅里叶变换

1234567891011121314151617181920212223242526272829303132333435public Complex[] fft(Complex[] x) {int n = x.length;// 因为exp(-2i*n*PI)=1,n=1时递归原点if (n == 1){return x;}// 如果信号数为奇数,使用dft计算if (n % 2 != 0) {return dft(x);}// 提取下标为偶数的原始信号值进行递归fft计算Complex[] even = new Complex[n / 2];for (int k = 0; k < n / 2; k++) {even[k] = x[2 * k];}Complex[] evenValue = fft(even);// 提取下标为奇数的原始信号值进行fft计算// 节约内存Complex[] odd = even;for (int k = 0; k < n / 2; k++) {odd[k] = x[2 * k + 1];}Complex[] oddValue = fft(odd);// 偶数+奇数Complex[] result = new Complex[n];for (int k = 0; k < n / 2; k++) {// 使用欧拉公式e^(-i*2pi*k/N) = cos(-2pi*k/N) + i*sin(-2pi*k/N)double p = -2 * k * Math.PI / n;Complex m = new Complex(Math.cos(p), Math.sin(p));result[k] = evenValue[k].add(m.multiply(oddValue[k]));// exp(-2*(k+n/2)*PI/n) 相当于 -exp(-2*k*PI/n),其中exp(-n*PI)=-1(欧拉公式);result[k + n / 2] = evenValue[k].subtract(m.multiply(oddValue[k]));}return result;}

计算out的频域值

123456789101112131415161718192021private void setFFTResult(){byte audio[] = SmartAuto.out.toByteArray();final int totalSize = audio.length;System.out.println("totalSize = " + totalSize);int chenkSize = 4;int amountPossible = totalSize/chenkSize;//When turning into frequency domain we'll need complex numbers: SmartAuto.results = new Complex[amountPossible][];DftOperate dfaOperate = new DftOperate();//For all the chunks: for(int times = 0;times < amountPossible; times++) {Complex[] complex = new Complex[chenkSize];for(int i = 0;i < chenkSize;i++) {//Put the time domain data into a complex number with imaginary part as 0: complex[i] = new Complex(audio[(times*chenkSize)+i], 0);}//Perform FFT analysis on the chunk: SmartAuto.results[times] = dfaOperate.fft(complex);}System.out.println("results = " + SmartAuto.results.toString());}

总结

以上所述是小编给大家介绍的Java实现Shazam声音识别算法的实例代码,希望对大家有所帮助,如果大家有任何疑问请给我留言,小编会及时回复大家的。在此也非常感谢大家对脚本之家网站的支持!

Java图片上查找图片算法

之前用按键精灵写过一些游戏辅助,里面有个函数叫FindPic,就是在屏幕范围查找给定的一张图片,返回查找到的坐标位置。

  现在,Java来实现这个函数类似的功能。

  算法描述:

  1. 屏幕截图,得到图A,(查找的目标图片为图B);
  2. 遍历图A的像素点,根据图B的尺寸,得到图B四个角映射到图A上的四个点;
  3. 得到的四个点与图B的四个角像素点的值比较。如果四个点一样,执行步骤4;否则,回到步骤2继续;
  4. 进一步对比,将映射范围内的全部点与图B全部的点比较。如果全部一样,则说明图片已找到;否则,回到步骤2继续;

  这里,像素之间的比较是通过BufferedImage对象获取每个像素的RGB值来比较的。如下,将BufferedImage转换为int二维数组:

/**
* 根据BufferedImage获取图片RGB数组
* @param bfImage
* @return
*/
public static int[][] getImageGRB(BufferedImage bfImage) {
int width = bfImage.getWidth();
int height = bfImage.getHeight();
int[][] result = new int[height][width];
for (int h = 0; h < height; h++) {
for (int w = 0; w < width; w++) {
//使用getRGB(w, h)获取该点的颜色值是ARGB,而在实际应用中使用的是RGB,所以需要将ARGB转化成RGB,即bufImg.getRGB(w, h) & 0xFFFFFF。
result[h][w] = bfImage.getRGB(w, h) & 0xFFFFFF;
}
}
return result;
}

比较两个像素点的RGB值是否相同,是通过异或操作比较的(据说比==效率更高),如果异或操作后得到的值为0,说明两个像素点的RGB一样,否则不一样。

  下面附上算法完整java代码:

package com.jebysun.test.imagefind;

import java.awt.AWTException;
import java.awt.Rectangle;
import java.awt.Robot;
import java.awt.Toolkit;
import java.awt.image.BufferedImage;
import java.io.File;
import java.io.IOException;

import javax.imageio.ImageIO;
/**

  • 屏幕上查找指定图片
  • @author Jeby Sun
  • @date 2014-09-13
  • @website http://www.jebysun.com
    */
    public class ImageFindDemo { BufferedImage screenShotImage; //屏幕截图
    BufferedImage keyImage; //查找目标图片 int scrShotImgWidth; //屏幕截图宽度
    int scrShotImgHeight; //屏幕截图高度 int keyImgWidth; //查找目标图片宽度
    int keyImgHeight; //查找目标图片高度 int[][] screenShotImageRGBData; //屏幕截图RGB数据
    int[][] keyImageRGBData; //查找目标图片RGB数据 int[][][] findImgData; //查找结果,目标图标位于屏幕截图上的坐标数据 public ImageFindDemo(String keyImagePath) {
    screenShotImage = this.getFullScreenShot();
    keyImage = this.getBfImageFromPath(keyImagePath);
    screenShotImageRGBData = this.getImageGRB(screenShotImage);
    keyImageRGBData = this.getImageGRB(keyImage);
    scrShotImgWidth = screenShotImage.getWidth();
    scrShotImgHeight = screenShotImage.getHeight();
    keyImgWidth = keyImage.getWidth();
    keyImgHeight = keyImage.getHeight(); //开始查找 this.findImage(); } /**
    • 全屏截图
    • @return 返回BufferedImage
      */
      public BufferedImage getFullScreenShot() {
      BufferedImage bfImage = null;
      int width = (int) Toolkit.getDefaultToolkit().getScreenSize().getWidth();
      int height = (int) Toolkit.getDefaultToolkit().getScreenSize().getHeight();
      try {
      Robot robot = new Robot();
      bfImage = robot.createScreenCapture(new Rectangle(0, 0, width, height));
      } catch (AWTException e) {
      e.printStackTrace();
      }
      return bfImage;
      } /**
    • 从本地文件读取目标图片
    • @param keyImagePath – 图片绝对路径
    • @return 本地图片的BufferedImage对象
      */
      public BufferedImage getBfImageFromPath(String keyImagePath) {
      BufferedImage bfImage = null;
      try {
      bfImage = ImageIO.read(new File(keyImagePath));
      } catch (IOException e) {
      e.printStackTrace();
      }
      return bfImage;
      } /**
    • 根据BufferedImage获取图片RGB数组
    • @param bfImage
    • @return
      */
      public int[][] getImageGRB(BufferedImage bfImage) {
      int width = bfImage.getWidth();
      int height = bfImage.getHeight();
      int[][] result = new int[height][width];
      for (int h = 0; h < height; h++) {
      for (int w = 0; w < width; w++) {
      //使用getRGB(w, h)获取该点的颜色值是ARGB,而在实际应用中使用的是RGB,所以需要将ARGB转化成RGB,即bufImg.getRGB(w, h) & 0xFFFFFF。
      result[h][w] = bfImage.getRGB(w, h) & 0xFFFFFF;
      }
      }
      return result;
      } /**
    • 查找图片
      */
      public void findImage() {
      findImgData = new int[keyImgHeight][keyImgWidth][2];
      //遍历屏幕截图像素点数据
      for(int y=0; y<scrShotImgHeight-keyImgHeight; y++) {
      for(int x=0; x<scrShotImgWidth-keyImgWidth; x++) {
      //根据目标图的尺寸,得到目标图四个角映射到屏幕截图上的四个点,
      //判断截图上对应的四个点与图B的四个角像素点的值是否相同,
      //如果相同就将屏幕截图上映射范围内的所有的点与目标图的所有的点进行比较。
      if((keyImageRGBData[0][0]^screenShotImageRGBData[y][x])==0
      && (keyImageRGBData[0][keyImgWidth-1]^screenShotImageRGBData[y][x+keyImgWidth-1])==0
      && (keyImageRGBData[keyImgHeight-1][keyImgWidth-1]^screenShotImageRGBData[y+keyImgHeight-1][x+keyImgWidth-1])==0
      && (keyImageRGBData[keyImgHeight-1][0]^screenShotImageRGBData[y+keyImgHeight-1][x])==0) { boolean isFinded = isMatchAll(y, x); //如果比较结果完全相同,则说明图片找到,填充查找到的位置坐标数据到查找结果数组。 if(isFinded) { for(int h=0; h<keyImgHeight; h++) { for(int w=0; w<keyImgWidth; w++) { findImgData[h][w][0] = y+h; findImgData[h][w][1] = x+w; } } return; } } } }
      } /**
    • 判断屏幕截图上目标图映射范围内的全部点是否全部和小图的点一一对应。
    • @param y – 与目标图左上角像素点想匹配的屏幕截图y坐标
    • @param x – 与目标图左上角像素点想匹配的屏幕截图x坐标
    • @return
      */
      public boolean isMatchAll(int y, int x) {
      int biggerY = 0;
      int biggerX = 0;
      int xor = 0;
      for(int smallerY=0; smallerY=scrShotImgHeight || biggerX>=scrShotImgWidth) {
      return false;
      }
      xor = keyImageRGBData[smallerY][smallerX]^screenShotImageRGBData[biggerY][biggerX];
      if(xor!=0) {
      return false;
      }
      }
      biggerX = x;
      }
      return true;
      } /**
    • 输出查找到的坐标数据
      */
      private void printFindData() {
      for(int y=0; y<keyImgHeight; y++) {
      for(int x=0; x<keyImgWidth; x++) {
      System.out.print(“(“+this.findImgData[y][x][0]+”, “+this.findImgData[y][x][1]+”)”);
      }
      System.out.println();
      }
      }
    • public static void main(String[] args) {
      String keyImagePath = “D:/key.png”;
      ImageFindDemo demo = new ImageFindDemo(keyImagePath);
      demo.printFindData();
      }

}

PHP用CURL发送Content-type为application/json的POST请求方法

PHP发送JSON POST
/**
 * Curl版本
 * 使用方法:
 * $post_string = "app=request&version=beta";
 * request_by_curl('http://www.waitalone.cn/restServer.php', $post_string);
 */
function json_post($url, $data = NULL)
{

    $curl = curl_init();

    curl_setopt($curl, CURLOPT_URL, $url);
    curl_setopt($curl, CURLOPT_SSL_VERIFYPEER, false);
    curl_setopt($curl, CURLOPT_SSL_VERIFYHOST, false);
    if(!$data){
        return 'data is null';
    }
    if(is_array($data))
    {
        $data = json_encode($data);
    }
    curl_setopt($curl, CURLOPT_POST, 1);
    curl_setopt($curl, CURLOPT_POSTFIELDS, $data);
    curl_setopt($curl, CURLOPT_HEADER, 0);
    curl_setopt($curl, CURLOPT_HTTPHEADER,array(
        'Content-Type: application/json; charset=utf-8',
        'Content-Length:' . strlen($data),
        'Cache-Control: no-cache',
        'Pragma: no-cache'
    ));
    curl_setopt($curl, CURLOPT_RETURNTRANSFER, 1);
    $res = curl_exec($curl);
    $errorno = curl_errno($curl);
    if ($errorno) {
        return $errorno;
    }
    curl_close($curl);
    return $res;

}

PHP接受JSON POST
$data = json_decode(file_get_contents('php://input'), true);

swoole 如何实现 websocket 客户端的功能?

workerman 的 websocket 客服端是这样实现的:(很简单,很容易就使用了,和前端差不多)

http://doc.workerman.net/315306

swoole 如何实现这样相同的功能呢?

看了一下的一些文档,始终无法理解:

https://github.com/matyhtf/framework/blob/master/libs/Swoole/Client/WebSocket.php

https://wiki.swoole.com/wiki/page/p-http_client.html

如果只是获取的话,比较简单:

<?php
$client = new swoole_client(SWOOLE_SOCK_TCP, SWOOLE_SOCK_ASYNC);

$client->on('connect', function($cli) {
    $cli->send("GET / HTTP/1.1\r\n\r\n");
});
$client->on('receive', function($cli, $data) {
    echo "Received: " . $data;
});
$client->on('error', function($cli) {
    echo "Connect failed\r\n\r\n";
});
$client->on('close', function($cli) {
    echo "Connection close\r\n\r\n";
});

$client->connect('xxx.xxx.xxx.xxx', 1234, true);

但是我这里想要实现的是类似 workerman 那样的,可以一直连接。

使用PHP客户端连接到websocket

这是我一直在使用的代码,已经在不同的论坛上广泛发布.但由于某种原因,我无法让它发挥作用.

任何帮助,将不胜感激.

$host = 'host';  //where is the websocket server
$port = 443; //ssl
$local = "http://www.example.com/";  //url where this script run
$data = '{"id": 2,"command": "server_info"}';  //data to be send

$head =        "GET / HTTP/1.1"."\r\n".
               "Upgrade: WebSocket"."\r\n".
               "Connection: Upgrade"."\r\n".
               "Origin: $local"."\r\n".
               "Host: $host"."\r\n".
               "Content-Length: ".strlen($data)."\r\n"."\r\n";
////WebSocket handshake
$sock = fsockopen($host, $port, $errno, $errstr, 2);
fwrite($sock, $head ) or die('error:'.$errno.':'.$errstr);
$headers = fread($sock, 2000);
fwrite($sock, "\x00$data\xff" ) or die('error:'.$errno.':'.$errstr);
$wsdata = fread($sock, 2000);  //receives the data included in the websocket package "\x00DATA\xff"
$retdata = trim($wsdata,"\x00\xff"); //extracts data
////WebSocket handshake
fclose($sock);

echo $retdata;

我可能更喜欢使用现有的websocket客户端库(可能是 https://github.com/gabrielbull/php-websocket-client或 https://github.com/Devristo/phpws/tree/master/src/Devristo/Phpws/Client?)而不是自己滚动,但我至少通过使用以下方式连接:

$head = "GET / HTTP/1.1"."\r\n".
    "Host: $host"."\r\n".
    "Upgrade: websocket"."\r\n".
    "Connection: Upgrade"."\r\n".
    "Sec-WebSocket-Key: asdasdaas76da7sd6asd6as7d"."\r\n".
    "Sec-WebSocket-Version: 13"."\r\n".
    "Content-Length: ".strlen($data)."\r\n"."\r\n";

我的服务器正在使用TLS / SSL,所以我还需要:

$sock = fsockopen('tls://'.$host, $port, $errno, $errstr, 2);

完整的协议规范是:https://tools.ietf.org/rfc/rfc6455.txt

WebSocket\Server

1.7.9增加了内置的WebSocket服务器支持,通过几行PHP代码就可以写出一个异步非阻塞多进程的WebSocket服务器。

$server = new Swoole\WebSocket\Server("0.0.0.0", 9501);

$server->on('open', function (Swoole\WebSocket\Server $server, $request) {
    echo "server: handshake success with fd{$request->fd}\n";
});

$server->on('message', function (Swoole\WebSocket\Server $server, $frame) {
    echo "receive from {$frame->fd}:{$frame->data},opcode:{$frame->opcode},fin:{$frame->finish}\n";
    $server->push($frame->fd, "this is server");
});

$server->on('close', function ($ser, $fd) {
    echo "client {$fd} closed\n";
});

$server->start();

onRequest回调

WebSocket\Server 继承自 Http\Server

  • 设置了onRequest回调,WebSocket\Server也可以同时作为http服务器
  • 未设置onRequest回调,WebSocket\Server收到http请求后会返回http 400错误页面
  • 如果想通过接收http触发所有websocket的推送,需要注意作用域的问题,面向过程请使用globalWebSocket\Server进行引用,面向对象可以把WebSocket\Server设置成一个成员属性

1、面向过程代码

$server = new Swoole\WebSocket\Server("0.0.0.0", 9501);
$server->on('open', function (Swoole\WebSocket\Server $server, $request) {
        echo "server: handshake success with fd{$request->fd}\n";
    });
$server->on('message', function (Swoole\WebSocket\Server $server, $frame) {
        echo "receive from {$frame->fd}:{$frame->data},opcode:{$frame->opcode},fin:{$frame->finish}\n";
        $server->push($frame->fd, "this is server");
    });
$server->on('close', function ($ser, $fd) {
        echo "client {$fd} closed\n";
    });
$server->on('request', function (Swoole\Http\Request $request, Swoole\Http\Response $response) {
    global $server;//调用外部的server
    // $server->connections 遍历所有websocket连接用户的fd,给所有用户推送
    foreach ($server->connections as $fd) {
        // 需要先判断是否是正确的websocket连接,否则有可能会push失败
        if ($server->isEstablished($fd)) {
            $server->push($fd, $request->get['message']);
        }
    }
});
$server->start();

2、面向对象代码

class WebsocketTest {
    public $server;
    public function __construct() {
        $this->server = new Swoole\WebSocket\Server("0.0.0.0", 9501);
        $this->server->on('open', function (swoole_websocket_server $server, $request) {
            echo "server: handshake success with fd{$request->fd}\n";
        });
        $this->server->on('message', function (Swoole\WebSocket\Server $server, $frame) {
            echo "receive from {$frame->fd}:{$frame->data},opcode:{$frame->opcode},fin:{$frame->finish}\n";
            $server->push($frame->fd, "this is server");
        });
        $this->server->on('close', function ($ser, $fd) {
            echo "client {$fd} closed\n";
        });
        $this->server->on('request', function ($request, $response) {
            // 接收http请求从get获取message参数的值,给用户推送
            // $this->server->connections 遍历所有websocket连接用户的fd,给所有用户推送
            foreach ($this->server->connections as $fd) {
                // 需要先判断是否是正确的websocket连接,否则有可能会push失败
                if ($this->server->isEstablished($fd)) {
                    $this->server->push($fd, $request->get['message']);
                }
            }
        });
        $this->server->start();
    }
}
new WebsocketTest();

客户端

  • Chrome/Firefox/高版本IE/Safari等浏览器内置了JS语言的WebSocket客户端
  • 微信小程序开发框架内置的WebSocket客户端
  • 异步的PHP程序中可以使用Swoole\Http\Client作为WebSocket客户端
  • apache/php-fpm或其他同步阻塞的PHP程序中可以使用swoole/framework提供的同步WebSocket客户端
  • WebSocket客户端不能与WebSocket服务器通信

swoole 的websocket 连接

介绍下两种连接websocket服务器的方法

1 通过访问http服务器然后访问我们的html页面连接我们的websouket服务器,这样我们就需要3个文件。1 http.php  2 ws.php 3 ws.html 简单贴下我的图,也是参考官方文档写的

http.php

<?php
/**

  • Created by PhpStorm.
  • User: xyj
  • Date: 18-9-9
  • Time: 下午4:31
    */

//实例化
$http_server = new swoole_http_server(‘0.0.0.0’,9501);

//服务器配置
$http_server->set(
[
‘enable_static_handler’ => true,
‘document_root’ => ‘/www/wwwroot/test_project/swoole’,
]
);

$http_server->on(‘request’,function($request ,$response){
//print_r($request->get);
//设置响应头信息
$response->cookie(‘xyj’,’hello’,86400);
//服务器返回信息
$response->end(‘http_server’ . json_encode($request->get));
});

$http_server->start();
ws.php

<?php
/**

  • Created by PhpStorm.
  • User: xyj
  • Date: 18-9-9
  • Time: 下午5:02
    */

//创建websocket服务器对象,监听0.0.0.0:9502端口
$ws = new swoole_websocket_server(“0.0.0.0”, 9502);

$ws->set(
[
‘enable_static_handler’ => true,
‘document_root’ => ‘/www/wwwroot/test_project/swoole’,
]
);
//监听WebSocket连接打开事件
$ws->on(‘open’, function ($ws, $request) {
var_dump($request->fd, $request->get, $request->server);
$ws->push($request->fd, “hello, welcome\n”);
});

//监听WebSocket消息事件
$ws->on(‘message’, function ($ws, $frame) {
echo “Message: {$frame->data}\n”;
$ws->push($frame->fd, “server: {$frame->data}”);
});

//监听WebSocket连接关闭事件
$ws->on(‘close’, function ($ws, $fd) {
echo “client-{$fd} is closed\n”;
});

$ws->start();
ws.html



Document

hello swoole 测试

如果是在本地测试环境运行那么wsServer = ‘ws://127.0.0.1:9502’,如果是外网 那么127.0.0.1 就要改成你浏览器上所访问的地址,这里的端口必须要和你websocket 服务器端口一致否则无法访问的

准备工作做好了那么我们就开始访问下websocket,首先在终端 运行http.php 和 ws.php, 正常情况下是这样如果遇到错误一般常见的就是端口被占用,如果是swoole扩展没装好或者是语法错误,那么请在仔细的读啃下官方文档。

如果是端口被占用我们可以使用 lsof -i:9501 命令查看 端口信息, 在kill之前请先认真的看下 该端口是否是重要端口,不要盲目的kill 。

还有一种就是我们开启过http.php 在关闭后 可能被占用的端口没有及时的释放,当我们再次php http.php的时候他也会抛出端口被占用的错误,这里我们可以用 这个命令 netstat -ntlp 来查看tcp监听的端口,其父进程 kill 然后在运行就ok了,继续使用这个命令来查看tcp端口如图

走到这里我们的两个服务器都已经开启了 。我们就可以通过浏览器访问了,在浏览器上输入地址:9501 , 此时这里的端口是要个http服务器的端口一致而不是websocket的端口,访问成功会出现如下图

————————————————
版权声明:本文为CSDN博主「君子有攸往」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.csdn.net/xiao199306/article/details/82621140

swoole的process模块创建和使用子进程

Version:1.0 StartHTML:000000210 EndHTML:000047154 StartFragment:000003232 EndFragment:000047098 StartSelection:000003301 EndSelection:000047086 SourceURL:https://www.cnblogs.com/jkko123/p/10918056.html swoole的process模块创建和使用子进程 – 怀素真 – 博客园

swoole中为我们提供了一个进程管理模块 Process,替换PHP的 pcntl 扩展,方便我们创建进程,管理进程,和进程间的通信。

swoole提供了2种进程间的通信:

1、基于 unix socket 的管道 pipe。

2、基于 sysvmsg 的消息队列。

我们可以通过 new swoole_process() 快速的创建一个进程,默认会创建一个 SOCK_DGRAM 类型的管道,用于进程间的通信,当然可以设置成其他类型,也可以不创建。

一、通过同步阻塞管道进行进程间通信?

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748<?php $worker_process_nums = 5;$worker_process = []; for ($i = 0; $i < $worker_process_nums; $i++) {//创建子进程//默认为每个子进程创建一个管道,如果不想创建设置$pipe_type参数为false//注意管道默认是同步阻塞,半双工,如果读取不到数据就会阻塞$worker = new swoole_process(function (swoole_process $worker) {//注意,如果主进程中不写数据write(),那么子进程这里read()就会阻塞$task = json_decode($worker->read(), true); //进行计算任务$tmp = 0;for ($i = $task['start']; $i < $task['end']; $i++) {$tmp += $i;} echo '子进程 PID : ', $worker->pid, ' 计算 ', $task['start'], ' - ', $task['end'], ' 结果 : ', $tmp, PHP_EOL;//往管道中写入计算的结果$worker->write($tmp);//子进程退出$worker->exit();}); //保存子进程$worker_process[$i] = $worker; //启动子进程$worker->start();} //往每个子进程管道中投递任务for ($i = 0; $i < $worker_process_nums; $i++) {$worker_process[$i]->write(json_encode(['start' => mt_rand(1, 10),'end' => mt_rand(50, 100),]));} //父进程监听子进程退出信号,回收子进程,防止出现僵尸进程swoole_process::signal(SIGCHLD, function ($sig) {//必须为false,非阻塞模式while ($ret = swoole_process::wait(false)) {echo "子进程 PID : {$ret['pid']} 退出\n";}});

二、通过 swoole_event_add 将管道设为异步,来进行通信?

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556<?php $worker_process_nums = 5;$worker_process = []; for ($i = 0; $i < $worker_process_nums; $i++) {$worker = new swoole_process(function ($worker) {//在子进程中给管道添加事件监听//底层会自动将该管道设置为非阻塞模式//参数二,是可读事件回调函数,表示管道可以读了swoole_event_add($worker->pipe, function ($pipe) use ($worker) {$task = json_decode($worker->read(), true); $tmp = 0;for ($i = $task['start']; $i < $task['end']; $i++) {$tmp += $i;}echo "子进程 : {$worker->pid} 计算 {$task['start']} - {$task['end']} \n";//子进程把计算的结果,写入管道$worker->write($tmp);//注意,swoole_event_add与swoole_event_del要成对使用swoole_event_del($worker->pipe);//退出子进程$worker->exit();});}); $worker_process[$i] = $worker; //启动子进程$worker->start();} for ($i = 0; $i < $worker_process_nums; $i++) {$worker = $worker_process[$i]; $worker->write(json_encode(['start' => mt_rand(1, 10),'end' => mt_rand(50, 100),])); //主进程中,监听子进程管道事件swoole_event_add($worker->pipe, function ($pipe) use ($worker) {$result = $worker->read();echo "子进程 : {$worker->pid} 计算结果 {$result} \n";swoole_event_del($worker->pipe);});} //父进程监听子进程退出信号,回收子进程,防止出现僵尸进程swoole_process::signal(SIGCHLD, function ($sig) {//必须为false,非阻塞模式while ($ret = swoole_process::wait(false)) {echo "子进程 PID : {$ret['pid']} 退出\n";}});

三、使用消息队列来完成进程间通信?

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152<?php $worker_process_nums = 5;$worker_process = []; for ($i = 0; $i < $worker_process_nums; $i++) {//注意,这里将参数$pipe_type设为false,表示不创建管道$worker = new swoole_process(function ($worker) {$task = json_decode($worker->pop(), true); $tmp = 0;for ($i = $task['start']; $i < $task['end']; $i++) {$tmp += $i;}echo "子进程 : {$worker->pid} 计算 {$task['start']} - {$task['end']} \n";$worker->push($tmp);$worker->exit();}, false, false); //使用消息队列,作为进程间的通信//注意,消息队列是共享的$worker->useQueue(); $worker_process[$i] = $worker; //启动子进程$worker->start();} for ($i = 0; $i < $worker_process_nums; $i++) {//只需用一个子进程发送消息即可,因为消息队列是共享的$worker_process[0]->push(json_encode(['start' => mt_rand(1, 10),'end' => mt_rand(50, 100),]));} //注意,这里要暂停,防止加入队列的任务,立刻被主进程读出来。sleep(1); for ($i = 0; $i < $worker_process_nums; $i++) {$result = $worker_process[0]->pop();echo "计算结果 : {$result} \n";} //父进程监听子进程退出信号,回收子进程,防止出现僵尸进程swoole_process::signal(SIGCHLD, function ($sig) {//必须为false,非阻塞模式while ($ret = swoole_process::wait(false)) {echo "子进程 PID : {$ret['pid']} 退出\n";}});

四、进程可以通过 signal 监听信号,和 alarm 设置定时器。

我们可以在父进程上设置监听信号,当子进程退出时,重新挂起子进程。

也可以设置定时器,通过 swoole_process::kill($pid, 0); 定时检测进程是否存活。?

1234567891011121314151617181920212223<?php //每隔1秒触发SIGALAM信号//注意,alarm不能和Timer同时使用swoole_process::alarm(1000 * 1000, 0); swoole_process::signal(SIGALRM, function ($signo) {static $cnt = 0;$cnt++;echo "时钟定时信号\n"; if ($cnt > 10) {//清除定时器swoole_process::alarm(-1);}}); swoole_process::signal(SIGINT, function ($signo) {echo "我被ctrl+c了\n"; //退出主进程,不然将一直无法正常退出exit(0);});

https://www.cnblogs.com/jkko123/p/10918056.html

版权声明:博主文章,可以不经博主允许随意转载,随意修改,知识是用来传播的。