作者:罗源凯

coTrun

https://github.com/coturn/coturn
https://github.com/bprodoehl/docker-turnserver
带有Coturn STUN和TURN server (https://github.com/coturn/coturn)的Docker容器,
ocker run -d -e EXTERNAL_IP=1.2.3.4 –name=turnserver –restart=”on-failure:10″ –net=host -p 3478:3478 -p 3478:3478/udp bprodoehl/turnserver
环境参数
SKIP_AUTO_IP-绑定到地址,当与–net =host一起运行时,对IPv4和IPv6双协议栈很有用,EXTERNAL_IP –可选手动指定的外部IP地址端口–监听端口用于STUN和TURNLISTEN_ON_PUBLIC_IP –绑定到外部IPUSE_IPV4 –在确定外部IP时强制IPv4

下载安装

一、ubuntu安装软件包
# apt install coturn

二、配置coturnls
1、复制DTLS、TLS支持的证书文件:
# cp/usr/share/coturn/examples/etc/turn_server_cert.pem /etc/turn_server_cert.pem
# cp/usr/share/coturn/examples/etc/turn_server_pkey.pem /etc/turn_server_pkey.pem
2、编辑/etc/turnserver.conf文件:
listening-port=3478
tls-listening-port=5349
listening-ip=192.168.1.230
external-ip=47.104.19.243(NAT必须)
lt-cred-mech
user=tcp:123456(必须)
realm=example.com(必须)
3、编辑/etc/default/coturn文件:
TURNSERVER_ENABLED=1(必须)

三、完成安装
1、重启coturn
# service coturn restart

2、测试验证
# turnadmin -a -u test -r leifeng-elec.com-p test
# turnutils_uclient 192.168.1.230 -u test -wtest

可以先安装环境
sudo apt-get install libssl-dev
sudo apt-get install libevent-dev
sudo apt-get install libpq-dev
sudo apt-get install mysql-client
sudo apt-get install libmysqlclient-dev
sudo apt-get install libhiredis-dev
sudo apt-get install git

git clone https://github.com/coturn/coturn
./configure --prefix=/usr/local/coturn
make && make install

1) If your system supports automatic start-up system daemon services, 
then to enable the turnserver as a system service that is automatically
started, you have to:

    a) Create and edit /etc/turnserver.conf or 
    /usr/local/etc/turnserver.conf . 
    Use /usr/local/etc/turnserver.conf.default as an example.

    b) For user accounts settings: set up SQLite or PostgreSQL or 
    MySQL or MongoDB or Redis database for user accounts.
    Use /usr/local/share/turnserver/schema.sql as SQL database schema,
    or use /usr/local/share/turnserver/schema.userdb.redis as Redis
    database schema description and/or 
    /usr/local/share/turnserver/schema.stats.redis
    as Redis status & statistics database schema description.

    If you are using SQLite, the default database location is in 
    /var/db/turndb or in /usr/local/var/db/turndb or in /var/lib/turn/turndb.

    c) add whatever is necessary to enable start-up daemon for the 
    /usr/local/bin/turnserver.

2) If you do not want the turnserver to be a system service, 
   then you can start/stop it "manually", using the "turnserver" 
   executable with appropriate options (see the documentation).

3) To create database schema, use schema in file 
/usr/local/share/turnserver/schema.sql.

4) For additional information, run:

   $ man turnserver
   $ man turnadmin
   $ man turnutils

ERROR: OpenSSL Crypto development libraries are not installed properly in required location.
apt-get install libssl-dev

生成md5码:turnadmin -k –u 用户名 -r nanjing -p 密码

生成证书
sudo openssl req -x509 -newkey rsa:2048 -keyout /etc/turn_server_pkey.pem -out /etc/turn_server_cert.pem -days 99999 -nodes

创建turnuserdb.conf文件
vi /etc/turnuserdb.conf
在其中填入之前生成的用户名和key

