Julia与Electron进程间通信

Julia与Electron进程间通信

Julia进程间通信采用命名管道的方式,主要使用Sockets。Julia 提供了一个丰富的接口处理终端、管道、tcp套接字等等I/O流对象,并且接口在系统层的实现是异步的。

定义一个命名管道

使用一个字符串即可命名一个管道,如:

julia> using Sockets
julia> name_pipe = "\\\\.\\pipe\\mypipe-1"
"\\\\.\\pipe\\mypipe-1"

异步监听服务器端

#一定要异步监听,否则将会阻塞
@async begin
  server = listen(name_pipe)
  while true
 	 #等待有客户端连接
      sock = accept(server)      
  end
end

使用Sockets下面的listen(name_pipe)建立服务器端的监听,返回PipeServer类型。accpet函数调用后,会调用wait函数,等待一个客户端的连接,如果不采用异步形式,此步会阻塞。sock为PipeEndPoint类型,此类型为Base下的不导出的类型。

julia> isa(sock,Base.PipeEndpoint)
true

建立客户端与服务器端连接

clientside = connect(name_pipe)
true
julia> clientside.status
3

使用connnect(name_pipe)返回PipeEndPoint类型的客户端,查看clientside.status=3,代表StatusOpen,各状态码的意义如下:

const StatusUninit      = 0 # handle is allocated, but not initialized
const StatusInit        = 1 # handle is valid, but not connected/active
const StatusConnecting  = 2 # handle is in process of connecting
const StatusOpen        = 3 # handle is usable
const StatusActive      = 4 # handle is listening for read/write/connect events
const StatusClosing     = 5 # handle is closing / being closed
const StatusClosed      = 6 # handle is closed
const StatusEOF         = 7 # handle is a TTY that has seen an EOF event
const StatusPaused      = 8 # handle is Active, but not consuming events, and will transition to Open if it receives an event

可用read/write向管道中读写数据

管道也是IO的子类型

julia> isa(sock,IO)
true

读也一定要采用异步的方式,否则管道中无数据中read函数将阻塞起来,知道管道中有数据出现,read出来之后,管道中的数据将减少。

      @async while isopen(sock)
	      #将从客户端读出的数据+"from the Echo Server"后再直接发给客户端
            write(sock, readline(sock,keep=true)*" from the Echo Server")
      end
      @async while isopen(clientside)
		#客户端从管道中读取数据后直接在标准输出设备输出
  		write(stdout, readline(clientside,keep=true))
	end
	#向服务端写一个字符串
	julia> println(clientside,"Hello World from the Echo Server")
	Hello World from the Echo Server

使用完成后记得关闭管道close(clientside);

控制打开其他可执行程序并传递参数

主要使用Sockets下的open函数,open(command, mode::AbstractString=“r”, stdio=devnull),或者
open(command, other=devnull; write::Bool = false, read::Bool = !write)
command是可以解析为执行命令的抽象字符串。

using Sockets
nodepad_path="C:\\WINDOWS\\system32\\notepad.exe"
filepath=joinpath(@__DIR__,"test.txt")
open(`$nodepad_path $filepath`,"w",stdout)

open函数会生成一个Process类型的对象,同时会解析command命令,command命令中包含需要打开的可执行程序及传递的参数,如:nodepad_path为记事本的路径,filepath为传递的参数,此处为要加载的txt的路径。上述命令执行完毕后notepad被打开并加载了test.txt中的内容。
如果在julia中建立一个命名管道,julia作用服务器端,再用open控制打开electron,并将管道的地址作为参数传递给electron,那么就可以再electron端建立客户端并与服务器端通过管道连接,这样就到达了两者通信的目的。

解析Electron.jl源码

Electron.jl这个包正是通过上述原理实现julia与Elctron通信的。解析Electron.jl的源码,在Application()函数中

