kcp学习-通用的kcp lua绑定
- 源码
- [架构对比:lkcp.c vs lkcpsn.c](#架构对比:lkcp.c vs lkcpsn.c)
- 核心组件详解
-
- [1. Callback 结构体](#1. Callback 结构体)
- [2. 线程安全机制](#2. 线程安全机制)
- [3. 核心回调函数](#3. 核心回调函数)
- [4. Lua C 函数详解](#4. Lua C 函数详解)
- [5. 模块初始化](#5. 模块初始化)
- 完整使用示例
- 总结
一个通用版本的 KCP Lua 绑定,不依赖于特定的框架(如 Skynet),可以独立使用
源码
lkcp.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"
#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;
};
static pthread_mutex_t lock = PTHREAD_MUTEX_INITIALIZER;
// #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 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);
}
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;
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);
struct Callback* c = malloc(sizeof(struct Callback));
memset(c, 0, sizeof(struct Callback));
c->handle = handle;
c->L = L;
c->eL = L;
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
printf("debug kcp lkcp_create kcp=%p L=%p handle=%ld conv=%d\n", kcp, L, c->handle, conv);
#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);
#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);
#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_lkcp(lua_State* L) {
luaL_checkversion(L);
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;
}
架构对比:lkcp.c vs lkcpsn.c
| 特性 | lkcp.c(通用版) | lkcpsn.c(Skynet版) |
|---|---|---|
| 依赖 | 无框架依赖 | 依赖 Skynet 框架 |
| 输出方式 | Lua 回调函数 | 直接通过 Skynet UDP 发送 |
| 地址管理 | 无地址管理 | 内置地址管理 |
| 线程模型 | 可选线程安全 | 单线程安全 |
| 使用场景 | 通用 Lua 项目 | Skynet 专用 |
核心组件详解
1. Callback 结构体
c
struct Callback {
uint64_t handle; // Lua 回调函数的引用(Registry 索引)
lua_State* L; // 创建 KCP 时的 Lua 状态
lua_State* eL; // 当前执行时的 Lua 状态
};
- handle: Lua 函数的引用 ID,通过 luaL_ref 获得
- L: 保存创建时的 Lua 状态,用于垃圾回收时解除引用
- eL: 实时更新,确保在正确的 Lua 上下文中执行回调
2. 线程安全机制
c
static pthread_mutex_t lock = PTHREAD_MUTEX_INITIALIZER;
#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");\
- 默认禁用:锁相关代码被注释掉,单线程模式下性能更好
- 可启用:需要多线程时取消注释即可
- 设计考虑:大多数 Lua 应用是单线程的,避免不必要的锁开销
3. 核心回调函数
kcp_output_callback - 输出回调
c
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
// 1. 从 Registry 中获取 Lua 函数
lua_rawgeti(L, LUA_REGISTRYINDEX, handle);
// 2. 检查是否为函数
if(lua_isfunction(L, -1)) {
// 3. 将数据推入栈
lua_pushlstring(L, buf, len);
// 4. 调用 Lua 函数 (1个参数,0个返回值)
lua_call(L, 1, 0);
} else {
printf("debug kcp kcp_output_callback error1\n");
}
} else {
printf("debug kcp kcp_output_callback error2\n");
}
return 0;
}
工作流程:
- 从 handle 获取 Lua 回调函数
- 检查函数有效性
- 将 KCP 数据作为参数调用 Lua 函数
- Lua 函数负责实际的数据发送(如 UDP 发送)
4. Lua C 函数详解
lkcp_create - 创建 KCP 对象
c
static int lkcp_create(lua_State* L) {
lock_on
// 获取参数:第一个参数是会话ID,栈顶是回调函数
uint64_t handle = luaL_ref(L, LUA_REGISTRYINDEX); // 将栈顶函数保存到 Registry
int32_t conv = luaL_checkinteger(L, 1); // 会话ID
// 创建回调结构
struct Callback* c = malloc(sizeof(struct Callback));
memset(c, 0, sizeof(struct Callback));
c->handle = handle; // Lua 函数引用
c->L = L; // 当前 Lua 状态
c->eL = L; // 执行 Lua 状态(初始相同)
// 创建 KCP 实例
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;
// 创建 Lua userdata
*(ikcpcb**)lua_newuserdata(L, sizeof(void*)) = kcp;
luaL_getmetatable(L, "kcp_meta");
lua_setmetatable(L, -2);
#ifdef logs_on
printf("debug kcp lkcp_create kcp=%p L=%p handle=%ld conv=%d\n",
kcp, L, c->handle, conv);
#endif
lock_off
return 1;
}
Lua 调用方式:
lua
local kcp = lkcp.lkcp_create(conv, function(data)
-- 这里处理数据发送
udp_socket:send(data)
end)
lkcp_recv - 接收数据
c
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;
}
// 从 Registry 获取全局接收缓冲区
lua_getfield(L, LUA_REGISTRYINDEX, "kcp_lua_recv_buffer");
char* buf = check_buf(L, -1); // 4MB 的缓冲区
lua_pop(L, 1);
// 调用 KCP 接收
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);
lock_off
return 2;
}
缓冲区管理:
- 全局缓冲区:所有 KCP 实例共享一个 4MB 的缓冲区
- 避免频繁分配:减少内存分配开销
- 线程安全:由于是只读操作,多线程安全
lkcp_send - 发送数据
c
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);
// 调用 KCP 发送
int32_t hr = ikcp_send(kcp, data, size);
lua_pushinteger(L, hr); // 返回结果
lock_off
return 1;
}
注意:这里只是将数据放入 KCP 发送队列,实际发送由 kcp_output_callback 触发
lkcp_update - 更新 KCP 状态
c
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);
// 更新当前 Lua 状态
struct Callback* c = (struct Callback*)kcp->user;
c->eL = L; // 确保回调在正确的上下文中执行
// 更新 KCP
ikcp_update(kcp, current);
lock_off
return 0;
}
关键点:
- 更新 c->eL = L:确保输出回调在正确的 Lua 上下文中
- 定期调用:每 10-100ms 调用一次
lkcp_check - 检查下次更新时间
c
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); // 返回下次更新时间
lock_off
return 1;
}
优化用途:
c
local next_time = kcp:lkcp_check(current_time)
if next_time > current_time then
-- 可以休眠到 next_time
sleep(next_time - current_time)
end
lkcp_input - 输入网络数据
c
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);
// 将网络数据输入到 KCP
int32_t hr = ikcp_input(kcp, data, size);
lua_pushinteger(L, hr); // 返回结果
lock_off
return 1;
}
使用场景:
c
-- 当收到 UDP 数据时
udp_socket:receive(function(data)
local result = kcp:lkcp_input(data)
if result < 0 then
print("KCP input error:", result)
end
end)
lkcp_flush - 强制刷新
c
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);
lock_off
return 0;
}
注意:通常不需要手动调用,ikcp_update 会自动处理
lkcp_wndsize - 设置窗口大小
c
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;
}
lkcp_nodelay - 设置快速模式
c
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;
}
kcp_gc - 垃圾回收
c
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
// 1. 释放 Lua 函数引用
if(c->handle > 0) {
luaL_unref(c->L, LUA_REGISTRYINDEX, c->handle);
c->handle = 0;
}
// 2. 清理指针
c->L = NULL;
c->eL = NULL;
// 3. 释放内存
free(c);
kcp->user = NULL;
}
// 4. 释放 KCP 实例
ikcp_release(kcp);
kcp = NULL;
lock_off
return 0;
}
关键步骤:
- 从 Registry 中移除 Lua 函数引用
- 防止野指针
- 释放回调结构内存
- 释放 KCP 实例
5. 模块初始化
luaopen_lkcp - 模块入口
c
int luaopen_lkcp(lua_State* L) {
luaL_checkversion(L); // 检查 Lua 版本兼容性
// 1. 创建 KCP 元表
luaL_newmetatable(L, "kcp_meta");
// 2. 设置元方法
lua_newtable(L);
luaL_setfuncs(L, lkcp_methods, 0); // 注册方法表
lua_setfield(L, -2, "__index"); // 设置索引元方法
// 3. 设置垃圾回收元方法
lua_pushcfunction(L, kcp_gc);
lua_setfield(L, -2, "__gc");
// 4. 创建接收缓冲区元表
luaL_newmetatable(L, "recv_buffer");
// 5. 创建全局接收缓冲区(4MB)
char* global_recv_buffer = lua_newuserdata(L, sizeof(char)*RECV_BUFFER_LEN);
memset(global_recv_buffer, 0, sizeof(char)*RECV_BUFFER_LEN);
// 6. 设置元表
luaL_getmetatable(L, "recv_buffer");
lua_setmetatable(L, -2);
// 7. 保存到 Registry
lua_setfield(L, LUA_REGISTRYINDEX, "kcp_lua_recv_buffer");
// 8. 创建模块函数表
luaL_newlib(L, l_methods);
return 1; // 返回模块表
}
完整使用示例
lua
-- 1. 加载模块
local lkcp = require "lkcp"
-- 2. 创建 UDP socket(假设有 LuaSocket)
local socket = require "socket"
local udp = socket.udp()
udp:settimeout(0)
udp:setsockname("127.0.0.1", 8888)
-- 3. 创建 KCP 实例(会话ID为 12345)
local kcp = lkcp.lkcp_create(12345, function(data)
-- 输出回调:发送数据到网络
udp:sendto(data, "127.0.0.1", 9999)
end)
-- 4. 配置 KCP
kcp:lkcp_nodelay(1, 10, 2, 1) -- 快速模式
kcp:lkcp_wndsize(128, 128) -- 窗口大小
-- 5. 发送数据
function send_data(data)
local result = kcp:lkcp_send(data)
if result < 0 then
print("Send error:", result)
end
end
-- 6. 接收数据循环
function receive_loop()
while true do
-- 检查网络数据
local data, ip, port = udp:receivefrom()
if data then
-- 输入到 KCP
local result = kcp:lkcp_input(data)
if result < 0 then
print("Input error:", result)
end
end
-- 更新 KCP
local current_time = socket.gettime() * 1000 -- 毫秒
kcp:lkcp_update(math.floor(current_time))
-- 接收 KCP 数据
local len, kcp_data = kcp:lkcp_recv()
if len > 0 then
-- 处理应用层数据
process_application_data(kcp_data)
end
-- 使用 ikcp_check 优化
local next_time = kcp:lkcp_check(math.floor(current_time))
if next_time > current_time then
-- 休眠到下次更新时间
socket.sleep((next_time - current_time) / 1000)
else
socket.sleep(0.001) -- 1ms
end
end
end
总结
这个 lkcp.c 文件实现了一个干净、高效、可移植的 KCP Lua 绑定:
优点:
- 无外部依赖:纯 C + Lua,易于集成到任何项目
- 设计简洁:回调机制灵活,适应各种网络后端
- 内存高效:共享接收缓冲区,减少内存分配
- 线程安全可选:默认单线程模式,性能更好
- 完整的垃圾回收:避免内存泄漏
缺点:
- 需要手动处理网络 I/O:回调函数中需要自行实现发送逻辑
- 地址管理需要外部处理:不像 lkcpsn.c 那样内置地址管理