kcp学习-skynet中的kcp绑定

kcp学习-skynet中的kcp绑定

可以在 Skynet 中轻松使用 KCP 协议,获得比 TCP 更低的延迟,同时保持可靠性

源码

lkcpsn.c

c 复制代码
/**
 *
 * Copyright (C) 2015 by David Lin
 * 
 * Permission is hereby granted, free of charge, to any person obtaining a copy
 * of this software and associated documentation files (the "Software"), to deal
 * in the Software without restriction, including without limitation the rights
 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 * copies of the Software, and to permit persons to whom the Software is
 * furnished to do so, subject to the following conditions:
 * 
 * The above copyright notice and this permission notice shall be included in
 * all copies or substantial portions of the Software.
 * 
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALING IN
 * THE SOFTWARE.
 *
 */

#include <stdlib.h>
#include <stdint.h>
#include <string.h>
#include <unistd.h>
#include <pthread.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <netdb.h>
#include <netinet/in.h>

#include <lua.h>
#include <lauxlib.h>
#include <lualib.h>

#include "ikcp.h"
#include "../../../skynet/skynet-src/skynet_server.h"
#include "../../../skynet/skynet-src/socket_buffer.h"
#include "../../../skynet/skynet-src/skynet_socket.h"

#define RECV_BUFFER_LEN 4*1024*1024

#define check_kcp(L, idx)\
    *(ikcpcb**)luaL_checkudata(L, idx, "kcp_meta")

#define check_buf(L, idx)\
    (char*)luaL_checkudata(L, idx, "recv_buffer")

struct Callback {
    uint64_t handle;
    lua_State* L;
    lua_State* eL;
    char *address;
    size_t addresssz;
};

static pthread_mutex_t lock = PTHREAD_MUTEX_INITIALIZER;
static lua_State *LL = NULL;
static uint64_t svrsock = 0;

// #define logs_on

#define lock_on \
//    if(pthread_mutex_lock(&lock) != 0) {\
//        luaL_error(L, "pthread_mutex_lock error");\
//    }\
//    else {\
//        printf("pthread_mutex_lock lock\n");\
//    }\

#define lock_off \
//    pthread_mutex_unlock(&lock);\
//    printf("pthread_mutex_lock unlock\n");\

static void print_address(const uint8_t *addr, size_t sz) {
    uint16_t port = 0;
    memcpy(&port, addr+1, sizeof(uint16_t));
    port = ntohs(port);
    const void * src = addr+3;
    char tmp[256];
    int family;
    if (sz == 1+2+4) {
        family = AF_INET;
    } else {
        if (sz != 1+2+16) {
            printf("debug kcp print_address error1\n");
            return;
        }
        family = AF_INET6;
    }
    if (inet_ntop(family, src, tmp, sizeof(tmp)) == NULL) {
        printf("debug kcp print_address error2\n");
        return;
    }
    printf("debug kcp print_address ip=%s port=%d sz=%d\n", tmp, port, sz);
}