配制参数 编辑文件/etc/turnserver.conf
listening-port=3478
external-ip=公网IP
user=username:password
realm=域名
以上常用,以下按需
listening-device=填写自己的网卡类型
relay-device=填写自己的网卡类型
listening-ip=ip地址
listening-port=3478
tls-listening-port=5349
listening-ip与relay-ip采用内网ip,external-ip是外网的ip
relay-ip=ip地址
external-ip=ip地址
relay-threads=50
lt-cred-mech
static-auth-secret=用户名
user=用户名:密钥
userdb=/etc/turnuserdb.conf
#max-bps=102400
pidfile="/var/run/turnserver.pid"
no-loopback-peers
no-multicast-peers
sha256
mobility
no-cli
cert=/etc/turn_server_cert.pem
pkey=/etc/turn_server_pkey.pem
stale-nonce
use-auth-secret
Verbose
fingerprint

运行:
sudo turnserver -L 23.83.233.168 -o -a -b /etc/turnuserdb.conf -f -r nanjing

修改信令连接
 var pcConfig = {
            'iceServers': [
                {
                    'url': 'stun:xx.xx.xx.xxip:3478',
                },
                {
                    'url': 'stun:stun.xx.xx.xx.xxip:3478',
                },
                {
                'urls': 'turn:xxx.cn:3478',
                'credential': "mypasswd", or 密钥
                'username': "myname"
            }]
        };

可以根据需要调整其他参数。有关更多信息,请查看Coturn帮助页面:
https://github.com/coturn/coturn/wiki/turnserver
https://github.com/coturn/coturn/wiki/CoturnConfig
编辑文件/etc/default/coturn并取消注释TURNSERVER_ENABLED=1,以便TURN服务器自动启动为系统服务守护程序。

在防火墙中打开以下端口:
3478 TCP和UDP。
49152 – 65535 UDP:按照 RFC 5766,这些是TURN服务器用于交换媒体的端口。可以使用TURN服务器上的min-port和max-port参数更改这些端口。
注意
虽然RFC指定了TURN使用的端口,但如果使用STUN,则需要打开所有UDP端口,因为STUN不会限制可能使用的端口范围。

测试地址
https://webrtc.github.io/samples/src/content/peerconnection/trickle-ice/

Wireshark

mac下用Wireshark抓包报错you don’t have permission to capture on that device

cd /dev
ls -la | grep bp

看到用户组所属
crw——- 1 root wheel 23, 0 6 23 23:42 bpf0
crw——- 1 root wheel 23, 1 6 23 23:42 bpf1
crw——- 1 root wheel 23, 2 6 23 23:43 bpf2
crw——- 1 root wheel 23, 3 6 23 23:42 bpf3

这样导致上述没有权限问题,需要手动修改,确保所有列表有你的用户名和admin作为用户组。
whoami 命令看你当前用户名
sudo chown 上面看到的用户名:admin bp*

test:
https://webrtc.github.io/samples/src/content/peerconnection/trickle-ice

socket.io for java

Server

<dependency>
      <groupId>com.corundumstudio.socketio</groupId>
      <artifactId>netty-socketio</artifactId>
      <version>1.7.16</version>
    </dependency>
    <dependency>
      <groupId>org.slf4j</groupId>
      <artifactId>slf4j-nop</artifactId>
      <version>1.7.2</version>
    </dependency>

public class ServerStart {

