前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >【OpenIM原创】C/C++调用golang函数,golang回调C/C++函数

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

原创
作者头像
用户8964349
修改2021-09-09 10:35:52
1.6K0
修改2021-09-09 10:35:52
举报
文章被收录于专栏:OpenIM

需求来源

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代码:

代码语言:javascript
复制
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代码

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

原封不动保存就可以了。

编译成动态库

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

生成libcallback.h 和libcallback.so

C代码调用

代码语言:javascript
复制
#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();
}
​

编译

代码语言:javascript
复制
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需要暂时地脱离调度器的管理。

img
img

要达到这些要求,运行时提供的支持就是切换栈,以及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

原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。

如有侵权,请联系 cloudcommunity@tencent.com 删除。

原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。

如有侵权,请联系 cloudcommunity@tencent.com 删除。

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 需求来源
  • goland代码及注意事项
  • 编译成动态库
  • C代码调用
  • 小节
相关产品与服务
即时通信 IM
即时通信 IM(Instant Messaging)基于腾讯二十余年的 IM 技术积累,支持 Android、iOS、Mac、Windows、Web、H5、小程序平台且跨终端互通,低代码 UI 组件助您30分钟集成单聊、群聊、好友与资料、消息漫游、群组管理、会话管理、直播弹幕、内容审核和推送等能力。适用于直播互动、电商带货、客服咨询、社交沟通、企业办公、互动游戏、医疗健康等场景。
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档