static int kcp_output_callback(const char *buf, int len, ikcpcb *kcp, void *arg) {
    struct Callback* c = (struct Callback*)arg;
    lua_State* L = c->eL;
    
    // uint64_t handle = c->handle;
    // if (handle > 0 && L) {
    //     #ifdef logs_on
    //         printf("debug kcp kcp_output_callback do kcp=%p L=%p eL=%p handle=%ld\n", kcp, c->L, c->eL, c->handle);
    //     #endif
    //     lua_rawgeti(L, LUA_REGISTRYINDEX, handle);
    //     if(lua_isfunction(L, -1)) {
    //         lua_pushlstring(L, buf, len);
    //         lua_call(L, 1, 0);
    //     } else {
    //         printf("debug kcp kcp_output_callback error1 kcp=%p L=%p eL=%p handle=%ld\n", kcp, c->L, c->eL, c->handle);
    //     }
    // } else {
    //     printf("debug kcp kcp_output_callback error2 kcp=%p L=%p eL=%p handle=%ld\n", kcp, c->L, c->eL, c->handle);
    // }

    if(!c->address) {
        printf("debug kcp kcp_output_callback error1 kcp=%p L=%p eL=%p handle=%ld\n", kcp, c->L, c->eL, c->handle);
        luaL_error(L, "kcp_output_callback error1: address invalid");
        return 1;
    }
    // struct skynet_context *ctx = lua_touserdata(L, lua_upvalueindex(1));
    lua_getfield(L, LUA_REGISTRYINDEX, "skynet_context");
    struct skynet_context *ctx = lua_touserdata(L, -1);
    if(!ctx) {
        printf("debug kcp kcp_output_callback error2 kcp=%p L=%p eL=%p handle=%ld\n", kcp, c->L, c->eL, c->handle);
        luaL_error(L, "kcp_output_callback error2: ctx invalid");
        return 1;
    }
    lua_pop(L, 1);

    struct socket_sendbuffer buf2;
    buf2.id = svrsock;
    buf2.type = SOCKET_BUFFER_RAWPOINTER;

    lua_getglobal(LL, "pack_output");
    if(!lua_isfunction(LL, -1)) {
        printf("debug kcp kcp_output_callback error3 kcp=%p L=%p eL=%p handle=%ld\n", kcp, c->L, c->eL, c->handle);
        luaL_error(L, "kcp_output_callback error3: pack_output invalid");
        return 1;
    }
    lua_pushinteger(LL, kcp->conv);
    lua_pushlstring(LL, buf, len);
    lua_pcall(LL, 2, 1, 0);
    buf2.buffer = luaL_checklstring(LL, 1, &buf2.sz);
    lua_pop(LL, 1);
    if(!buf2.buffer) {
        printf("debug kcp kcp_output_callback error4 kcp=%p L=%p eL=%p handle=%ld\n", kcp, c->L, c->eL, c->handle);
        luaL_error(L, "kcp_output_callback error4: buf2.buffer invalid");
        return 1;
    }
    int err = skynet_socket_udp_sendbuffer(ctx, c->address, &buf2);
    if(err) {
        printf("debug kcp kcp_output_callback error5 kcp=%p L=%p eL=%p handle=%ld\n", kcp, c->L, c->eL, c->handle);
        luaL_error(L, "kcp_output_callback error5: skynet_socket_udp_sendbuffer fail");
        return 1;
    }

    #ifdef logs_on
        printf("debug kcp kcp_output_callback do kcp=%p L=%p eL=%p handle=%ld\n", kcp, c->L, c->eL, c->handle);
    #endif
    return 0;
}

static int kcp_gc(lua_State* L) {
    lock_on
    ikcpcb* kcp = check_kcp(L, 1);
    if (kcp == NULL) {
        printf("debug kcp kcp_gc error L=%p\n", L);
        lock_off
        return 0;
    }
    if (kcp->user != NULL) {
        struct Callback* c = (struct Callback*)kcp->user;
        #ifdef logs_on
            printf("debug kcp kcp_gc kcp=%p L=%p eL=%p thisL=%p handle=%ld\n", kcp, c->L, c->eL, L, c->handle);
        #endif
        // if(c->handle > 0) {
        //     luaL_unref(c->L, LUA_REGISTRYINDEX, c->handle);
        //     c->handle = 0;
        // }
        c->L = NULL;
        c->eL = NULL;
        if(c->address) {
            free(c->address);
            c->address = NULL;
        }
        free(c);
        kcp->user = NULL;
    }
    ikcp_release(kcp);
    kcp = NULL;
    
    lock_off
    return 0;
}

