这是一个HTTPS Server的例子,CEXE调用GODLL的函数启动HTTPS Server,Server收到数据后会调用CEXE中的回调函数将HTTP请求数据发送给CEXE并从CEXE处获得HTTP响应数据。
需要注意的是在C和GO之间进行变量类型转换的时候,如果使用C.CString和C.CBytes转换字符串或数据,则需要调用C.free释放内存,使用C.free的需要在import “C”前的C代码中引入头文件stdlib.h,否则会报错。
package main
/*
#include <stdint.h>
#include <stdlib.h>
#include <string.h>
#include <stdio.h>
typedef void (*callback)(const char *req_head,unsigned char *req_body,unsigned int req_body_len,unsigned char *res_body_buffer,unsigned int res_body_buffer_len,unsigned int *res_body_len);
static inline void call_callback(callback p,const char *req_head,unsigned char *req_body,unsigned int req_body_len,unsigned char *res_body_buffer,unsigned int res_body_buffer_len,unsigned int *res_body_len)
{
p(req_head,req_body,req_body_len,res_body_buffer,res_body_buffer_len,res_body_len);
}
*/
import "C"
import (
"context"
"encoding/binary"
"fmt"
"io"
"net/http"
"runtime/cgo"
"unsafe"
)
type HttpServer struct {
http.Server
remark string
cb C.callback
port uint16
crt_file string
key_file string
}
func (ser *HttpServer) ServeHTTP(w http.ResponseWriter, r *http.Request) {
header := fmt.Sprintf("%s %s %s\r\n", r.Method, r.RequestURI, r.Proto)
header += fmt.Sprintf("Host: %s\r\n", r.Host)
for k, vs := range r.Header {
header += fmt.Sprintf("%s: ", k)
for _, v := range vs {
header += v
}
header += "\r\n"
}
header += "\r\n"
body, err := io.ReadAll(io.Reader(r.Body))
if err != nil {
http.Error(w, "Error reading request body", http.StatusInternalServerError)
return
}
cHeader := C.CString(header)
cReqBody := C.CBytes(body)
res_body_buffer := make([]byte, 1024*1024)
var res_body_len C.uint
C.call_callback(ser.cb, cHeader, (*C.uchar)(cReqBody), C.uint(binary.Size(body)), (*C.uchar)(unsafe.Pointer(&res_body_buffer[0])), C.uint(binary.Size(res_body_buffer)), &res_body_len)
slice := res_body_buffer[0:res_body_len]
w.Write(slice)
C.free(unsafe.Pointer(cHeader))
C.free(unsafe.Pointer(cReqBody))
}
//export ListenAndServe
func ListenAndServe(handle C.uintptr_t) {
h := cgo.Handle(handle)
server := h.Value().(*HttpServer)
server.ListenAndServeTLS(server.crt_file, server.key_file)
}
//export CreateServer
func CreateServer(remark *C.char, port C.ushort, crt_file *C.char, key_file *C.char, p C.callback) (handle C.uintptr_t) {
server := HttpServer{}
server.remark = C.GoString(remark)
server.cb = p
server.port = uint16(port)
server.crt_file = C.GoString(crt_file)
server.key_file = C.GoString(key_file)
server.Addr = fmt.Sprintf(":%d", server.port)
server.Handler = &server
handle = C.uintptr_t(cgo.NewHandle(&server))
return handle
}
//export ShutdownServer
func ShutdownServer(handle C.uintptr_t) {
h := cgo.Handle(handle)
server := h.Value().(*HttpServer)
server.Shutdown(context.Background())
h.Delete()
}
// //export main
func main() {
}
#define WIN32_LEAN_AND_MEAN
#include <Windows.h>
#include <stdio.h>
#include <string.h>
#include "https_server.h"
void test_callback(const char *req_head,unsigned char *req_body,unsigned int req_body_len,unsigned char *res_body_buffer,unsigned int res_body_buffer_len,unsigned int *res_body_len)
{
printf("%s\r\n",req_head);
printf("body length: %u\r\n",req_body_len);
printf("res_body_buffer_len: %u\r\n",res_body_buffer_len);
strcpy(res_body_buffer,"0123456789!111111111111111111111111111111111");
*res_body_len=10;
}
void test_callback2(const char *req_head,unsigned char *req_body,unsigned int req_body_len,unsigned char *res_body_buffer,unsigned int res_body_buffer_len,unsigned int *res_body_len)
{
printf("%s\r\n",req_head);
printf("body length: %u\r\n",req_body_len);
printf("res_body_buffer_len: %u\r\n",res_body_buffer_len);
strcpy(res_body_buffer,"9876543210!111111111111111111111111111111111");
*res_body_len=10;
}
VOID WINAPI ListenAndServeThread(uintptr_t handle)
{
ListenAndServe(handle);
printf("exit ListenAndServe thread\r\n");
}
int main(int argc,char *argv[]){
uintptr_t handle;
uintptr_t handle2;
handle=CreateServer("test_8000",8000,"server.crt","server.key",test_callback);
handle2=CreateServer("test_8001",8000,"server.crt","server.key",test_callback2);
CreateThread(NULL,0,(LPTHREAD_START_ROUTINE)ListenAndServeThread,(LPVOID)handle,0,NULL);
//CreateThread(NULL,0,(LPTHREAD_START_ROUTINE)ListenAndServeThread,(LPVOID)handle2,0,NULL);
getchar();
ShutdownServer(handle);
return 0;
}
编译命令
go build -o https_server.dll -buildmode=c-shared server.go gcc -o main.exe main.c https_server.dll
这里也可以将GO代码编译成C能用的静态库文件
go build -o https_server_static.lib -buildmode=c-archive server.go
生成自签名证书
openssl genrsa -out server.key 2048 openssl req -new -x509 -sha256 -key server.key -out server.crt -days 3650
发表回复