写点什么

【OpenIM 原创】C/C++ 调用 golang 函数,golang 回调 C/C++ 函数

用户头像
OpenIM
关注
发布于: 2 小时前

需求来源

Open-IM 是由前微信技术专家打造的全开源、永久免费、无限制的即时通讯组件。Open-IM 包括 IM 服务端和客户端 SDK,实现了高性能、轻量级、易扩展等重要特性。开发者通过集成 Open-IM 组件,并私有化部署服务端,可以将即时通讯、实时网络能力免费、快速集成到自身应用中,并确保业务数据的安全性和私密性。


OpenIM 包括 Server 和 SDK,两者都是采用 golang 实现的,移动端通过 gomobile 生成代码,再加上对应的插件,这样能适应多个前端开发框架,无论是原生的 iOS、Android 还是跨端开发的 Flutter、uniapp、react native、cordova 等。OpenIM SDK 要用在 pc 端 electron 框架中,先解决 C 调用 golang 的问题,再打通 nodejs 调用 C /C++,当然这里还涉及到各种回调函数。



网上有很多例子告诉你怎么从 Go 语言调用 C /C++语言的函数,但少文章有告诉你,如何从 C /C++语言函数中调用 Golang 语言写的函数。本文通过实际代码,来展示两个能力:(1)golang 如何编译成动态库 so (2)C /C++如何调用 golang 函数 (3)golang 如何调用 C /C++的回调函数。

goland 代码及注意事项

a.go 代码:


package main/*#cgo CFLAGS: -I .#include <stdio.h>#include <string.h>#include <stdlib.h>typedef void (*callback)(void *,int);extern void c_callback (void *,int);extern callback _cb;*/import "C"import (    "sync"    "unsafe"    "time"    "fmt"    "encoding/json")
var mutex sync.Mutex

type HelloWorld interface { OnSuccessCallback(result string)}
func doSomething(worker HelloWorld, input string){ //模拟异步 go func() { fmt.Println("func doSomething start...") time.Sleep(time.Duration(2)*time.Second) fmt.Println("func doSomething end...") result := "do something successful" worker.OnSuccessCallback(result) }()}
type SomeHelloWorld struct { cb C.callback input string passBackData string}
type CallbackOutput struct { Data string `json:"data"` Output string `json:"output"`}

func(t *SomeHelloWorld) OnSuccessCallback(result string){ var callbackOutput CallbackOutput callbackOutput.Data = t.passBackData callbackOutput.Output = result
jsonStr, err := json.Marshal(callbackOutput) if err != nil { fmt.Println("err: ", err.Error()) } fmt.Println("json: ", string(jsonStr))
var cmsg *C.char = C.CString(string(jsonStr)) var ivalue C.int = C.int(len(jsonStr)) defer C.free(unsafe.Pointer(cmsg)) //C._cb全局变量,再回调时加锁互斥 mutex.Lock() defer mutex.Unlock() C._cb = t.cb C.c_callback(unsafe.Pointer(cmsg), ivalue)}
//export doSomethingCallbackfunc doSomethingCallback(p C.callback, input *C.char, data *C.char){ var one SomeHelloWorld one.cb = p one.passBackData = C.GoString(data) one.input = C.GoString(input) fmt.Println("one: ", one) doSomething(&one, one.input)}
func main() {}
复制代码


在代码块,有几个点需要注意:


(1)package main 这个必须是 main


(2)这个注释不能少,原封不动复制粘贴即可


/* #cgo CFLAGS: -I . #include <stdio.h> #include <string.h> #include <stdlib.h> typedef void (*callback)(void *,int); extern void c_callback (void *,int); extern callback _cb; */


(3)对于提供给 C 调用的函数,在函数上一行加上 export 例如://export doSomethingCallback


(4) main 函数保留


func main() { }


b.go 代码


package main
/*#include <stdio.h>
typedef void (*callback)(void *,int);
callback _cb;
void c_callback(void* p,int i){ _cb(p,i);}*/import "C"
复制代码


原封不动保存就可以了。

编译成动态库

go build -o libcallback.so  -buildmode=c-shared a.go b.go
复制代码



生成 libcallback.h 和 libcallback.so

C 代码调用

#include <stdio.h>#include <unistd.h>
#include "libcallback.h"
void gocallback(void* s,int len) {
printf("%s\n", (char*)s);
}
int main() { const char* a = "cstring input"; doSomethingCallback(gocallback, (char*)"cstring hello", (char*)a); pause();}
复制代码


编译


gcc -v  m.cpp  -o m ./libcallback.so 
复制代码


生成可执行程序 m



输入./m 执行,C 调用 golang 的 doSomethingCallback 函数,并在此函数回调 C 的 gocallback 函数,完成了 C->golang->C


小节

github源代码下载


OpemIM开源IM项目


OpenIM官网


C 和 golang 互调能力打通,这样,对于采用 C/C++开发的项目,如果某些业务特性不追求性能上的机制,可以通过 golang 实现,这样达到了开发效率和执行效率的平衡,对业务开发非常有帮助。


通过深度调用机制分析,无论是 Go 调用 C,还是 C 调用 Go,其需要解决的核心问题其实都是提供一个 C/Go 的运行环境来执行相应的代码。Go 的代码执行环境就是 goroutine 以及 Go 的 runtime,而 C 的执行环境需要一个不使用分段的栈,并且执行 C 代码的 goroutine 需要暂时地脱离调度器的管理。



要达到这些要求,运行时提供的支持就是切换栈,以及 runtime.entersyscall。在 Go 中调用 C 函数时,runtime.cgocall 中调用 entersyscall 脱离调度器管理。runtime.asmcgocall 切换到 m 的 g0 栈,于是得到 C 的运行环境。在 C 中调用 Go 函数时,crosscall2 解决 gcc 编译到 6c 编译之间的调用协议问题。cgocallback 切换回 goroutine 栈。runtime.cgocallbackg 中调用 exitsyscall 恢复 Go 的运行环境


OpenIMgithub 开源地址:


https://github.com/OpenIMSDK/Open-IM-Server


OpenIM 官网 : https://www.rentsoft.cn


OpenIM 官方论坛: https://forum.rentsoft.cn/


更多技术文章:


开源 OpenIM:高性能、可伸缩、易扩展的即时通讯架构https://forum.rentsoft.cn/thread/3


【OpenIM 原创】简单轻松入门 一文讲解 WebRTC 实现 1 对 1 音视频通信原理https://forum.rentsoft.cn/thread/4


【OpenIM 原创】开源 OpenIM:轻量、高效、实时、可靠、低成本的消息模型https://forum.rentsoft.cn/thread/1

用户头像

OpenIM

关注

还未添加个人签名 2021.08.30 加入

还未添加个人简介

评论

发布
暂无评论
【OpenIM原创】C/C++调用golang函数,golang回调C/C++函数