static int lkcp_create(lua_State* L) {
    lock_on
    // uint64_t handle = luaL_ref(L, LUA_REGISTRYINDEX);
    // int32_t conv = luaL_checkinteger(L, 1);
    size_t sz = 0;
    uint64_t sock = luaL_checkinteger(L, 1);
    const char *address = luaL_checklstring(L, 2, &sz);
    int32_t conv = luaL_checkinteger(L, 3);
    if(sock && !svrsock) {
        svrsock = sock;
    }

    struct Callback* c = malloc(sizeof(struct Callback));
    memset(c, 0, sizeof(struct Callback));
    c->handle = 0;
    c->L = L;
    c->eL = L;
    c->address = (char*)malloc(sz+1);
    memset(c->address, 0, sz+1);
    memcpy(c->address, address, sz);
    c->addresssz = sz;

    ikcpcb* kcp = ikcp_create(conv, (void*)c);
    if (kcp == NULL) {
        free(c);
        lua_pushnil(L);
        lua_pushstring(L, "error: fail to create kcp");
        lock_off
        return 2;
    }

    kcp->output = kcp_output_callback;

    *(ikcpcb**)lua_newuserdata(L, sizeof(void*)) = kcp;
    luaL_getmetatable(L, "kcp_meta");
    lua_setmetatable(L, -2);

    // #ifdef logs_on
        print_address(c->address, c->addresssz);
        printf("debug kcp lkcp_create kcp=%p L=%p handle=%ld conv=%d svrsock=%ld address=%s addresssz=%d\n", kcp, L, c->handle, conv, svrsock, c->address, c->addresssz);
    // #endif
    lock_off
    return 1;
}

static int lkcp_recv(lua_State* L) {
    lock_on
    ikcpcb* kcp = check_kcp(L, 1);
    if (kcp == NULL || kcp->user == NULL) {
        lua_pushnil(L);
        lua_pushstring(L, "error: kcp not args");
        lock_off
        return 2;
    }
    lua_getfield(L, LUA_REGISTRYINDEX, "kcp_lua_recv_buffer");
    char* buf = check_buf(L, -1);
    lua_pop(L, 1);

    int32_t hr = ikcp_recv(kcp, buf, RECV_BUFFER_LEN);
    if (hr <= 0) {
        lua_pushinteger(L, hr);
        lock_off
        return 1;
    }
    lua_pushinteger(L, hr);
    lua_pushlstring(L, (const char *)buf, hr);

    #ifdef logs_on
        printf("debug kcp lkcp_recv kcp=%p L=%p handle=%ld\n", kcp, L, ((struct Callback*)kcp->user)->handle);
    #endif
    lock_off
    return 2;
}

static int lkcp_send(lua_State* L) {
    lock_on
    ikcpcb* kcp = check_kcp(L, 1);
    if (kcp == NULL || kcp->user == NULL) {
        lua_pushnil(L);
        lua_pushstring(L, "error: kcp not args");
        lock_off
        return 2;
    }
    size_t size;
    const char *data = luaL_checklstring(L, 2, &size);
    int32_t hr = ikcp_send(kcp, data, size);
    lua_pushinteger(L, hr);

    size_t sz;
    const uint8_t *address = (const uint8_t *)luaL_checklstring(L, 3, &sz);
    if(address && sz) {
        struct Callback* c = (struct Callback*)kcp->user;
        if(c->addresssz != sz) {
            free(c->address);
            c->address = (char*)malloc(sz+1);
            c->addresssz = sz;
        }
        memset(c->address, 0, sz+1);
        memcpy(c->address, address, sz);
    }

    #ifdef logs_on
        printf("debug kcp lkcp_send kcp=%p L=%p handle=%ld\n", kcp, L, ((struct Callback*)kcp->user)->handle);
    #endif
    lock_off
    return 1;
}

static int lkcp_update(lua_State* L) {
    lock_on
    ikcpcb* kcp = check_kcp(L, 1);
    if (kcp == NULL || kcp->user == NULL) {
        lua_pushnil(L);
        lua_pushstring(L, "error: kcp not args");
        lock_off
        return 2;
    }
    int32_t current = luaL_checkinteger(L, 2);
    struct Callback* c = (struct Callback*)kcp->user;
    c->eL = L;
    ikcp_update(kcp, current);

    #ifdef logs_on
        // printf("debug kcp lkcp_update kcp=%p L=%p handle=%ld\n", kcp, L, ((struct Callback*)kcp->user)->handle);
    #endif
    lock_off
    return 0;
}

