命名管道通信
管道通信(Communication Pipeline)即发送进程以字符流形式将大量数据送入管道,接收进程可从管道接收数据,二者利用管道进行通信。无论是SQL Server用户,还是PB用户,作为C/S结构开发环境,他们在网络通信的实现上,都有一种共同的方法——命名管道。(来源百度百科)
起因
因为一些特殊的原因,开发工程中不能使用http这种开服务端口的形式进行数据交互,而且当时的项目也是类似的C/S架构,所以刚好学习并应用到工作开发中。。。
开整
废话不多说
数据交互以命名管道通信为主。
上代码(只有一部分,主要架构的代码,前端node.js)
后端
import gw "github.com/Microsoft/go-winio"
var ServerSocket net.Conn
var lockSend sync.Mutex
var pipeFile = "\\\\.\\pipe\\wtreelee_pipe"
//注册函数,一个函数对应一个口令
func register() map[string]returnFunc {
return map[string]returnFunc{
"entry": pipeEntry, // 获取入口数据
"main": startMain, // 进入
"load": load, // 加载
}
}
/**
开启管道通信监听
通过一个命名管道文件 wtreelee_pipe 进行管道通信进行数据交互
通过封装数据交互:方法&&&参数
*/
func OpenPipeServer() {
log.Println("开启管道通信")
l, err := gw.ListenPipe(pipeFile, &config)
if err != nil {
log.Println("管道通信,开启管道错误")
log.Println(err)
return
}
for {
conn, err := l.Accept()
if err != nil {
log.Println("管道通信,开启监听错误")
log.Println(err)
continue
}
go doServer(conn)
}
}
func doServer(conn net.Conn) {
ServerSocket = conn
var cmdChan = make(chan string) //接收请求数据
var stopChan = make(chan string) //发送、接收一起停止
var methods = register()
go func() {
for{
cmd := <-cmdChan
var message []byte
if len(cmd) > 0 {
log.Println("管道通信,接收到数据")
// 口令格式和数据格式: cmd&&&json
data := map[string]interface{}{}
if strings.Contains(cmd, "&&&") {
params := strings.Split(cmd, "&&&")
cmd = params[0]
log.Println("read ", cmd+" ", params[1])
err := json.Unmarshal([]byte(params[1]), &data)
if err != nil {
log.Println("read2:", err)
stopChan <- err.Error()
break
}
}
cmdFunc, ok := methods[cmd]
if !ok {
log.Println("socket", "没有这个函数", cmd)
stopChan <- fmt.Errorf("没有这个函数" + cmd).Error()
break
}
message, _ = json.Marshal(cmdFunc(data))
//log.Println(string(message))
err := sendMessage(fmt.Sprintf("%s&&&%s", cmd, string(message)))
if err != nil {
log.Println("write2:", err)
stopChan <- err.Error()
break
}
stopChan <- ""
}
}
}()
for{
r := bufio.NewReader(conn)
msg, err := r.ReadString('\n')
if err != nil {
log.Println("readErr",err)
return
}
log.Println("msg:",msg)
cmdChan<-msg
}
}
/**
管道通信:发送数据
*/
func sendMessage(message string) error {
lockSend.Lock()
defer lockSend.Unlock()
if ServerSocket != nil {
_,err := fmt.Fprintln(ServerSocket,message)
if err != nil {
log.Println("sendMessage:", err)
return err
}
}
return nil
}
前端Js
/* eslint-disable no-undef */
/** 用pipe连接 */
import setting from '../../setting';
import Utils from './Utils';
const openTips = true;
const net = window.$nw.require('net');
/** 建立
* @param PIPE_NAME: [String] 管道名
*/
export default class Pipe {
constructor(PIPE_NAME = "wtreelee_pipe") {
this.path = "\\\\.\\pipe\\" + PIPE_NAME;
this.channel = Pipe.getChannel();
this.server = Pipe.newPipe(this);
this.readyState = 0;
}
// /** 重置失败次数, 重置之后将会重新连接*/
resetPipe() {
if (openTips) {
let tips = `[pipe ${this.path}] : 失败次数重置, 将重新连接...`;
tips = Utils.formatTips(tips);
console.log(...tips);
}
this.failTime = 0;
this.socket = Pipe.newPipe(this);
}
/** 生成事件实例 */
static getChannel() {
const events = require('events');
return new events.EventEmitter();
}
/** 开始连接
* @param that : [Object] 实例对象
* @return socket: 返回一个socket
*/
static newPipe(that) {
let server = net.connect(that.path, function () {
let tips = `[pipe ${that.path}]: 建立`;
tips = Utils.formatTips(tips);
console.log(...tips);
});
// server.setTimeout(3000);
// server.on('timeout', () => {
// console.log('socket 超时');
// that.resetPipe();
// });
server.on('connect',function(){
console.log("连接成功");
});
server.on('data', function (c) {
let order = '';
let data = c.toString();
let tips = '';
// 如果是[命令]&&&[数据的格式]
if (~data.indexOf('&&&')) {
data = c.toString().split('&&&'); // 约定格式, &&&是命令跟数据的分割符
order = data[0]; // 命令(一个标记, send的命令它就是什么)
data = data[1] ? JSON.parse(data[1]) : 'have no data'; // 数据
tips = `[pipe ${that.path}]: 接收到 ${order} 的数据\n`;
}
// 不是这种格式的话就直接解析
else {
data = JSON.parse(data);
tips = `[pipe ${that.path}]: 接收到数据\n`;
}
if (openTips) {
tips = Utils.formatTips(tips);
console.log(...tips, data);
}
that.channel.emit(order, JSON.parse(JSON.stringify(data)));
});
server.on('end', function () {
console.log("关闭");
});
return server;
}
/** 发送数据
* @param order: [String] 命令
* @param data: [Object] 数据
*/
send(order, data = {}) {
Utils.isintance(order, 'String');
Utils.isintance(data, 'Object');
if (this.server.readyState === "open") {
this.server.write(`${order}&&&${JSON.stringify(data)}\n`); // 数据发送
if (openTips) {
const o = `${order}&&&${JSON.stringify(data)}`;
let tips = `[pipe ${this.path}]: 发送数据, 命令: ${o}`;
tips = Utils.formatTips(tips);
const tips_data = Object.keys(data).length ? data : '';
console.log(...tips, tips_data); // 打印状态
}
}
// 当连接未完成的时候, 递归本身直到连接完成
else {
setTimeout(() => this.send(order, data), 500);
}
}
/** 监听数据
* @param order: [String] 事件名
* @param cb: [Function] 回调函数
*/
listen(order, cb) {
Utils.isintance(order, 'String');
Utils.isintance(cb, 'Function');
if (openTips) {
let tips = `[pipe ${this.path}]: 正在监听 ${order} 的数据...`;
tips = Utils.formatTips(tips);
console.log(...tips);
}
this.channel.on(order, cb);
}
/** 移除某个监听
* @param order: [String] 事件名
*/
removeListen(order) {
Utils.isintance(order, 'String');
if (openTips) {
let tips = `[pipe ${this.path}]: 取消 ${order} 的监听`;
tips = Utils.formatTips(tips);
console.log(...tips);
}
this.channel.removeAllListeners(order);
}
}
实现接口的功能就自己定义数据的格式 怎么好搞怎么来
(^-^)V