【OpenIM原創(chuàng)】C/C++調(diào)用golang函數(shù),golang回調(diào)C/C++函數(shù)

需求來(lái)源

Open-IM 是由前微信技術(shù)專(zhuān)家打造的全開(kāi)源、永久免費(fèi)、無(wú)限制的即時(shí)通訊組件。Open-IM 包括 IM 服務(wù)端和客戶(hù)端 SDK,實(shí)現(xiàn)了高性能、輕量級(jí)、易擴(kuò)展等重要特性。開(kāi)發(fā)者通過(guò)集成 Open-IM 組件,并私有化部署服務(wù)端,可以將即時(shí)通訊、實(shí)時(shí)網(wǎng)絡(luò)能力免費(fèi)、快速集成到自身應(yīng)用中,并確保業(yè)務(wù)數(shù)據(jù)的安全性和私密性。

OpenIM包括Server和SDK,兩者都是采用golang實(shí)現(xiàn)的,移動(dòng)端通過(guò)gomobile生成代碼,再加上對(duì)應(yīng)的插件,這樣能適應(yīng)多個(gè)前端開(kāi)發(fā)框架,無(wú)論是原生的iOS、Android還是跨端開(kāi)發(fā)的Flutter、uniapp、react native、cordova等。OpenIM SDK 要用在pc端electron框架中,先解決C調(diào)用golang的問(wèn)題,再打通nodejs調(diào)用C /C++,當(dāng)然這里還涉及到各種回調(diào)函數(shù)。

網(wǎng)上有很多例子告訴你怎么從Go語(yǔ)言調(diào)用C /C++語(yǔ)言的函數(shù),但少文章有告訴你,如何從C /C++語(yǔ)言函數(shù)中調(diào)用Golang語(yǔ)言寫(xiě)的函數(shù)。本文通過(guò)實(shí)際代碼,來(lái)展示兩個(gè)能力:(1)golang如何編譯成動(dòng)態(tài)庫(kù)so (2)C /C++如何調(diào)用golang函數(shù) (3)golang如何調(diào)用C /C++的回調(diào)函數(shù)。

goland代碼及注意事項(xiàng)

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全局變量,再回調(diào)時(shí)加鎖互斥

?? 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() {

}

在代碼塊,有幾個(gè)點(diǎn)需要注意:

(1)package main? 這個(gè)必須是main

(2)這個(gè)注釋不能少,原封不動(dòng)復(fù)制粘貼即可

/* #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)對(duì)于提供給C調(diào)用的函數(shù),在函數(shù)上一行加上export? 例如://export? doSomethingCallback

(4) main函數(shù)保留

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"

原封不動(dòng)保存就可以了。

編譯成動(dòng)態(tài)庫(kù)

go build -o libcallback.so? -buildmode=c-shared a.go b.go


生成libcallback.h 和libcallback.so

C代碼調(diào)用

#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

生成可執(zhí)行程序 m


輸入./m 執(zhí)行,C調(diào)用golang的doSomethingCallback函數(shù),并在此函數(shù)回調(diào)C的gocallback函數(shù),完成了C->golang->C

小節(jié)

github源代碼下載

OpemIM開(kāi)源IM項(xiàng)目

OpenIM官網(wǎng)

C和golang互調(diào)能力打通,這樣,對(duì)于采用C/C++開(kāi)發(fā)的項(xiàng)目,如果某些業(yè)務(wù)特性不追求性能上的機(jī)制,可以通過(guò)golang實(shí)現(xiàn),這樣達(dá)到了開(kāi)發(fā)效率和執(zhí)行效率的平衡,對(duì)業(yè)務(wù)開(kāi)發(fā)非常有幫助。

通過(guò)深度調(diào)用機(jī)制分析,無(wú)論是Go調(diào)用C,還是C調(diào)用Go,其需要解決的核心問(wèn)題其實(shí)都是提供一個(gè)C/Go的運(yùn)行環(huán)境來(lái)執(zhí)行相應(yīng)的代碼。Go的代碼執(zhí)行環(huán)境就是goroutine以及Go的runtime,而C的執(zhí)行環(huán)境需要一個(gè)不使用分段的棧,并且執(zhí)行C代碼的goroutine需要暫時(shí)地脫離調(diào)度器的管理。

要達(dá)到這些要求,運(yùn)行時(shí)提供的支持就是切換棧,以及runtime.entersyscall。在Go中調(diào)用C函數(shù)時(shí),runtime.cgocall中調(diào)用entersyscall脫離調(diào)度器管理。runtime.asmcgocall切換到m的g0棧,于是得到C的運(yùn)行環(huán)境。在C中調(diào)用Go函數(shù)時(shí),crosscall2解決gcc編譯到6c編譯之間的調(diào)用協(xié)議問(wèn)題。cgocallback切換回goroutine棧。runtime.cgocallbackg中調(diào)用exitsyscall恢復(fù)Go的運(yùn)行環(huán)境

OpenIMgithub開(kāi)源地址:

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

OpenIM官網(wǎng) : https://www.rentsoft.cn

OpenIM官方論壇: https://forum.rentsoft.cn/

更多技術(shù)文章:

開(kāi)源OpenIM:高性能、可伸縮、易擴(kuò)展的即時(shí)通訊架構(gòu)https://forum.rentsoft.cn/thread/3

【OpenIM原創(chuàng)】簡(jiǎn)單輕松入門(mén) 一文講解WebRTC實(shí)現(xiàn)1對(duì)1音視頻通信原理https://forum.rentsoft.cn/thread/4

【OpenIM原創(chuàng)】開(kāi)源OpenIM:輕量、高效、實(shí)時(shí)、可靠、低成本的消息模型https://forum.rentsoft.cn/thread/1

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書(shū)系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

  • 回調(diào)函數(shù)就是一個(gè)通過(guò)函數(shù)指針調(diào)用的函數(shù)。如果你把函數(shù)的指針(地址)作為參數(shù)傳遞給另一個(gè)函數(shù),當(dāng)這個(gè)指針被用來(lái)調(diào)用其...
    chjxidian閱讀 1,557評(píng)論 0 0
  • golang調(diào)用c動(dòng)態(tài)庫(kù) 簡(jiǎn)介 golang調(diào)用c語(yǔ)言動(dòng)態(tài)庫(kù),動(dòng)態(tài)方式調(diào)用,可指定動(dòng)態(tài)庫(kù)路徑,無(wú)需系統(tǒng)目錄下 核心...
    笑吧小鳥(niǎo)閱讀 1,929評(píng)論 0 0
  • 【轉(zhuǎn)載】C&C++——C函數(shù)與C++函數(shù)相互調(diào)用問(wèn)題 C C++相互調(diào)用 在項(xiàng)目中融合C和C++有時(shí)是不可避免的,...
    天之道天知道閱讀 3,587評(píng)論 2 19
  • Spring Cloud為開(kāi)發(fā)人員提供了快速構(gòu)建分布式系統(tǒng)中一些常見(jiàn)模式的工具(例如配置管理,服務(wù)發(fā)現(xiàn),斷路器,智...
    卡卡羅2017閱讀 136,639評(píng)論 19 139
  • 函數(shù)指針是一個(gè)存著某個(gè)函數(shù)地址的變量。這個(gè)函數(shù)之后可以通用這個(gè)變量來(lái)調(diào)用。為什么需要函數(shù)指針呢?這邊舉個(gè)例子說(shuō)明下...
    雨幻逐光閱讀 372評(píng)論 0 0

友情鏈接更多精彩內(nèi)容