static int lkcp_check(lua_State* L) {
    lock_on
    ikcpcb* kcp = check_kcp(L, 1);
    if (kcp == NULL || kcp->user == NULL) {
        lua_pushnil(L);
        lua_pushstring(L, "error: kcp not args");
        lock_off
        return 2;
    }
    int32_t current = luaL_checkinteger(L, 2);
    int32_t hr = ikcp_check(kcp, current);
    lua_pushinteger(L, hr);

    #ifdef logs_on
        // printf("debug kcp lkcp_check kcp=%p L=%p handle=%ld\n", kcp, L, ((struct Callback*)kcp->user)->handle);
    #endif
    lock_off
    return 1;
}

static int lkcp_input(lua_State* L) {
    lock_on
    ikcpcb* kcp = check_kcp(L, 1);
    if (kcp == NULL || kcp->user == NULL) {
        lua_pushnil(L);
        lua_pushstring(L, "error: kcp not args");
        lock_off
        return 2;
    }
    
    size_t size;
    const char *data = luaL_checklstring(L, 2, &size);
    int32_t hr = ikcp_input(kcp, data, size);
    lua_pushinteger(L, hr);

    size_t sz;
    const uint8_t *address = (const uint8_t *)luaL_checklstring(L, 3, &sz);
    if(address && sz) {
        struct Callback* c = (struct Callback*)kcp->user;
        if(c->addresssz != sz) {
            free(c->address);
            c->address = (char*)malloc(sz+1);
            c->addresssz = sz;
        }
        memset(c->address, 0, sz+1);
        memcpy(c->address, address, sz);
    }
    
    #ifdef logs_on
        printf("debug kcp lkcp_input kcp=%p L=%p handle=%ld\n", kcp, L, ((struct Callback*)kcp->user)->handle);
    #endif
    lock_off
    return 1;
}

static int lkcp_flush(lua_State* L) {
    lock_on
    ikcpcb* kcp = check_kcp(L, 1);
    if (kcp == NULL || kcp->user == NULL) {
        lua_pushnil(L);
        lua_pushstring(L, "error: kcp not args");
        lock_off
        return 2;
    }
    struct Callback* c = (struct Callback*)kcp->user;
    c->eL = L;
    ikcp_flush(kcp);

    #ifdef logs_on
        printf("debug kcp lkcp_flush kcp=%p L=%p handle=%ld\n", kcp, L, ((struct Callback*)kcp->user)->handle);
    #endif
    lock_off
    return 0;
}

static int lkcp_wndsize(lua_State* L) {
    lock_on
    ikcpcb* kcp = check_kcp(L, 1);
    if (kcp == NULL || kcp->user == NULL) {
        lua_pushnil(L);
        lua_pushstring(L, "error: kcp not args");
        lock_off
        return 2;
    }
    int32_t sndwnd = luaL_checkinteger(L, 2);
    int32_t rcvwnd = luaL_checkinteger(L, 3);
    ikcp_wndsize(kcp, sndwnd, rcvwnd);

    lock_off
    return 0;
}

static int lkcp_nodelay(lua_State* L) {
    lock_on
    ikcpcb* kcp = check_kcp(L, 1);
    if (kcp == NULL || kcp->user == NULL) {
        lua_pushnil(L);
        lua_pushstring(L, "error: kcp not args");
        lock_off
        return 2;
    }
    int32_t nodelay = luaL_checkinteger(L, 2);
    int32_t interval = luaL_checkinteger(L, 3);
    int32_t resend = luaL_checkinteger(L, 4);
    int32_t nc = luaL_checkinteger(L, 5);
    int32_t hr = ikcp_nodelay(kcp, nodelay, interval, resend, nc);
    lua_pushinteger(L, hr);

    lock_off
    return 1;
}