#获取electon二进制文件的路径
electron_path = get_electron_binary_cmd()
#获取mainjs的路径,作为传递给electon的第一个参数,electron启动后加载的js文件
mainjs = joinpath(@__DIR__, "main.js")
#一个命名管道,负责julia向electron发送数据
main_pipe_name = generate_pipe_name("juliaelectron-$process_id-$id")
#一个命名管道,负责electron向Julia发送数据
sysnotify_pipe_name = generate_pipe_name("juliaelectron-sysnotify-$process_id-$id")
#验证码,确保管道在两者之间准确无误的连通
secure_cookie = rand(UInt8, 128)
secure_cookie_encoded = base64encode(secure_cookie)
#启动electron,并向electon传递两个管道的地址及一个验证码
proc = open(`$electron_path $mainjs $main_pipe_name $sysnotify_pipe_name $secure_cookie_encoded`, "w", stdout)

electron启动后加载main.js,main.js的源码解析:

app.on('ready', function() {
//从进程中读取传递过来的第四个参数(验证码),并转换成64为编码格式
var secure_cookie = Buffer.from(process.argv[4], 'base64');
//该函数建立管道连接,并向管道中写入验证码,供服务器端验证
function secure_connect(addr, secure_cookie) {
    var connection = net.connect(addr);
    connection.setEncoding('utf8')
    connection.write(secure_cookie);
    return connection;
}
//分别建立两个管道的连接,并向管道中写入验证码
    var connection = secure_connect(process.argv[2], secure_cookie)
    sysnotify_connection = secure_connect(process.argv[3], secure_cookie)
}

在julia端收到来自electron的管道连接,并受到验证码后,首先验证“验证码是否正确”

    #下面两段就是验真命名管道是否正常运行了,accept会等待连接
    sock = accept(server)
    if read!(sock, zero(secure_cookie)) != secure_cookie
        close(server)
        close(sysnotify_server)
        close(sock)
        error("Electron failed to authenticate with the proper security token")
    end

    let sysnotify_sock = accept(sysnotify_server)
        if read!(sysnotify_sock, zero(secure_cookie))!= secure_cookie
            close(server)
            close(sysnotify_server)
            close(sysnotify_sock)
            close(sock)
            error("Electron failed to authenticate with the proper security token")
        end

如果验证成功,则表示管道连接成功,可以正常通信了。在julia段一定要异步的读取数据,数据是以JSON的格式传递的,用key表示属性

 let app = _Application(Window, sock, proc, secure_cookie)
         #@async 的arg 就是要执行的exp
         @async begin
            try
                try
                    while true
                        try
                            # 注意这里是系统管道
                            line_json = readline(sysnotify_sock)
                            isempty(line_json) && break # EOF
                            cmd_parsed = JSON.parse(line_json)
                             #如果渲染窗口向主窗口传递过来了关闭的消息
                            if cmd_parsed["cmd"] == "windowclosed"
                                 win_index = findfirst(w -> w.id == cmd_parsed["winid"], app.windows)                                    
                                 app.windows[win_index].exists = false
                                #关闭两者的通信通道
                                 close(app.windows[win_index].msg_channel)
                                #从app.windows数组中删除对应窗口Item
                                deleteat!(app.windows, win_index)
                                #渲染窗口的回收是由electron负责的

                                    #closing暂时没用,估计是作者预留的
                                elseif cmd_parsed["cmd"] == "appclosing"
                                    break
                                    #如果是消息通过channel传递过来了
                                elseif cmd_parsed["cmd"] == "msg_from_window"
                                    win_index = findfirst(w -> w.id == cmd_parsed["winid"], app.windows)
                                    #解析出来,放到msg_channel里面。
                                    put!(app.windows[win_index].msg_channel, cmd_parsed["payload"])
                                end                
                            catch er
                                bt = catch_backtrace()
                                io = PipeBuffer()
                                print_with_color(Base.error_color(), io, "Electron ERROR: "; bold = true)
                                Base.showerror(IOContext(io, :limit => true), er, bt)
                                println(io)
                                write(stderr, io)
                            end
                        end
                    finally
                        # Cleanup all the windows that are associated with this application
                        for w in app.windows
                            w.exists = false
                        end
                        empty!(app.windows)
                    end
                finally
                    # Cleanup the application instance
                    app.exists = false
                    close(sysnotify_sock)
                    app_index = findfirst(a -> a === app, _global_applications)
                    deleteat!(_global_applications, app_index)
                    #deleteapp之后就会closeapp ,init()中已经定义
                end
            end
            return app
    end
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值