需求來源
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? doSomethingCallback
func 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
小節
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