static const struct luaL_Reg lkcp_methods [] = {
    { "lkcp_recv" , lkcp_recv },
    { "lkcp_send" , lkcp_send },
    { "lkcp_update" , lkcp_update },
    { "lkcp_check" , lkcp_check },
    { "lkcp_input" , lkcp_input },
    { "lkcp_flush" , lkcp_flush },
    { "lkcp_wndsize" , lkcp_wndsize },
    { "lkcp_nodelay" , lkcp_nodelay },
    {NULL, NULL},
};

static const struct luaL_Reg l_methods[] = {
    { "lkcp_create" , lkcp_create },
    {NULL, NULL},
};

int luaopen_lkcpsn(lua_State* L) {
    luaL_checkversion(L);

    if(LL == NULL) {
        LL = luaL_newstate();
        luaL_openlibs(LL);
        char code[512] = "\
                        function pack_output(subid, buf)\
                            return string.pack(\">I4s2\", subid, buf)\
                        end\
                        ";
        luaL_dostring(LL, code);
        // test
        // printf("luaopen_lkcp L=%p size2=%d\n", L, lua_gettop(LL));
        // int subid =100;
        // char str[128] = "dffccc";
        // lua_getglobal(LL, "pack_output");
        // lua_pushinteger(LL, subid);
        // lua_pushstring(LL, &str);
        // lua_call(LL, 2, 1);
        // size_t sz;
        // const char *r = luaL_checklstring(LL, 1, &sz);
        // printf("==xxxxxxrr r==%s sz==%d\n", r, sz);
        // lua_pop(LL, 1);
    }

    luaL_newmetatable(L, "kcp_meta");

    lua_newtable(L);
    luaL_setfuncs(L, lkcp_methods, 0);
    lua_setfield(L, -2, "__index");
    lua_pushcfunction(L, kcp_gc);
    lua_setfield(L, -2, "__gc");

    luaL_newmetatable(L, "recv_buffer");

    char* global_recv_buffer = lua_newuserdata(L, sizeof(char)*RECV_BUFFER_LEN);
    memset(global_recv_buffer, 0, sizeof(char)*RECV_BUFFER_LEN);
    luaL_getmetatable(L, "recv_buffer");
    lua_setmetatable(L, -2);
    lua_setfield(L, LUA_REGISTRYINDEX, "kcp_lua_recv_buffer");

    luaL_newlib(L, l_methods);

    return 1;
}

整体架构

bash 复制代码
+----------------+      +---------------+      +----------------+
|    Lua Layer   | ---> |   lkcpsn.c    | ---> |  KCP Protocol  |
+----------------+      +---------------+      +----------------+
       ↓                       ↓                       ↓
+----------------+      +---------------+      +----------------+
| Skynet Service | <--> | UDP Network   | <--> |  Remote Peer   |
+----------------+      +---------------+      +----------------+

文件结构详解

1. 头文件包含

c 复制代码
// 系统头文件
#include <stdlib.h>
#include <stdint.h>
#include <string.h>
#include <unistd.h>
#include <pthread.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <netdb.h>
#include <netinet/in.h>

// Lua 头文件
#include <lua.h>
#include <lauxlib.h>
#include <lualib.h>

// KCP 头文件
#include "ikcp.h"

// Skynet 头文件
#include "../../../skynet/skynet-src/skynet_server.h"
#include "../../../skynet/skynet-src/socket_buffer.h"
#include "../../../skynet/skynet-src/skynet_socket.h"

2. 宏定义与常量

c 复制代码
#define RECV_BUFFER_LEN 4*1024*1024  // 4MB 接收缓冲区

// Lua 类型检查宏
#define check_kcp(L, idx) \
    *(ikcpcb**)luaL_checkudata(L, idx, "kcp_meta")

#define check_buf(L, idx) \
    (char*)luaL_checkudata(L, idx, "recv_buffer")

