Golang进阶之路:实现命名管道通信数据交互

命名管道通信

管道通信(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

  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值