    public static void main(String [] args){
        Configuration config = new Configuration();
        config.setHostname("localhost");
        config.setPort(9999);
        SocketIOServer server = new SocketIOServer(config);
        server.addConnectListener(new ConnectListener() {
            // 添加客户端连接监听器
            public void onConnect(SocketIOClient client) {
                //logger.info(client.getRemoteAddress() + " web客户端接入");
                //不知道如何与客户端对应,好的办法是自己去写对应的函数
                client.sendEvent("connected", "hello");
            }
        });

        //监听客户端事件,client_info为事件名称,-自定义事件
         server.addEventListener("client_info", String.class, new DataListener<String>(){
            public void onData(SocketIOClient client, String data, AckRequest ackRequest) throws ClassNotFoundException {
                //客户端推送advert_info事件时,onData接受数据,这里是string类型的json数据,还可以为Byte[],object其他类型
                String sa = client.getRemoteAddress().toString();
                String clientIp = sa.substring(1,sa.indexOf(":"));//获取客户端连接的ip
                Map params = client.getHandshakeData().getUrlParams();//获取客户端url参数
                System.out.println(clientIp+":客户端:************"+data);
            }
         });

        //添加客户端断开连接事件
        server.addDisconnectListener(new DisconnectListener(){
            public void onDisconnect(SocketIOClient client) {
                String sa = client.getRemoteAddress().toString();
                String clientIp = sa.substring(1,sa.indexOf(":"));//获取设备ip
                System.out.println(clientIp+"-------------------------"+"客户端已断开连接");
                //给客户端发送消息
                client.sendEvent("advert_info",clientIp+"客户端你好,我是服务端,期待下次和你见面");
            }
        });
        server.start();

        while (true){
            try {
                Thread.sleep(1500);
                //广播消息
                server.getBroadcastOperations().sendEvent("borcast","are you live?");
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }
}

Client

        <dependency>
            <groupId>io.socket</groupId>
            <artifactId>socket.io-client</artifactId>
            <version>1.0.0</version>
        </dependency>

public class AppStart {

    public static void main(String [] args){
        String url ="http://localhost:9999";
        try{
            IO.Options options = new IO.Options();
            options.transports = new String[]{"websocket"};
            options.reconnectionAttempts = 2;
            options.reconnectionDelay = 1000;//失败重连的时间间隔
            options.timeout = 500;//连接超时时间(ms)
            //par1 是任意参数
            final Socket socket = IO.socket(url+"?par1=1234", options);

            socket.on(Socket.EVENT_CONNECT, new Emitter.Listener() {

                public void call(Object... args) {
                    socket.send("hello");
                }
            });

            //自定义事件
            socket.on("borcast", new Emitter.Listener() {
                public void call(Object... objects) {
                    System.out.println("receive borcast data:" + objects[0].toString());
                }
            });

            socket.on("connected", new Emitter.Listener() {
                public void call(Object... objects) {
                    System.out.println("receive connected data:" + objects[0].toString());
                }
            });

            socket.connect();
            //循环发送数据
            while (true){
                socket.emit("client_info"," 客户端在发送数据");
                Thread.sleep(2000);
            }
        }catch (Exception ex){
            ex.printStackTrace();
        }
    }
}

VUE
https://www.npmjs.com/package/vue-socket.io

import VueSocketIO from 'vue-socket.io'
import SocketIO from 'socket.io-client'

Vue.use(new VueSocketIO({
  debug: true,
  connection: SocketIO('http://192.168.88.108:443', {
    path: '', // path千万不要写“/”
    transports: ['websocket', 'xhr-polling', 'jsonp-polling']
  }) // options object is Optional
}))

Other https://github.com/probil/vue-socket.io-extended

import Vue from 'vue'
import VueSocketIOExt from 'vue-socket.io-extended'
import SocketIO from 'socket.io-client';

const socket = SocketIO('https://localhost');
export default ({ store }) => {
  Vue.use(VueSocketIOExt, socket, { store });
}

sockets: {
      // 创建连接
      connect() {
        console.log('连接成功啦')
      },
      // 监听断开连接,函数
      disconnect() {
        console.log('断开服务器连接');
        this.connectdisabled = false;
        this.leavedisabled = true;
        this.senddisabled = true;
        this.inputdisabled = true;
      },
      reconnect() {
        console.log("重新链接");
      },
      // 监听自定义事件,需与服务端emit事件名一致
      joined(room, id) {
        console.log('joined',room, id)
        this.connectdisabled = true;
        this.leavedisabled = false;
        this.senddisabled = false;
        this.inputdisabled = false;
      },

//发送加入信令
this.$socket.client.emit('join', this.room)

ESLint

ESLint
规则的细节请到ESLint官方网站查看 http://eslint.org/docs/rules/

配置esLint
eslint配置方式主要有两种:

注释配置:使用js注释来直接嵌入ESlint配置信息到一个文件里
配置文件:使用一个js,JSON或者YAML文件来给整个目录和它的子目录指定配置信息。这些配置可以写成一个文件名.eslintrc.*的文件或者package.json文件里的eslintConfig项里
这两种方式ESlint都会自动寻找然后读取,也可以直接在命令行内指定一个配置文件。
一般需要我们去配置项包括:

环境:你的脚本会在哪种环境下运行。每个环境带来了一组特定的预定义的全局变量。
全局变量:脚本运行期间会访问额外的全局变量。
规则:使用那些规则,并且规则的等级是多少。
vue-cli脚手架安装完成后,主要有几个地方配置了eslint。

1,项目创建后项目中就会出现.eslintignore 和.eslintrc.js两个文件
.eslintignore类似Git的.gitignore用来配置不需要Eslint检查的文件
.eslintrc.js主要用来配置具体规则

.eslintignore文件

添加不启动静态检查的文件

build/*.js // 忽略build文件夹下所有的脚本文件
config/*.js
.eslintrc.js文件

// https://eslint.org/docs/user-guide/configuring
module.exports = {
  root: true,
  parser: 'babel-eslint', // 解析器为babel-eslint,可在package.json文件中配置
  parserOptions: {
    sourceType: 'module'
  },
  env: { //环境配置为浏览器
    browser: true,
  },
  // https://github.com/standard/standard/blob/master/docs/RULES-en.md
  extends: 'standard', //文件配置继承standard规则,具体访问连接
  // required to lint *.vue files
  plugins: [
    'html'
  ],
  // add your custom rules here
  'rules': { // 添加自定义规则
    // allow paren-less arrow functions
    'arrow-parens': 0,
    // allow async-await
    'generator-star-spacing': 0,
    // allow debugger during development
    'no-debugger': process.env.NODE_ENV === 'production' ? 2 : 0
  }
}
说明: 在rules中每个配置项后面的第一个值为eslint规则的错误等级

"off" 或 0 (关闭这条规则)
"warn" 或 1 (违反规则会警告,不影响项目运行)
"error" 或 2 (违反规则会报错,终止项目运行)
2 在package.json文件中配置文件

"script" : {
    "lint": "eslint --ext .js, .vue src"
}
"devDenpendencies" : {
    "babel-eslint": "^7.1.1",
    // 更多eslint文件
    ...
}
#### 3 在webpack配置文件中 ####

webpack.base.conf.js

  module: {
    rules: [
      {
        test: /\.(js|vue)$/,
        loader: 'eslint-loader',
        enforce: 'pre',
        include: [resolve('src'), resolve('test')],
        options: {
          formatter: require('eslint-friendly-formatter')
        }
      }
    ]
  }
有时候代码里有些特殊情况需要我们在某一行或者某几行关闭ESLint检测,可以使用注释:

1,注释关闭所有规则

/* eslint-disable */
alert('foo');
/* eslint-enable */
2,关闭某一行的所有规则

alert('foo'); // eslint-disable-line
// eslint-disable-next-line
alert('foo');
3,在某一行关闭指定的规则

alert('foo'); // eslint-disable-line no-alert
// eslint-disable-next-line no-alert
alert('foo');

babel-loader

https://www.webpackjs.com/loaders/babel-loader/
““
webpack 3.x | babel-loader 8.x | babel 7.x
npm install babel-loader@8.0.0-beta.0 @babel/core @babel/preset-env webpack

webpack 3.x babel-loader 7.x | babel 6.x
npm install babel-loader babel-core babel-preset-env webpack

编译工具
npm i babel-cli // 这是babel解释器的客户端主程序
npm i babel-core // babel的核心文件,好像默认会自动安装
npm i babel-preset-es2015 // 把代码转换成ES6
npm i babel-preset-stage-0 // 把代码转换成ES7

在package.json中修改运行脚本
–exec babel-node
在根路径创建.babelrc文件,写入配置
.babelrc
{
“presets”: [“es2015″,”stage-0”]
}

WebRtc

https://appr.tc/

WebRTC adapter.js
github地址为:https://github.com/webrtc/adapter.
FYI: http://www.vue5.com/webrtc/webrtc.html
主要功能是隐藏像webkitRTCPeerConnection和mozRTCPeerConnection这样的前缀差异,并提供函数将MediaStream附加到HTML的或元素。
– 直接在代码中加入https://webrtc.github.io/adapter/adapter-latest.js
– npm install webrtc-adapter
使用时在JavaScript文件中加入一个模块
const adapter = require(‘webrtc-adapter’);
加载完成后既可以使用,例如可使用
console.log(adapter.browserDetails.browser);
or
import adapter from ‘webrtc-adapter’;
“child_process”: “^1.0.2”,
“fs”: “0.0.1-security”,
“webrtc-adapter”: “^7.5.1”,
“window”: “^4.2.7”
“net”: “^1.0.2”,
“tls”: “0.0.1”,

vue socket.io的github地址:https://github.com/MetinSeylan/Vue-Socket.io

npm install vue-socket.io --save
npm install socket.io-client --save

chrome里无法正常触发vue-socket.io中的connect回调,vue-socket.io使用到了大量es6的语法,考虑到原生es6在浏览器中的兼容性,导入babel-loader(nuxt cli中没有自带),
{src: ‘@/plugins/vue-socket.io’, ssr: false}

webSocket连接的地址是固定的

在main.js中直接这样写
    Vue.use(new VueSocketIO({
      debug: true,
      connection: 'socket地址:端口号',
      vuex: {       // 不需要用到vuex这个可以不加
            store,
            actionPrefix: 'SOCKET_',
            mutationPrefix: 'SOCKET_'
      }
}))

socket可以与vuex结合使用

import store from'./yourstore'
Vue.use(VueSocketio,socketio('http://socketserver.com:1923'), store);

socket地址是静态的写法

mounted(){
this.$socket.emit('connect', 1)
}

npm install –save-dev @gauseen/nuxt-proxy
// nuxt.config.js

modules: [
// 请求代理配置,解决跨域
‘@gauseen/nuxt-proxy’,
],
proxyTable: {
‘/api’: { target: ‘http://example.com’, ws: false }
},
注:
/api ———————— 每个接口特定标识字段 [String]
target —————— 目标代理服务 [String]
ws —————————— 是否支持 websocket 代理 [Boolean]

Nuxt.js

基于Vue2,支持Vue Router,Vuex,Vue Server Renderer(spa),Vue-meta

https://zh.nuxtjs.org/
https://www.nuxtjs.cn/

1,模版安装
https://github.com/nuxt-community/koa-template
vue init nuxt-community/koa-template <project-name>
cd <project-name> # move to your project
需将nuxt降级到1.4.2
npm install # or yarn install*[see note below]

npm i eslint-plugin-html@^3

2.脚手架安装
npm install -g npx
(npx在NPM版本5.2.0默认安装了):
$ npx create-nuxt-app <项目名>

rendering mode :Universal ->否开启SSR服务端渲染

$ cd <project-name>
$ npm run dev 运行开发模式

npm run build 打包构建
npm start 正式运行

npm install --update-binary 重新编译

问题一:使用ES6的 import/export,不支持import需使用babel

解决方案:
    1 在启动参数中加上  --exec babel-node
    2 在根目录下增加.babelrc配制文件
    {
        "presets":["es2015"]
    }
    3 npm install babel-preset-es2015
    npm install babel-cli -S

    deprecated babel-preset-es2015@6.24.1: ????  Thanks for using Babel: we recommend using babel-preset-env now: please read https://babeljs.io/env to update!
+ babel-preset-es2015@6.24.1
failed to start process, "babel-node" exec not found

Module build failed:Error:Plugin/Preset files are not allowed to export objects
1 删除    npm uninstall –save babel-loader
npm uninstall –save @babel/core
    2 新安装    npm install –save -dev babel-loader@^7

问题二:安装 sass

安装依赖
 "fibers": "^4.0.2","sass": "^1.26.3"
Windows + OS X instructions here: https://github.com/nodejs/node-gyp
Ubuntu users please run: `sudo apt-get install g++ build-essential`
RHEL users please run: `yum install gcc-c++` and `yum groupinstall 'Development Tools'`
Alpine users please run: `sudo apk add python make g++`
OS
xcode-select --install
后出现错误
xcode-select: error: command line tools are already installed, use "Software Update" to install updates
解决办法:
1.使用softwareupdate --list查看可用的内容,然后softwareupdate --install -a安装所有更新或softwareupdate --install <product name>只安装Xcode更新。

2.$ rm -rf /Library/Developer/CommandLineTools
$ xcode-select --install

3. Apple 官网下载最新版本的Command Line Tools安装即可
https://developer.apple.com/download/more/
通过brew config查看当前CLT版本号是否变更
CLT: 1103.0.32.62 -> 11.5.0.0.1.1588476445
Xcode: 11.4


npm install  sass fibers

This relative module was not found:
** npm install sass-loader node-sass**
安装后可以会有提示:
npm WARN acorn-jsx@5.0.0 requires a peer of acorn@^6.0.0 but none is installed. You must install peer dependencies yourself.
npm WARN eslint-plugin-vue@4.7.1 requires a peer of eslint@^3.18.0 || ^4.0.0 but none is installed. You must install peer dependencies yourself.

此时,需要安装:
$ npm i eslint@^3.18.0
$ npm i acorn@^6.0.0
3. 修改 nuxt.config.js
css: [
    'element-ui/lib/theme-chalk/reset.css',
    'element-ui/lib/theme-chalk/index.css'
],
...
build: {
    /*
    ** You can extend webpack config here
    */
    extend(config, ctx) {
      // Run ESLint on save
      if (ctx.isDev && ctx.isClient) {
        config.module.rules.push({
          enforce: 'pre',
          test: /\.(js|vue)$/,
          loader: 'eslint-loader',
          exclude: /(node_modules)/
        })
      }
    },
    cache: true
}

配置sass编译 lang="scss"
yarn add @nuxtjs/style-resources node-sass sass-loader
nuxt.config.js文件添加代码
   modules: [
        '@nuxtjs/style-resources'
    ],

Module build failed (from ./node_modules/eslint-loader/index.js): friendly-errors 23:51:12
TypeError: Cannot read property ‘eslint’ of undefined
at Object.module.exports (/Users/sweet/Desktop/IDEA_project/vue/nuxt/node_modules/eslint-loader/index.js:148:18)

解决方案:
1     npm install eslint-loader@2.1.2 –save -dev
最后再次执行就没问题了。
初始化的版本太旧,升级
“backpack-core”: “^0.8.4”,
“eslint”: “^3.19.0”,
“eslint-config-standard”: “^10.2.1”,
“eslint-loader”: “^2.1.1”,

常用组件

npm i koa-router
npm install --save axios
npm i -g webpack webpack-cli

数据渲染

 async mounted() {
      // mounted在服务端渲染时不会被执行,页面源码不会渲染有数据,只会在浏览器中发生效果
      // let self = this
      // let {status, data: { list }} = await axios.get('/city/list')
      // if (status === 200) {
      //   self.list = list
      // }
    },
    async asyncData() {
      let {status, data: { list }} = await axios.get('http://localhost:3000/city/list')
      if (status === 200) {
        return { list }
      }
    }

fetch 用于vuex

报错Error:Interface ‘NuxtApp’ incorrectly extends interface ‘Vue’

https://github.com/nuxt/typescript/issues/49 类似,element-ui的$loading和nuxt.js 中的$loading冲突导致,临时解决方法

tsconfig.json
"compilerOptions": {
    "skipLibCheck": true
}

配置请求的baseUrl参数

    nuxt.config.js文件添加代码
   env: {
        baseUrl: process.env.NODE_ENV == 'development' ? 'http://localhost:3000/mapi' : 'http://www.ddd.cn'
    }
    测试 test.js 文件
    任何文件都可以使用 process.env.baseUrl
import Http from '@/plugins/http/index'; // 封装axios请求
let API = process.env.baseUrl;
     // 文章列表
    getArticleList(params = {}) {
        let url = `${API}/api/article/weblist`;
        return Http.get(url, params)
    },

Window 或 Document 对象未定义?

npm install --save window
那个js文件有window就在那个文件添加一下代码
// 客户端
if (process.client) {
...your code
}
// 浏览器
if (process.browser) {
...your code
}
例子: 以wowjs为例子
if (process.browser) {
var { WOW } = require("wowjs");
}
if (process.browser) {
let wow = new WOW({
live: false
});
wow.init();
}
or
在webpack.base.config.js下的output中添加一条globalObject: 'this'
or
(function(window) {
/* Keep source code the same */
// })(typeof window == "undefined" ? global : window);
// or
})(this);
nuxt.js.config
plugins: [
'@/plugins/element-ui',
// '@/plugins/adapter-latest',
{src: '@/plugins/adapter-latest', ssr: false}
],

koa-redis

安装session和redis
npm i koa-generic-session koa-redis

const session = require(‘koa-generic-session’)
const Redis = require(‘koa-redis’)

app.keys=[‘keys’,’keyskeys’]
app.use(session({
store:new Redis()
}))