// 调试日志控制
// #define logs_on  // 注释掉表示关闭调试日志

3. 全局变量与锁机制

c 复制代码
static pthread_mutex_t lock = PTHREAD_MUTEX_INITIALIZER;
static lua_State *LL = NULL;     // 全局 Lua 状态(用于 pack_output)
static uint64_t svrsock = 0;     // Skynet UDP socket ID

// 线程锁宏(当前被注释掉,表示单线程模式)
#define lock_on   // 空
#define lock_off  // 空

4. Callback 结构体

c 复制代码
struct Callback {
    uint64_t handle;      // Lua 回调函数引用(当前未使用)
    lua_State* L;        // 创建时的 Lua 状态
    lua_State* eL;       // 当前执行时的 Lua 状态
    char *address;       // 目标地址(二进制格式)
    size_t addresssz;    // 地址长度
};

5. 辅助函数

print_address - 地址打印

c 复制代码
static void print_address(const uint8_t *addr, size_t sz) {
    // 解析并打印二进制地址
    // 格式:[type(1字节) + port(2字节) + ip(4/16字节)]
    // type: 1=IPv4, 2=IPv6
}
  • 功能:调试用,打印二进制格式的地址
  • 地址格式:
bash 复制代码
IPv4: [type(1) + port(2) + ip(4)] = 7字节
IPv6: [type(1) + port(2) + ip(16)] = 19字节

6. 核心回调函数

kcp_output_callback - KCP 输出回调

c 复制代码
static int kcp_output_callback(const char *buf, int len, ikcpcb *kcp, void *arg) {
    struct Callback* c = (struct Callback*)arg;
    
    // 1. 获取 Skynet 上下文
    lua_getfield(c->eL, LUA_REGISTRYINDEX, "skynet_context");
    struct skynet_context *ctx = lua_touserdata(c->eL, -1);
    lua_pop(c->eL, 1);
    
    // 2. 调用 Lua 函数 pack_output 打包数据
    lua_getglobal(LL, "pack_output");
    lua_pushinteger(LL, kcp->conv);     // 会话ID
    lua_pushlstring(LL, buf, len);      // KCP数据
    lua_pcall(LL, 2, 1, 0);
    
    // 3. 获取打包后的数据
    const char* packed = luaL_checklstring(LL, 1, &size);
    
    // 4. 通过 Skynet UDP 发送
    struct socket_sendbuffer buf2;
    buf2.id = svrsock;
    buf2.type = SOCKET_BUFFER_RAWPOINTER;
    buf2.buffer = packed;
    buf2.sz = size;
    
    skynet_socket_udp_sendbuffer(ctx, c->address, &buf2);
}

打包格式说明:

bash 复制代码
-- Lua 中的 pack_output 函数
function pack_output(subid, buf)
    -- 使用 string.pack 打包数据
    -- ">I4s2" 表示:大端序的 4 字节整数 + 字符串(长度在前)
    return string.pack(">I4s2", subid, buf)
end

-- 实际打包结果:
-- +------------+------------+-------------+
-- | 会话ID(4B) | 数据长度(2B)| 数据(N字节) |
-- +------------+------------+-------------+

7. Lua C 函数实现

luaopen_lkcpsn - 模块初始化

c 复制代码
int luaopen_lkcpsn(lua_State* L) {
    // 1. 创建全局 Lua 状态 LL(用于 pack_output)
    if(LL == NULL) {
        LL = luaL_newstate();
        luaL_openlibs(LL);
        
        // 定义 pack_output 函数
        char code[512] = "function pack_output(subid, buf) return string.pack(\">I4s2\", subid, buf) end";
        luaL_dostring(LL, code);
    }
    
    // 2. 创建 KCP 元表
    luaL_newmetatable(L, "kcp_meta");
    lua_newtable(L);
    luaL_setfuncs(L, lkcp_methods, 0);
    lua_setfield(L, -2, "__index");
    lua_pushcfunction(L, kcp_gc);
    lua_setfield(L, -2, "__gc");
    
    // 3. 创建接收缓冲区元表和全局缓冲区
    luaL_newmetatable(L, "recv_buffer");
    char* global_recv_buffer = lua_newuserdata(L, sizeof(char)*RECV_BUFFER_LEN);
    memset(global_recv_buffer, 0, sizeof(char)*RECV_BUFFER_LEN);
    luaL_getmetatable(L, "recv_buffer");
    lua_setmetatable(L, -2);
    lua_setfield(L, LUA_REGISTRYINDEX, "kcp_lua_recv_buffer");
    
    // 4. 注册模块函数
    luaL_newlib(L, l_methods);
    
    return 1;
}

lkcp_create - 创建 KCP 实例

c 复制代码
static int lkcp_create(lua_State* L) {
    // 参数:sock, address, conv
    uint64_t sock = luaL_checkinteger(L, 1);
    const char *address = luaL_checklstring(L, 2, &sz);
    int32_t conv = luaL_checkinteger(L, 3);
    
    // 保存 socket ID
    if(sock && !svrsock) {
        svrsock = sock;
    }
    
    // 创建 Callback 结构
    struct Callback* c = malloc(sizeof(struct Callback));
    c->handle = 0;
    c->L = L;
    c->eL = L;
    c->address = (char*)malloc(sz+1);
    memcpy(c->address, address, sz);
    c->addresssz = sz;
    
    // 创建 KCP 实例
    ikcpcb* kcp = ikcp_create(conv, (void*)c);
    kcp->output = kcp_output_callback;
    
    // 创建 Lua userdata
    *(ikcpcb**)lua_newuserdata(L, sizeof(void*)) = kcp;
    luaL_getmetatable(L, "kcp_meta");
    lua_setmetatable(L, -2);
    
    return 1;
}

lkcp_recv - 接收数据

c 复制代码
static int lkcp_recv(lua_State* L) {
    ikcpcb* kcp = check_kcp(L, 1);
    
    // 获取全局接收缓冲区
    lua_getfield(L, LUA_REGISTRYINDEX, "kcp_lua_recv_buffer");
    char* buf = check_buf(L, -1);
    lua_pop(L, 1);
    
    // 接收数据
    int32_t hr = ikcp_recv(kcp, buf, RECV_BUFFER_LEN);
    if (hr <= 0) {
        lua_pushinteger(L, hr);  // 返回错误码
        return 1;
    }
    
    // 返回数据和长度
    lua_pushinteger(L, hr);
    lua_pushlstring(L, (const char *)buf, hr);
    
    return 2;
}

lkcp_send - 发送数据

c 复制代码
static int lkcp_send(lua_State* L) {
    ikcpcb* kcp = check_kcp(L, 1);
    const char *data = luaL_checklstring(L, 2, &size);
    
    // 发送数据
    int32_t hr = ikcp_send(kcp, data, size);
    lua_pushinteger(L, hr);
    
    // 可选:更新目标地址
    const uint8_t *address = (const uint8_t *)luaL_checklstring(L, 3, &sz);
    if(address && sz) {
        struct Callback* c = (struct Callback*)kcp->user;
        // 重新分配地址内存
    }
    
    return 1;
}

lkcp_input - 输入网络数据

c 复制代码
static int lkcp_input(lua_State* L) {
    ikcpcb* kcp = check_kcp(L, 1);
    const char *data = luaL_checklstring(L, 2, &size);
    
    // 处理输入数据
    int32_t hr = ikcp_input(kcp, data, size);
    lua_pushinteger(L, hr);
    
    // 可选:更新源地址
    const uint8_t *address = (const uint8_t *)luaL_checklstring(L, 3, &sz);
    if(address && sz) {
        struct Callback* c = (struct Callback*)kcp->user;
        // 更新地址
    }
    
    return 1;
}

kcp_gc - 垃圾回收

c 复制代码
static int kcp_gc(lua_State* L) {
    ikcpcb* kcp = check_kcp(L, 1);
    
    if (kcp->user != NULL) {
        struct Callback* c = (struct Callback*)kcp->user;
        // 清理资源
        if(c->address) {
            free(c->address);
            c->address = NULL;
        }
        free(c);
        kcp->user = NULL;
    }
    
    ikcp_release(kcp);
    
    return 0;
}

接口映射表

Lua 函数名 C 函数名 KCP 函数 功能描述
lkcp_create lkcp_create ikcp_create 创建 KCP 实例
lkcp_recv lkcp_recv ikcp_recv 接收数据
lkcp_send lkcp_send ikcp_send 发送数据
lkcp_update lkcp_update ikcp_update 更新 KCP 状态
lkcp_check lkcp_check ikcp_check 检查下次更新时间
lkcp_input lkcp_input ikcp_input 输入网络数据
lkcp_flush lkcp_flush ikcp_flush 强制刷新发送缓冲区
lkcp_wndsize lkcp_wndsize ikcp_wndsize 设置窗口大小
lkcp_nodelay lkcp_nodelay ikcp_nodelay 设置快速模式参数

数据流详解

发送流程

bash 复制代码
Lua发送数据 → lkcp_send() → ikcp_send() → 放入发送队列
              ↓
定时器触发 → lkcp_update() → ikcp_update() → ikcp_flush()
              ↓
需要发送 → kcp_output_callback() → pack_output() → 打包
              ↓
skynet_socket_udp_sendbuffer() → 网络发送

接收流程

bash 复制代码
网络接收 → Skynet UDP回调 → on_udp_message()
              ↓
解包 → string.unpack(">I4s2") → 分离会话ID和数据
              ↓
lkcp_input() → ikcp_input() → 解析处理
              ↓
定时器 → lkcp_update() → 触发接收处理
              ↓
应用层 → lkcp_recv() → ikcp_recv() → 获取数据

配置与调优

1. 缓冲区配置

c 复制代码
#define RECV_BUFFER_LEN 4*1024*1024  // 4MB
  • 调优建议:
    • 高并发:减小缓冲区,如 1MB
    • 大数据传输:增大缓冲区,如 8MB
    • 根据实际内存调整

2. 线程安全

bash 复制代码
// 当前实现是单线程安全的,因为:
// 1. lock_on/off 宏被注释掉(无锁)
// 2. Skynet 服务是单线程模型
// 3. 全局状态 LL 在初始化时创建

3. 地址管理

  • 地址格式:Skynet 二进制地址格式
  • 地址更新:lkcp_send 和 lkcp_input 可更新地址
  • 内存管理:需要手动管理地址内存

实现了什么?

1. KCP 到 Lua 的绑定 :为 Skynet 提供了 KCP 协议的 Lua 接口
2. Skynet 集成 :与 Skynet 的 UDP socket 系统无缝集成
3. 二进制协议 :使用自定义的打包格式传输数据
4. 地址管理 :支持动态更新目标地址
5. 资源管理:完善的垃圾回收机制

相关推荐
专注于大数据技术栈2 小时前
java学习--HashSet
java·学习·哈希算法
扶苏-su2 小时前
Java--标准输入输出流
java·开发语言
szm02252 小时前
Spring
java·后端·spring
奋斗的小青年!!2 小时前
Flutter跨平台开发OpenHarmony应用:个人中心实现
开发语言·前端·flutter·harmonyos·鸿蒙
石头wang2 小时前
jmeter java.lang.OutOfMemoryError: Java heap space 修改内存大小,指定自己的JDK
java·开发语言·jmeter
LawrenceLan2 小时前
Flutter 零基础入门(十五):继承、多态与面向对象三大特性
开发语言·前端·flutter·dart
zh_xuan2 小时前
kotlin对象表达式
开发语言·kotlin
AlexDeng2 小时前
EF Core 开发实践:Left Join 查询的多种实现方式
后端