feat: 初始化 OmniSocket 目录结构与底层网络接口
This commit is contained in:
16
.vscode/launch.json
vendored
Normal file
16
.vscode/launch.json
vendored
Normal file
@@ -0,0 +1,16 @@
|
||||
{
|
||||
// 使用 IntelliSense 了解相关属性。
|
||||
// 悬停以查看现有属性的描述。
|
||||
// 欲了解更多信息,请访问: https://go.microsoft.com/fwlink/?linkid=830387
|
||||
"version": "0.2.0",
|
||||
"configurations": [
|
||||
{
|
||||
"name": "OmniSocket",
|
||||
"type": "lldb",
|
||||
"request": "launch",
|
||||
"program": "${workspaceRoot}/build",
|
||||
"args": [],
|
||||
"cwd": "${workspaceRoot}"
|
||||
}
|
||||
]
|
||||
}
|
||||
40
include/common.h
Normal file
40
include/common.h
Normal file
@@ -0,0 +1,40 @@
|
||||
/*
|
||||
* common.h
|
||||
* 全局公共定义:消息头、错误码、通用宏
|
||||
*/
|
||||
|
||||
#ifndef OMNISOCKET_COMMON_H
|
||||
#define OMNISOCKET_COMMON_H
|
||||
|
||||
#include <stdint.h>
|
||||
#include <time.h>
|
||||
|
||||
/* 统一的 16 字节消息头(解决 TCP 粘包用) */
|
||||
typedef struct MsgHeader {
|
||||
uint32_t magic; /* 固定魔数,用于快速校验 */
|
||||
uint32_t length; /* 后续负载长度(字节数) */
|
||||
uint64_t seq; /* 序列号或会话内消息 ID */
|
||||
} MsgHeader;
|
||||
|
||||
#define MSG_HEADER_SIZE (sizeof(MsgHeader)) /* 16 字节 */
|
||||
#define MSG_MAGIC 0x4F4D4E49u /* 'OMNI' */
|
||||
|
||||
/* 通用错误码(负数返回表示出错) */
|
||||
enum {
|
||||
OMNI_OK = 0,
|
||||
OMNI_ERR_GENERIC = -1,
|
||||
OMNI_ERR_PARAM = -2,
|
||||
OMNI_ERR_IO = -3,
|
||||
OMNI_ERR_TIMEOUT = -4
|
||||
};
|
||||
|
||||
/* 获取当前单调时间(毫秒),用于延迟统计 */
|
||||
static inline uint64_t omni_now_ms(void)
|
||||
{
|
||||
struct timespec ts;
|
||||
clock_gettime(CLOCK_MONOTONIC, &ts);
|
||||
return (uint64_t)ts.tv_sec * 1000u + (uint64_t)(ts.tv_nsec / 1000000u);
|
||||
}
|
||||
|
||||
#endif /* OMNISOCKET_COMMON_H */
|
||||
|
||||
416
include/kcp/ikcp.h
Normal file
416
include/kcp/ikcp.h
Normal file
@@ -0,0 +1,416 @@
|
||||
//=====================================================================
|
||||
//
|
||||
// KCP - A Better ARQ Protocol Implementation
|
||||
// skywind3000 (at) gmail.com, 2010-2011
|
||||
//
|
||||
// Features:
|
||||
// + Average RTT reduce 30% - 40% vs traditional ARQ like tcp.
|
||||
// + Maximum RTT reduce three times vs tcp.
|
||||
// + Lightweight, distributed as a single source file.
|
||||
//
|
||||
//=====================================================================
|
||||
#ifndef __IKCP_H__
|
||||
#define __IKCP_H__
|
||||
|
||||
#include <stddef.h>
|
||||
#include <stdlib.h>
|
||||
#include <assert.h>
|
||||
|
||||
|
||||
//=====================================================================
|
||||
// 32BIT INTEGER DEFINITION
|
||||
//=====================================================================
|
||||
#ifndef __INTEGER_32_BITS__
|
||||
#define __INTEGER_32_BITS__
|
||||
#if defined(_WIN64) || defined(WIN64) || defined(__amd64__) || \
|
||||
defined(__x86_64) || defined(__x86_64__) || defined(_M_IA64) || \
|
||||
defined(_M_AMD64)
|
||||
typedef unsigned int ISTDUINT32;
|
||||
typedef int ISTDINT32;
|
||||
#elif defined(_WIN32) || defined(WIN32) || defined(__i386__) || \
|
||||
defined(__i386) || defined(_M_X86)
|
||||
typedef unsigned long ISTDUINT32;
|
||||
typedef long ISTDINT32;
|
||||
#elif defined(__MACOS__)
|
||||
typedef UInt32 ISTDUINT32;
|
||||
typedef SInt32 ISTDINT32;
|
||||
#elif defined(__APPLE__) && defined(__MACH__)
|
||||
#include <sys/types.h>
|
||||
typedef u_int32_t ISTDUINT32;
|
||||
typedef int32_t ISTDINT32;
|
||||
#elif defined(__BEOS__)
|
||||
#include <sys/inttypes.h>
|
||||
typedef u_int32_t ISTDUINT32;
|
||||
typedef int32_t ISTDINT32;
|
||||
#elif (defined(_MSC_VER) || defined(__BORLANDC__)) && (!defined(__MSDOS__))
|
||||
typedef unsigned __int32 ISTDUINT32;
|
||||
typedef __int32 ISTDINT32;
|
||||
#elif defined(__GNUC__)
|
||||
#include <stdint.h>
|
||||
typedef uint32_t ISTDUINT32;
|
||||
typedef int32_t ISTDINT32;
|
||||
#else
|
||||
typedef unsigned long ISTDUINT32;
|
||||
typedef long ISTDINT32;
|
||||
#endif
|
||||
#endif
|
||||
|
||||
|
||||
//=====================================================================
|
||||
// Integer Definition
|
||||
//=====================================================================
|
||||
#ifndef __IINT8_DEFINED
|
||||
#define __IINT8_DEFINED
|
||||
typedef char IINT8;
|
||||
#endif
|
||||
|
||||
#ifndef __IUINT8_DEFINED
|
||||
#define __IUINT8_DEFINED
|
||||
typedef unsigned char IUINT8;
|
||||
#endif
|
||||
|
||||
#ifndef __IUINT16_DEFINED
|
||||
#define __IUINT16_DEFINED
|
||||
typedef unsigned short IUINT16;
|
||||
#endif
|
||||
|
||||
#ifndef __IINT16_DEFINED
|
||||
#define __IINT16_DEFINED
|
||||
typedef short IINT16;
|
||||
#endif
|
||||
|
||||
#ifndef __IINT32_DEFINED
|
||||
#define __IINT32_DEFINED
|
||||
typedef ISTDINT32 IINT32;
|
||||
#endif
|
||||
|
||||
#ifndef __IUINT32_DEFINED
|
||||
#define __IUINT32_DEFINED
|
||||
typedef ISTDUINT32 IUINT32;
|
||||
#endif
|
||||
|
||||
#ifndef __IINT64_DEFINED
|
||||
#define __IINT64_DEFINED
|
||||
#if defined(_MSC_VER) || defined(__BORLANDC__)
|
||||
typedef __int64 IINT64;
|
||||
#else
|
||||
typedef long long IINT64;
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#ifndef __IUINT64_DEFINED
|
||||
#define __IUINT64_DEFINED
|
||||
#if defined(_MSC_VER) || defined(__BORLANDC__)
|
||||
typedef unsigned __int64 IUINT64;
|
||||
#else
|
||||
typedef unsigned long long IUINT64;
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#ifndef INLINE
|
||||
#if defined(__GNUC__)
|
||||
|
||||
#if (__GNUC__ > 3) || ((__GNUC__ == 3) && (__GNUC_MINOR__ >= 1))
|
||||
#define INLINE __inline__ __attribute__((always_inline))
|
||||
#else
|
||||
#define INLINE __inline__
|
||||
#endif
|
||||
|
||||
#elif (defined(_MSC_VER) || defined(__BORLANDC__) || defined(__WATCOMC__))
|
||||
#define INLINE __inline
|
||||
#else
|
||||
#define INLINE
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#if (!defined(__cplusplus)) && (!defined(inline))
|
||||
#define inline INLINE
|
||||
#endif
|
||||
|
||||
|
||||
//=====================================================================
|
||||
// QUEUE DEFINITION
|
||||
//=====================================================================
|
||||
#ifndef __IQUEUE_DEF__
|
||||
#define __IQUEUE_DEF__
|
||||
|
||||
struct IQUEUEHEAD {
|
||||
struct IQUEUEHEAD *next, *prev;
|
||||
};
|
||||
|
||||
typedef struct IQUEUEHEAD iqueue_head;
|
||||
|
||||
|
||||
//---------------------------------------------------------------------
|
||||
// queue init
|
||||
//---------------------------------------------------------------------
|
||||
#define IQUEUE_HEAD_INIT(name) { &(name), &(name) }
|
||||
#define IQUEUE_HEAD(name) \
|
||||
struct IQUEUEHEAD name = IQUEUE_HEAD_INIT(name)
|
||||
|
||||
#define IQUEUE_INIT(ptr) ( \
|
||||
(ptr)->next = (ptr), (ptr)->prev = (ptr))
|
||||
|
||||
#define IOFFSETOF(TYPE, MEMBER) ((size_t) &((TYPE *)0)->MEMBER)
|
||||
|
||||
#define ICONTAINEROF(ptr, type, member) ( \
|
||||
(type*)( ((char*)((type*)ptr)) - IOFFSETOF(type, member)) )
|
||||
|
||||
#define IQUEUE_ENTRY(ptr, type, member) ICONTAINEROF(ptr, type, member)
|
||||
|
||||
|
||||
//---------------------------------------------------------------------
|
||||
// queue operation
|
||||
//---------------------------------------------------------------------
|
||||
#define IQUEUE_ADD(node, head) ( \
|
||||
(node)->prev = (head), (node)->next = (head)->next, \
|
||||
(head)->next->prev = (node), (head)->next = (node))
|
||||
|
||||
#define IQUEUE_ADD_TAIL(node, head) ( \
|
||||
(node)->prev = (head)->prev, (node)->next = (head), \
|
||||
(head)->prev->next = (node), (head)->prev = (node))
|
||||
|
||||
#define IQUEUE_DEL_BETWEEN(p, n) ((n)->prev = (p), (p)->next = (n))
|
||||
|
||||
#define IQUEUE_DEL(entry) (\
|
||||
(entry)->next->prev = (entry)->prev, \
|
||||
(entry)->prev->next = (entry)->next, \
|
||||
(entry)->next = 0, (entry)->prev = 0)
|
||||
|
||||
#define IQUEUE_DEL_INIT(entry) do { \
|
||||
IQUEUE_DEL(entry); IQUEUE_INIT(entry); } while (0)
|
||||
|
||||
#define IQUEUE_IS_EMPTY(entry) ((entry) == (entry)->next)
|
||||
|
||||
#define iqueue_init IQUEUE_INIT
|
||||
#define iqueue_entry IQUEUE_ENTRY
|
||||
#define iqueue_add IQUEUE_ADD
|
||||
#define iqueue_add_tail IQUEUE_ADD_TAIL
|
||||
#define iqueue_del IQUEUE_DEL
|
||||
#define iqueue_del_init IQUEUE_DEL_INIT
|
||||
#define iqueue_is_empty IQUEUE_IS_EMPTY
|
||||
|
||||
#define IQUEUE_FOREACH(iterator, head, TYPE, MEMBER) \
|
||||
for ((iterator) = iqueue_entry((head)->next, TYPE, MEMBER); \
|
||||
&((iterator)->MEMBER) != (head); \
|
||||
(iterator) = iqueue_entry((iterator)->MEMBER.next, TYPE, MEMBER))
|
||||
|
||||
#define iqueue_foreach(iterator, head, TYPE, MEMBER) \
|
||||
IQUEUE_FOREACH(iterator, head, TYPE, MEMBER)
|
||||
|
||||
#define iqueue_foreach_entry(pos, head) \
|
||||
for( (pos) = (head)->next; (pos) != (head) ; (pos) = (pos)->next )
|
||||
|
||||
|
||||
#define __iqueue_splice(list, head) do { \
|
||||
iqueue_head *first = (list)->next, *last = (list)->prev; \
|
||||
iqueue_head *at = (head)->next; \
|
||||
(first)->prev = (head), (head)->next = (first); \
|
||||
(last)->next = (at), (at)->prev = (last); } while (0)
|
||||
|
||||
#define iqueue_splice(list, head) do { \
|
||||
if (!iqueue_is_empty(list)) __iqueue_splice(list, head); } while (0)
|
||||
|
||||
#define iqueue_splice_init(list, head) do { \
|
||||
iqueue_splice(list, head); iqueue_init(list); } while (0)
|
||||
|
||||
|
||||
#ifdef _MSC_VER
|
||||
#pragma warning(disable:4311)
|
||||
#pragma warning(disable:4312)
|
||||
#pragma warning(disable:4996)
|
||||
#endif
|
||||
|
||||
#endif
|
||||
|
||||
|
||||
//---------------------------------------------------------------------
|
||||
// BYTE ORDER & ALIGNMENT
|
||||
//---------------------------------------------------------------------
|
||||
#ifndef IWORDS_BIG_ENDIAN
|
||||
#ifdef _BIG_ENDIAN_
|
||||
#if _BIG_ENDIAN_
|
||||
#define IWORDS_BIG_ENDIAN 1
|
||||
#endif
|
||||
#endif
|
||||
#ifndef IWORDS_BIG_ENDIAN
|
||||
#if defined(__hppa__) || \
|
||||
defined(__m68k__) || defined(mc68000) || defined(_M_M68K) || \
|
||||
(defined(__MIPS__) && defined(__MIPSEB__)) || \
|
||||
defined(__ppc__) || defined(__POWERPC__) || defined(_M_PPC) || \
|
||||
defined(__sparc__) || defined(__powerpc__) || \
|
||||
defined(__mc68000__) || defined(__s390x__) || defined(__s390__)
|
||||
#define IWORDS_BIG_ENDIAN 1
|
||||
#endif
|
||||
#endif
|
||||
#ifndef IWORDS_BIG_ENDIAN
|
||||
#define IWORDS_BIG_ENDIAN 0
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#ifndef IWORDS_MUST_ALIGN
|
||||
#if defined(__i386__) || defined(__i386) || defined(_i386_)
|
||||
#define IWORDS_MUST_ALIGN 0
|
||||
#elif defined(_M_IX86) || defined(_X86_) || defined(__x86_64__)
|
||||
#define IWORDS_MUST_ALIGN 0
|
||||
#elif defined(__amd64) || defined(__amd64__)
|
||||
#define IWORDS_MUST_ALIGN 0
|
||||
#else
|
||||
#define IWORDS_MUST_ALIGN 1
|
||||
#endif
|
||||
#endif
|
||||
|
||||
|
||||
//=====================================================================
|
||||
// SEGMENT
|
||||
//=====================================================================
|
||||
struct IKCPSEG
|
||||
{
|
||||
struct IQUEUEHEAD node;
|
||||
IUINT32 conv;
|
||||
IUINT32 cmd;
|
||||
IUINT32 frg;
|
||||
IUINT32 wnd;
|
||||
IUINT32 ts;
|
||||
IUINT32 sn;
|
||||
IUINT32 una;
|
||||
IUINT32 len;
|
||||
IUINT32 resendts;
|
||||
IUINT32 rto;
|
||||
IUINT32 fastack;
|
||||
IUINT32 xmit;
|
||||
char data[1];
|
||||
};
|
||||
|
||||
|
||||
//---------------------------------------------------------------------
|
||||
// IKCPCB
|
||||
//---------------------------------------------------------------------
|
||||
struct IKCPCB
|
||||
{
|
||||
IUINT32 conv, mtu, mss, state;
|
||||
IUINT32 snd_una, snd_nxt, rcv_nxt;
|
||||
IUINT32 ts_recent, ts_lastack, ssthresh;
|
||||
IINT32 rx_rttval, rx_srtt, rx_rto, rx_minrto;
|
||||
IUINT32 snd_wnd, rcv_wnd, rmt_wnd, cwnd, probe;
|
||||
IUINT32 current, interval, ts_flush, xmit;
|
||||
IUINT32 nrcv_buf, nsnd_buf;
|
||||
IUINT32 nrcv_que, nsnd_que;
|
||||
IUINT32 nodelay, updated;
|
||||
IUINT32 ts_probe, probe_wait;
|
||||
IUINT32 dead_link, incr;
|
||||
struct IQUEUEHEAD snd_queue;
|
||||
struct IQUEUEHEAD rcv_queue;
|
||||
struct IQUEUEHEAD snd_buf;
|
||||
struct IQUEUEHEAD rcv_buf;
|
||||
IUINT32 *acklist;
|
||||
IUINT32 ackcount;
|
||||
IUINT32 ackblock;
|
||||
void *user;
|
||||
char *buffer;
|
||||
int fastresend;
|
||||
int fastlimit;
|
||||
int nocwnd, stream;
|
||||
int logmask;
|
||||
int (*output)(const char *buf, int len, struct IKCPCB *kcp, void *user);
|
||||
void (*writelog)(const char *log, struct IKCPCB *kcp, void *user);
|
||||
};
|
||||
|
||||
|
||||
typedef struct IKCPCB ikcpcb;
|
||||
|
||||
#define IKCP_LOG_OUTPUT 1
|
||||
#define IKCP_LOG_INPUT 2
|
||||
#define IKCP_LOG_SEND 4
|
||||
#define IKCP_LOG_RECV 8
|
||||
#define IKCP_LOG_IN_DATA 16
|
||||
#define IKCP_LOG_IN_ACK 32
|
||||
#define IKCP_LOG_IN_PROBE 64
|
||||
#define IKCP_LOG_IN_WINS 128
|
||||
#define IKCP_LOG_OUT_DATA 256
|
||||
#define IKCP_LOG_OUT_ACK 512
|
||||
#define IKCP_LOG_OUT_PROBE 1024
|
||||
#define IKCP_LOG_OUT_WINS 2048
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
//---------------------------------------------------------------------
|
||||
// interface
|
||||
//---------------------------------------------------------------------
|
||||
|
||||
// create a new kcp control object, 'conv' must equal in two endpoint
|
||||
// from the same connection. 'user' will be passed to the output callback
|
||||
// output callback can be setup like this: 'kcp->output = my_udp_output'
|
||||
ikcpcb* ikcp_create(IUINT32 conv, void *user);
|
||||
|
||||
// release kcp control object
|
||||
void ikcp_release(ikcpcb *kcp);
|
||||
|
||||
// set output callback, which will be invoked by kcp
|
||||
void ikcp_setoutput(ikcpcb *kcp, int (*output)(const char *buf, int len,
|
||||
ikcpcb *kcp, void *user));
|
||||
|
||||
// user/upper level recv: returns size, returns below zero for EAGAIN
|
||||
int ikcp_recv(ikcpcb *kcp, char *buffer, int len);
|
||||
|
||||
// user/upper level send, returns below zero for error
|
||||
int ikcp_send(ikcpcb *kcp, const char *buffer, int len);
|
||||
|
||||
// update state (call it repeatedly, every 10ms-100ms), or you can ask
|
||||
// ikcp_check when to call it again (without ikcp_input/_send calling).
|
||||
// 'current' - current timestamp in millisec.
|
||||
void ikcp_update(ikcpcb *kcp, IUINT32 current);
|
||||
|
||||
// Determine when should you invoke ikcp_update:
|
||||
// returns when you should invoke ikcp_update in millisec, if there
|
||||
// is no ikcp_input/_send calling. you can call ikcp_update in that
|
||||
// time, instead of call update repeatly.
|
||||
// Important to reduce unnacessary ikcp_update invoking. use it to
|
||||
// schedule ikcp_update (eg. implementing an epoll-like mechanism,
|
||||
// or optimize ikcp_update when handling massive kcp connections)
|
||||
IUINT32 ikcp_check(const ikcpcb *kcp, IUINT32 current);
|
||||
|
||||
// when you received a low level packet (eg. UDP packet), call it
|
||||
int ikcp_input(ikcpcb *kcp, const char *data, long size);
|
||||
|
||||
// flush pending data
|
||||
void ikcp_flush(ikcpcb *kcp);
|
||||
|
||||
// check the size of next message in the recv queue
|
||||
int ikcp_peeksize(const ikcpcb *kcp);
|
||||
|
||||
// change MTU size, default is 1400
|
||||
int ikcp_setmtu(ikcpcb *kcp, int mtu);
|
||||
|
||||
// set maximum window size: sndwnd=32, rcvwnd=32 by default
|
||||
int ikcp_wndsize(ikcpcb *kcp, int sndwnd, int rcvwnd);
|
||||
|
||||
// get how many packet is waiting to be sent
|
||||
int ikcp_waitsnd(const ikcpcb *kcp);
|
||||
|
||||
// fastest: ikcp_nodelay(kcp, 1, 20, 2, 1)
|
||||
// nodelay: 0:disable(default), 1:enable
|
||||
// interval: internal update timer interval in millisec, default is 100ms
|
||||
// resend: 0:disable fast resend(default), 1:enable fast resend
|
||||
// nc: 0:normal congestion control(default), 1:disable congestion control
|
||||
int ikcp_nodelay(ikcpcb *kcp, int nodelay, int interval, int resend, int nc);
|
||||
|
||||
|
||||
void ikcp_log(ikcpcb *kcp, int mask, const char *fmt, ...);
|
||||
|
||||
// setup allocator
|
||||
void ikcp_allocator(void* (*new_malloc)(size_t), void (*new_free)(void*));
|
||||
|
||||
// read conv
|
||||
IUINT32 ikcp_getconv(const void *ptr);
|
||||
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif
|
||||
|
||||
|
||||
58
include/logger.h
Normal file
58
include/logger.h
Normal file
@@ -0,0 +1,58 @@
|
||||
/*
|
||||
* logger.h
|
||||
* 日志与性能统计接口
|
||||
*/
|
||||
|
||||
#ifndef OMNISOCKET_LOGGER_H
|
||||
#define OMNISOCKET_LOGGER_H
|
||||
|
||||
#include <stddef.h>
|
||||
#include <stdint.h>
|
||||
|
||||
/* 通过该结构体收集全局统计信息 */
|
||||
typedef struct OmniStats {
|
||||
uint64_t start_ms; /* 起始时间(毫秒) */
|
||||
uint64_t last_report_ms; /* 上一次打印日志时间 */
|
||||
|
||||
uint64_t bytes_sent; /* 发送总字节数 */
|
||||
uint64_t bytes_recv; /* 接收总字节数 */
|
||||
|
||||
uint64_t send_count; /* 调用 omni_send 次数 */
|
||||
uint64_t recv_count; /* 调用 omni_recv 次数 */
|
||||
|
||||
uint64_t last_rtt_ms; /* 最近一次 RTT */
|
||||
uint64_t max_rtt_ms; /* 最大 RTT */
|
||||
|
||||
uint64_t tcp_retrans; /* 预留:TCP 重传统计(如可从内核获取) */
|
||||
uint64_t udp_retrans; /* UDP 上层重传次数 */
|
||||
uint64_t kcp_retrans; /* KCP 内部重传次数(可从 ikcp 统计) */
|
||||
} OmniStats;
|
||||
|
||||
/* 初始化统计模块,在程序启动时调用一次 */
|
||||
void logger_init(void);
|
||||
|
||||
/* 记录一次发送/接收 */
|
||||
void logger_on_send(size_t bytes);
|
||||
void logger_on_recv(size_t bytes);
|
||||
|
||||
/* 记录一次 RTT(由上层在合适时机调用) */
|
||||
void logger_on_rtt(uint64_t rtt_ms);
|
||||
|
||||
/* 记录 KCP 重传次数变化(可在 KCP 更新循环中调用) */
|
||||
void logger_on_kcp_retrans(uint64_t delta);
|
||||
|
||||
/* 计算当前吞吐量(返回:字节/秒) */
|
||||
double logger_calculate_throughput(void);
|
||||
|
||||
/* 打印一条结构化性能日志(例如每隔若干秒调用) */
|
||||
void logger_print_performance_log(const char *tag);
|
||||
|
||||
/* 结构化通用日志(key=value 形式) */
|
||||
void logger_log(const char *level, const char *component,
|
||||
const char *fmt, ...);
|
||||
|
||||
/* 获取内部统计快照(线程不安全,仅调试用) */
|
||||
OmniStats logger_get_snapshot(void);
|
||||
|
||||
#endif /* OMNISOCKET_LOGGER_H */
|
||||
|
||||
78
include/network.h
Normal file
78
include/network.h
Normal file
@@ -0,0 +1,78 @@
|
||||
/*
|
||||
* network.h
|
||||
* 统一的协议抽象层,对上层暴露 omni_* 接口
|
||||
*
|
||||
* 支持 TCP / UDP / KCP 三种协议,通过命令行参数切换:
|
||||
* -t 使用 TCP
|
||||
* -u 使用 UDP
|
||||
* -k 使用 KCP
|
||||
*/
|
||||
|
||||
#ifndef OMNISOCKET_NETWORK_H
|
||||
#define OMNISOCKET_NETWORK_H
|
||||
|
||||
#include <stddef.h>
|
||||
#include <stdint.h>
|
||||
#include <sys/types.h> /* for ssize_t */
|
||||
|
||||
/* 协议类型 */
|
||||
typedef enum {
|
||||
OMNI_PROTO_TCP = 0,
|
||||
OMNI_PROTO_UDP = 1,
|
||||
OMNI_PROTO_KCP = 2
|
||||
} OmniProtocol;
|
||||
|
||||
/* 角色:客户端 / 服务端 */
|
||||
typedef enum {
|
||||
OMNI_ROLE_CLIENT = 0,
|
||||
OMNI_ROLE_SERVER = 1
|
||||
} OmniRole;
|
||||
|
||||
/* 统一上下文句柄(对上层不透明) */
|
||||
typedef struct OmniContext OmniContext;
|
||||
|
||||
/* 协议实现函数表(由底层协议模块提供) */
|
||||
struct ProtoVTable {
|
||||
OmniContext *(*init)(OmniRole role,
|
||||
const char *bind_ip,
|
||||
uint16_t bind_port,
|
||||
const char *peer_ip,
|
||||
uint16_t peer_port);
|
||||
ssize_t (*send)(OmniContext *ctx, const void *buf, size_t len);
|
||||
ssize_t (*recv)(OmniContext *ctx, void *buf, size_t len);
|
||||
void (*close)(OmniContext *ctx);
|
||||
};
|
||||
|
||||
/*
|
||||
* 创建并初始化一个网络上下文。
|
||||
*
|
||||
* 参数:
|
||||
* role - 客户端或服务端
|
||||
* proto - 协议类型(TCP/UDP/KCP)
|
||||
* bind_ip - 服务器监听或客户端本地绑定 IP(可为 NULL 表示 INADDR_ANY)
|
||||
* bind_port - 监听端口或本地端口(0 表示让系统分配)
|
||||
* peer_ip - 对端 IP(客户端连接或服务端应答的默认地址,可为 NULL)
|
||||
* peer_port - 对端端口
|
||||
*
|
||||
* 返回:
|
||||
* 成功:上下文指针
|
||||
* 失败:NULL
|
||||
*/
|
||||
OmniContext *omni_init(OmniRole role,
|
||||
OmniProtocol proto,
|
||||
const char *bind_ip,
|
||||
uint16_t bind_port,
|
||||
const char *peer_ip,
|
||||
uint16_t peer_port);
|
||||
|
||||
/* 发送数据(阻塞直到全部发送或出错) */
|
||||
ssize_t omni_send(OmniContext *ctx, const void *buf, size_t len);
|
||||
|
||||
/* 接收数据(阻塞直到读到至少 1 字节或出错/关闭) */
|
||||
ssize_t omni_recv(OmniContext *ctx, void *buf, size_t len);
|
||||
|
||||
/* 关闭并释放上下文 */
|
||||
void omni_close(OmniContext *ctx);
|
||||
|
||||
#endif /* OMNISOCKET_NETWORK_H */
|
||||
|
||||
BIN
kcp_impl.o
Normal file
BIN
kcp_impl.o
Normal file
Binary file not shown.
118
src/core/logger.c
Normal file
118
src/core/logger.c
Normal file
@@ -0,0 +1,118 @@
|
||||
/*
|
||||
* logger.c
|
||||
* 性能统计与结构化日志实现
|
||||
*/
|
||||
|
||||
#include "logger.h"
|
||||
#include "common.h"
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdarg.h>
|
||||
#include <string.h>
|
||||
|
||||
static OmniStats g_stats;
|
||||
|
||||
static uint64_t now_ms(void)
|
||||
{
|
||||
return omni_now_ms();
|
||||
}
|
||||
|
||||
void logger_init(void)
|
||||
{
|
||||
memset(&g_stats, 0, sizeof(g_stats));
|
||||
g_stats.start_ms = now_ms();
|
||||
g_stats.last_report_ms = g_stats.start_ms;
|
||||
}
|
||||
|
||||
void logger_on_send(size_t bytes)
|
||||
{
|
||||
g_stats.bytes_sent += bytes;
|
||||
g_stats.send_count++;
|
||||
}
|
||||
|
||||
void logger_on_recv(size_t bytes)
|
||||
{
|
||||
g_stats.bytes_recv += bytes;
|
||||
g_stats.recv_count++;
|
||||
}
|
||||
|
||||
void logger_on_rtt(uint64_t rtt_ms)
|
||||
{
|
||||
g_stats.last_rtt_ms = rtt_ms;
|
||||
if (rtt_ms > g_stats.max_rtt_ms) {
|
||||
g_stats.max_rtt_ms = rtt_ms;
|
||||
}
|
||||
}
|
||||
|
||||
void logger_on_kcp_retrans(uint64_t delta)
|
||||
{
|
||||
g_stats.kcp_retrans += delta;
|
||||
}
|
||||
|
||||
double logger_calculate_throughput(void)
|
||||
{
|
||||
uint64_t now = now_ms();
|
||||
uint64_t elapsed_ms = now - g_stats.start_ms;
|
||||
if (elapsed_ms == 0) {
|
||||
return 0.0;
|
||||
}
|
||||
double seconds = (double)elapsed_ms / 1000.0;
|
||||
return (double)(g_stats.bytes_sent + g_stats.bytes_recv) / seconds;
|
||||
}
|
||||
|
||||
static void print_timestamp(FILE *fp)
|
||||
{
|
||||
uint64_t ms = now_ms();
|
||||
fprintf(fp, "ts=%llu ", (unsigned long long)ms);
|
||||
}
|
||||
|
||||
void logger_print_performance_log(const char *tag)
|
||||
{
|
||||
uint64_t now = now_ms();
|
||||
uint64_t elapsed_ms = now - g_stats.start_ms;
|
||||
double thr = logger_calculate_throughput();
|
||||
|
||||
FILE *fp = stderr;
|
||||
print_timestamp(fp);
|
||||
fprintf(fp,
|
||||
"level=INFO component=perf tag=%s "
|
||||
"elapsed_ms=%llu bytes_sent=%llu bytes_recv=%llu "
|
||||
"send_count=%llu recv_count=%llu "
|
||||
"throughput_bytes_per_sec=%.2f "
|
||||
"last_rtt_ms=%llu max_rtt_ms=%llu "
|
||||
"kcp_retrans=%llu\n",
|
||||
tag ? tag : "periodic",
|
||||
(unsigned long long)elapsed_ms,
|
||||
(unsigned long long)g_stats.bytes_sent,
|
||||
(unsigned long long)g_stats.bytes_recv,
|
||||
(unsigned long long)g_stats.send_count,
|
||||
(unsigned long long)g_stats.recv_count,
|
||||
thr,
|
||||
(unsigned long long)g_stats.last_rtt_ms,
|
||||
(unsigned long long)g_stats.max_rtt_ms,
|
||||
(unsigned long long)g_stats.kcp_retrans);
|
||||
|
||||
g_stats.last_report_ms = now;
|
||||
}
|
||||
|
||||
void logger_log(const char *level, const char *component,
|
||||
const char *fmt, ...)
|
||||
{
|
||||
FILE *fp = stderr;
|
||||
print_timestamp(fp);
|
||||
fprintf(fp, "level=%s component=%s ", level ? level : "INFO",
|
||||
component ? component : "general");
|
||||
|
||||
va_list ap;
|
||||
va_start(ap, fmt);
|
||||
vfprintf(fp, fmt, ap);
|
||||
va_end(ap);
|
||||
|
||||
fputc('\n', fp);
|
||||
}
|
||||
|
||||
OmniStats logger_get_snapshot(void)
|
||||
{
|
||||
return g_stats;
|
||||
}
|
||||
|
||||
123
src/core/network.c
Normal file
123
src/core/network.c
Normal file
@@ -0,0 +1,123 @@
|
||||
/*
|
||||
* network.c
|
||||
* 协议分发与工厂模式实现
|
||||
*/
|
||||
|
||||
#include "network.h"
|
||||
#include "common.h"
|
||||
#include "logger.h"
|
||||
|
||||
#include <stdlib.h>
|
||||
|
||||
/* TCP / UDP / KCP 在各自的实现文件中提供以下符号 */
|
||||
extern const struct ProtoVTable TCP_PROTO_VTABLE;
|
||||
extern const struct ProtoVTable UDP_PROTO_VTABLE;
|
||||
extern const struct ProtoVTable KCP_PROTO_VTABLE;
|
||||
|
||||
struct OmniContext {
|
||||
OmniProtocol proto;
|
||||
OmniRole role;
|
||||
const struct ProtoVTable *vt;
|
||||
void *impl; /* 由具体协议实现使用的内部指针 */
|
||||
};
|
||||
|
||||
/* 工厂:根据协议类型选择对应实现 */
|
||||
static const struct ProtoVTable *select_vtable(OmniProtocol proto)
|
||||
{
|
||||
switch (proto) {
|
||||
case OMNI_PROTO_TCP:
|
||||
return &TCP_PROTO_VTABLE;
|
||||
case OMNI_PROTO_UDP:
|
||||
return &UDP_PROTO_VTABLE;
|
||||
case OMNI_PROTO_KCP:
|
||||
return &KCP_PROTO_VTABLE;
|
||||
default:
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
|
||||
OmniContext *omni_init(OmniRole role,
|
||||
OmniProtocol proto,
|
||||
const char *bind_ip,
|
||||
uint16_t bind_port,
|
||||
const char *peer_ip,
|
||||
uint16_t peer_port)
|
||||
{
|
||||
logger_init();
|
||||
|
||||
const struct ProtoVTable *vt = select_vtable(proto);
|
||||
if (!vt || !vt->init) {
|
||||
logger_log("ERROR", "network", "select_vtable_failed proto=%d",
|
||||
(int)proto);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
OmniContext *ctx = (OmniContext *)calloc(1, sizeof(OmniContext));
|
||||
if (!ctx) {
|
||||
logger_log("ERROR", "network", "calloc_OmniContext_failed");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
ctx->proto = proto;
|
||||
ctx->role = role;
|
||||
ctx->vt = vt;
|
||||
|
||||
OmniContext *impl_ctx = vt->init(role, bind_ip, bind_port, peer_ip, peer_port);
|
||||
if (!impl_ctx) {
|
||||
logger_log("ERROR", "network", "proto_init_failed proto=%d", (int)proto);
|
||||
free(ctx);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/* 简化:直接让 impl 指针等于协议实现返回的上下文 */
|
||||
ctx->impl = impl_ctx;
|
||||
|
||||
logger_log("INFO", "network",
|
||||
"omni_init_success proto=%d role=%d bind_port=%u peer_port=%u",
|
||||
(int)proto, (int)role,
|
||||
(unsigned)bind_port,
|
||||
(unsigned)peer_port);
|
||||
|
||||
return ctx;
|
||||
}
|
||||
|
||||
ssize_t omni_send(OmniContext *ctx, const void *buf, size_t len)
|
||||
{
|
||||
if (!ctx || !ctx->vt || !ctx->vt->send) {
|
||||
return OMNI_ERR_PARAM;
|
||||
}
|
||||
|
||||
ssize_t n = ctx->vt->send((OmniContext *)ctx->impl, buf, len);
|
||||
if (n > 0) {
|
||||
logger_on_send((size_t)n);
|
||||
}
|
||||
logger_log("DEBUG", "network", "omni_send proto=%d bytes=%zd",
|
||||
(int)ctx->proto, n);
|
||||
return n;
|
||||
}
|
||||
|
||||
ssize_t omni_recv(OmniContext *ctx, void *buf, size_t len)
|
||||
{
|
||||
if (!ctx || !ctx->vt || !ctx->vt->recv) {
|
||||
return OMNI_ERR_PARAM;
|
||||
}
|
||||
|
||||
ssize_t n = ctx->vt->recv((OmniContext *)ctx->impl, buf, len);
|
||||
if (n > 0) {
|
||||
logger_on_recv((size_t)n);
|
||||
}
|
||||
logger_log("DEBUG", "network", "omni_recv proto=%d bytes=%zd",
|
||||
(int)ctx->proto, n);
|
||||
return n;
|
||||
}
|
||||
|
||||
void omni_close(OmniContext *ctx)
|
||||
{
|
||||
if (!ctx) return;
|
||||
if (ctx->vt && ctx->vt->close && ctx->impl) {
|
||||
ctx->vt->close((OmniContext *)ctx->impl);
|
||||
}
|
||||
logger_print_performance_log("final");
|
||||
free(ctx);
|
||||
}
|
||||
|
||||
1306
src/protocols/ikcp.c
Normal file
1306
src/protocols/ikcp.c
Normal file
File diff suppressed because it is too large
Load Diff
171
src/protocols/kcp_impl.c
Normal file
171
src/protocols/kcp_impl.c
Normal file
@@ -0,0 +1,171 @@
|
||||
/*
|
||||
* kcp_impl.c
|
||||
* 基于 UDP 的 KCP 可靠传输实现
|
||||
*/
|
||||
|
||||
#include "common.h"
|
||||
#include "network.h"
|
||||
#include "logger.h"
|
||||
#include "kcp/ikcp.h"
|
||||
|
||||
#include <arpa/inet.h>
|
||||
#include <errno.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <sys/socket.h>
|
||||
#include <sys/types.h>
|
||||
#include <unistd.h>
|
||||
|
||||
struct KcpContext {
|
||||
int fd;
|
||||
struct sockaddr_in peer_addr;
|
||||
socklen_t peer_len;
|
||||
ikcpcb *kcp;
|
||||
};
|
||||
|
||||
static int kcp_output(const char *buf, int len, ikcpcb *kcp, void *user)
|
||||
{
|
||||
(void)kcp;
|
||||
struct KcpContext *ctx = (struct KcpContext *)user;
|
||||
ssize_t n = sendto(ctx->fd, buf, (size_t)len, 0,
|
||||
(struct sockaddr *)&ctx->peer_addr, ctx->peer_len);
|
||||
if (n < 0) {
|
||||
logger_log("ERROR", "kcp", "sendto_failed errno=%d", errno);
|
||||
return OMNI_ERR_IO;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static OmniContext *kcp_init(OmniRole role,
|
||||
const char *bind_ip,
|
||||
uint16_t bind_port,
|
||||
const char *peer_ip,
|
||||
uint16_t peer_port)
|
||||
{
|
||||
(void)role;
|
||||
|
||||
struct KcpContext *ctx = (struct KcpContext *)calloc(1, sizeof(*ctx));
|
||||
if (!ctx) return NULL;
|
||||
|
||||
int fd = socket(AF_INET, SOCK_DGRAM, 0);
|
||||
if (fd < 0) {
|
||||
logger_log("ERROR", "kcp", "socket_failed errno=%d", errno);
|
||||
free(ctx);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if (bind_port != 0) {
|
||||
struct sockaddr_in addr;
|
||||
memset(&addr, 0, sizeof(addr));
|
||||
addr.sin_family = AF_INET;
|
||||
addr.sin_port = htons(bind_port);
|
||||
addr.sin_addr.s_addr = bind_ip ? inet_addr(bind_ip) : INADDR_ANY;
|
||||
if (bind(fd, (struct sockaddr *)&addr, sizeof(addr)) < 0) {
|
||||
logger_log("ERROR", "kcp", "bind_failed errno=%d", errno);
|
||||
close(fd);
|
||||
free(ctx);
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
|
||||
memset(&ctx->peer_addr, 0, sizeof(ctx->peer_addr));
|
||||
ctx->peer_addr.sin_family = AF_INET;
|
||||
ctx->peer_addr.sin_port = htons(peer_port);
|
||||
ctx->peer_addr.sin_addr.s_addr = peer_ip ? inet_addr(peer_ip) : INADDR_ANY;
|
||||
ctx->peer_len = sizeof(ctx->peer_addr);
|
||||
|
||||
ctx->fd = fd;
|
||||
|
||||
/* conv 可简单使用端口号 */
|
||||
IUINT32 conv = (IUINT32)peer_port;
|
||||
ikcpcb *kcp = ikcp_create(conv, ctx);
|
||||
if (!kcp) {
|
||||
logger_log("ERROR", "kcp", "ikcp_create_failed");
|
||||
close(fd);
|
||||
free(ctx);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
ctx->kcp = kcp;
|
||||
|
||||
ikcp_setoutput(kcp, kcp_output);
|
||||
ikcp_nodelay(kcp, 1, 10, 2, 1);
|
||||
ikcp_wndsize(kcp, 128, 128);
|
||||
|
||||
logger_log("INFO", "kcp",
|
||||
"init bind_port=%u peer_ip=%s peer_port=%u",
|
||||
(unsigned)bind_port,
|
||||
peer_ip ? peer_ip : "NULL",
|
||||
(unsigned)peer_port);
|
||||
|
||||
return (OmniContext *)ctx;
|
||||
}
|
||||
|
||||
static void kcp_update_loop(struct KcpContext *ctx)
|
||||
{
|
||||
IUINT32 current = (IUINT32)omni_now_ms();
|
||||
ikcp_update(ctx->kcp, current);
|
||||
|
||||
char buf[1500];
|
||||
struct sockaddr_in from;
|
||||
socklen_t fromlen = sizeof(from);
|
||||
ssize_t n = recvfrom(ctx->fd, buf, sizeof(buf), MSG_DONTWAIT,
|
||||
(struct sockaddr *)&from, &fromlen);
|
||||
if (n > 0) {
|
||||
ctx->peer_addr = from;
|
||||
ctx->peer_len = fromlen;
|
||||
ikcp_input(ctx->kcp, buf, (long)n);
|
||||
}
|
||||
}
|
||||
|
||||
static ssize_t kcp_send(OmniContext *c, const void *buf, size_t len)
|
||||
{
|
||||
struct KcpContext *ctx = (struct KcpContext *)c;
|
||||
if (!ctx || !ctx->kcp) return OMNI_ERR_PARAM;
|
||||
|
||||
int rc = ikcp_send(ctx->kcp, (const char *)buf, (int)len);
|
||||
if (rc < 0) {
|
||||
logger_log("ERROR", "kcp", "ikcp_send_failed rc=%d", rc);
|
||||
return OMNI_ERR_IO;
|
||||
}
|
||||
|
||||
/* 驱动一次 flush */
|
||||
kcp_update_loop(ctx);
|
||||
return (ssize_t)len;
|
||||
}
|
||||
|
||||
static ssize_t kcp_recv(OmniContext *c, void *buf, size_t len)
|
||||
{
|
||||
struct KcpContext *ctx = (struct KcpContext *)c;
|
||||
if (!ctx || !ctx->kcp) return OMNI_ERR_PARAM;
|
||||
|
||||
kcp_update_loop(ctx);
|
||||
|
||||
int n = ikcp_recv(ctx->kcp, (char *)buf, (int)len);
|
||||
if (n < 0) {
|
||||
return 0; /* 暂无数据 */
|
||||
}
|
||||
return (ssize_t)n;
|
||||
}
|
||||
|
||||
static void kcp_close(OmniContext *c)
|
||||
{
|
||||
struct KcpContext *ctx = (struct KcpContext *)c;
|
||||
if (!ctx) return;
|
||||
if (ctx->kcp) {
|
||||
ikcp_release(ctx->kcp);
|
||||
}
|
||||
if (ctx->fd >= 0) {
|
||||
close(ctx->fd);
|
||||
}
|
||||
free(ctx);
|
||||
}
|
||||
|
||||
const struct ProtoVTable KCP_PROTO_VTABLE = {
|
||||
.init = kcp_init,
|
||||
.send = kcp_send,
|
||||
.recv = kcp_recv,
|
||||
.close = kcp_close,
|
||||
};
|
||||
|
||||
255
src/protocols/tcp_impl.c
Normal file
255
src/protocols/tcp_impl.c
Normal file
@@ -0,0 +1,255 @@
|
||||
/*
|
||||
* tcp_impl.c
|
||||
* TCP 协议实现,带 16 字节包头解决粘包
|
||||
*/
|
||||
|
||||
#include "common.h"
|
||||
#include "network.h"
|
||||
#include "logger.h"
|
||||
|
||||
#include <arpa/inet.h>
|
||||
#include <errno.h>
|
||||
#include <netinet/tcp.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <sys/socket.h>
|
||||
#include <sys/types.h>
|
||||
#include <unistd.h>
|
||||
|
||||
struct TcpContext {
|
||||
int fd;
|
||||
};
|
||||
|
||||
static int tcp_set_nodelay(int fd)
|
||||
{
|
||||
int flag = 1;
|
||||
return setsockopt(fd, IPPROTO_TCP, TCP_NODELAY, &flag, sizeof(flag));
|
||||
}
|
||||
|
||||
static int tcp_set_reuseaddr(int fd)
|
||||
{
|
||||
int flag = 1;
|
||||
return setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, &flag, sizeof(flag));
|
||||
}
|
||||
|
||||
static int tcp_bind_and_listen(struct TcpContext *ctx,
|
||||
const char *bind_ip,
|
||||
uint16_t bind_port)
|
||||
{
|
||||
int fd = socket(AF_INET, SOCK_STREAM, 0);
|
||||
if (fd < 0) {
|
||||
logger_log("ERROR", "tcp", "socket_failed errno=%d", errno);
|
||||
return -1;
|
||||
}
|
||||
|
||||
tcp_set_reuseaddr(fd);
|
||||
|
||||
struct sockaddr_in addr;
|
||||
memset(&addr, 0, sizeof(addr));
|
||||
addr.sin_family = AF_INET;
|
||||
addr.sin_port = htons(bind_port);
|
||||
addr.sin_addr.s_addr = bind_ip ? inet_addr(bind_ip) : INADDR_ANY;
|
||||
|
||||
if (bind(fd, (struct sockaddr *)&addr, sizeof(addr)) < 0) {
|
||||
logger_log("ERROR", "tcp", "bind_failed errno=%d", errno);
|
||||
close(fd);
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (listen(fd, 1) < 0) {
|
||||
logger_log("ERROR", "tcp", "listen_failed errno=%d", errno);
|
||||
close(fd);
|
||||
return -1;
|
||||
}
|
||||
|
||||
logger_log("INFO", "tcp", "listening port=%u", (unsigned)bind_port);
|
||||
|
||||
/* 简化:阻塞接受一个客户端,之后用于长连接 */
|
||||
int cfd = accept(fd, NULL, NULL);
|
||||
if (cfd < 0) {
|
||||
logger_log("ERROR", "tcp", "accept_failed errno=%d", errno);
|
||||
close(fd);
|
||||
return -1;
|
||||
}
|
||||
|
||||
close(fd);
|
||||
tcp_set_nodelay(cfd);
|
||||
|
||||
ctx->fd = cfd;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int tcp_connect_peer(struct TcpContext *ctx,
|
||||
const char *peer_ip,
|
||||
uint16_t peer_port)
|
||||
{
|
||||
int fd = socket(AF_INET, SOCK_STREAM, 0);
|
||||
if (fd < 0) {
|
||||
logger_log("ERROR", "tcp", "socket_failed errno=%d", errno);
|
||||
return -1;
|
||||
}
|
||||
|
||||
struct sockaddr_in addr;
|
||||
memset(&addr, 0, sizeof(addr));
|
||||
addr.sin_family = AF_INET;
|
||||
addr.sin_port = htons(peer_port);
|
||||
addr.sin_addr.s_addr = inet_addr(peer_ip);
|
||||
|
||||
if (connect(fd, (struct sockaddr *)&addr, sizeof(addr)) < 0) {
|
||||
logger_log("ERROR", "tcp", "connect_failed errno=%d", errno);
|
||||
close(fd);
|
||||
return -1;
|
||||
}
|
||||
|
||||
tcp_set_nodelay(fd);
|
||||
ctx->fd = fd;
|
||||
logger_log("INFO", "tcp", "connected peer_ip=%s peer_port=%u",
|
||||
peer_ip, (unsigned)peer_port);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static ssize_t tcp_read_n(int fd, void *buf, size_t n)
|
||||
{
|
||||
size_t off = 0;
|
||||
char *p = (char *)buf;
|
||||
while (off < n) {
|
||||
ssize_t r = read(fd, p + off, n - off);
|
||||
if (r < 0) {
|
||||
if (errno == EINTR) continue;
|
||||
return -1;
|
||||
}
|
||||
if (r == 0) {
|
||||
return off; /* 对端关闭 */
|
||||
}
|
||||
off += (size_t)r;
|
||||
}
|
||||
return (ssize_t)off;
|
||||
}
|
||||
|
||||
static ssize_t tcp_write_n(int fd, const void *buf, size_t n)
|
||||
{
|
||||
size_t off = 0;
|
||||
const char *p = (const char *)buf;
|
||||
while (off < n) {
|
||||
ssize_t r = write(fd, p + off, n - off);
|
||||
if (r < 0) {
|
||||
if (errno == EINTR) continue;
|
||||
return -1;
|
||||
}
|
||||
off += (size_t)r;
|
||||
}
|
||||
return (ssize_t)off;
|
||||
}
|
||||
|
||||
static OmniContext *tcp_init(OmniRole role,
|
||||
const char *bind_ip,
|
||||
uint16_t bind_port,
|
||||
const char *peer_ip,
|
||||
uint16_t peer_port)
|
||||
{
|
||||
struct TcpContext *ctx = (struct TcpContext *)calloc(1, sizeof(*ctx));
|
||||
if (!ctx) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
int rc;
|
||||
if (role == OMNI_ROLE_SERVER) {
|
||||
rc = tcp_bind_and_listen(ctx, bind_ip, bind_port);
|
||||
} else {
|
||||
if (!peer_ip || peer_port == 0) {
|
||||
logger_log("ERROR", "tcp", "client_requires_peer_ip_port");
|
||||
free(ctx);
|
||||
return NULL;
|
||||
}
|
||||
rc = tcp_connect_peer(ctx, peer_ip, peer_port);
|
||||
}
|
||||
|
||||
if (rc != 0) {
|
||||
free(ctx);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
return (OmniContext *)ctx;
|
||||
}
|
||||
|
||||
static ssize_t tcp_send(OmniContext *c, const void *buf, size_t len)
|
||||
{
|
||||
struct TcpContext *ctx = (struct TcpContext *)c;
|
||||
if (!ctx || ctx->fd < 0) return OMNI_ERR_PARAM;
|
||||
|
||||
MsgHeader hdr;
|
||||
hdr.magic = htonl(MSG_MAGIC);
|
||||
hdr.length = htonl((uint32_t)len);
|
||||
hdr.seq = 0; /* 如有需要,上层可扩展维护序列号 */
|
||||
|
||||
uint8_t header_buf[MSG_HEADER_SIZE];
|
||||
memcpy(header_buf, &hdr, MSG_HEADER_SIZE);
|
||||
|
||||
ssize_t n1 = tcp_write_n(ctx->fd, header_buf, MSG_HEADER_SIZE);
|
||||
if (n1 != (ssize_t)MSG_HEADER_SIZE) {
|
||||
return OMNI_ERR_IO;
|
||||
}
|
||||
|
||||
ssize_t n2 = tcp_write_n(ctx->fd, buf, len);
|
||||
if (n2 != (ssize_t)len) {
|
||||
return OMNI_ERR_IO;
|
||||
}
|
||||
|
||||
return (ssize_t)len;
|
||||
}
|
||||
|
||||
static ssize_t tcp_recv(OmniContext *c, void *buf, size_t len)
|
||||
{
|
||||
struct TcpContext *ctx = (struct TcpContext *)c;
|
||||
if (!ctx || ctx->fd < 0) return OMNI_ERR_PARAM;
|
||||
|
||||
uint8_t header_buf[MSG_HEADER_SIZE];
|
||||
ssize_t n1 = tcp_read_n(ctx->fd, header_buf, MSG_HEADER_SIZE);
|
||||
if (n1 <= 0) {
|
||||
return n1; /* 0 表示对端关闭,负数为错误 */
|
||||
}
|
||||
if (n1 != (ssize_t)MSG_HEADER_SIZE) {
|
||||
return OMNI_ERR_IO;
|
||||
}
|
||||
|
||||
MsgHeader hdr;
|
||||
memcpy(&hdr, header_buf, MSG_HEADER_SIZE);
|
||||
if (ntohl(hdr.magic) != MSG_MAGIC) {
|
||||
logger_log("ERROR", "tcp", "invalid_magic");
|
||||
return OMNI_ERR_IO;
|
||||
}
|
||||
|
||||
uint32_t payload_len = ntohl(hdr.length);
|
||||
if (payload_len > len) {
|
||||
logger_log("ERROR", "tcp", "buffer_too_small payload=%u buf_len=%zu",
|
||||
payload_len, len);
|
||||
/* 简化:这里返回错误,实际可考虑丢弃或扩展缓冲区 */
|
||||
return OMNI_ERR_PARAM;
|
||||
}
|
||||
|
||||
ssize_t n2 = tcp_read_n(ctx->fd, buf, payload_len);
|
||||
if (n2 != (ssize_t)payload_len) {
|
||||
return OMNI_ERR_IO;
|
||||
}
|
||||
|
||||
return (ssize_t)payload_len;
|
||||
}
|
||||
|
||||
static void tcp_close(OmniContext *c)
|
||||
{
|
||||
struct TcpContext *ctx = (struct TcpContext *)c;
|
||||
if (!ctx) return;
|
||||
if (ctx->fd >= 0) {
|
||||
close(ctx->fd);
|
||||
}
|
||||
free(ctx);
|
||||
}
|
||||
|
||||
const struct ProtoVTable TCP_PROTO_VTABLE = {
|
||||
.init = tcp_init,
|
||||
.send = tcp_send,
|
||||
.recv = tcp_recv,
|
||||
.close = tcp_close,
|
||||
};
|
||||
|
||||
126
src/protocols/udp_impl.c
Normal file
126
src/protocols/udp_impl.c
Normal file
@@ -0,0 +1,126 @@
|
||||
/*
|
||||
* udp_impl.c
|
||||
* UDP 协议实现(无连接,基础 sendto/recvfrom)
|
||||
*/
|
||||
|
||||
#include "common.h"
|
||||
#include "network.h"
|
||||
#include "logger.h"
|
||||
|
||||
#include <arpa/inet.h>
|
||||
#include <errno.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <sys/socket.h>
|
||||
#include <sys/types.h>
|
||||
#include <unistd.h>
|
||||
|
||||
struct UdpContext {
|
||||
int fd;
|
||||
struct sockaddr_in peer_addr;
|
||||
socklen_t peer_len;
|
||||
};
|
||||
|
||||
static OmniContext *udp_init(OmniRole role,
|
||||
const char *bind_ip,
|
||||
uint16_t bind_port,
|
||||
const char *peer_ip,
|
||||
uint16_t peer_port)
|
||||
{
|
||||
(void)role;
|
||||
|
||||
struct UdpContext *ctx = (struct UdpContext *)calloc(1, sizeof(*ctx));
|
||||
if (!ctx) return NULL;
|
||||
|
||||
int fd = socket(AF_INET, SOCK_DGRAM, 0);
|
||||
if (fd < 0) {
|
||||
logger_log("ERROR", "udp", "socket_failed errno=%d", errno);
|
||||
free(ctx);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if (bind_port != 0) {
|
||||
struct sockaddr_in addr;
|
||||
memset(&addr, 0, sizeof(addr));
|
||||
addr.sin_family = AF_INET;
|
||||
addr.sin_port = htons(bind_port);
|
||||
addr.sin_addr.s_addr = bind_ip ? inet_addr(bind_ip) : INADDR_ANY;
|
||||
if (bind(fd, (struct sockaddr *)&addr, sizeof(addr)) < 0) {
|
||||
logger_log("ERROR", "udp", "bind_failed errno=%d", errno);
|
||||
close(fd);
|
||||
free(ctx);
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
|
||||
memset(&ctx->peer_addr, 0, sizeof(ctx->peer_addr));
|
||||
ctx->peer_addr.sin_family = AF_INET;
|
||||
ctx->peer_addr.sin_port = htons(peer_port);
|
||||
ctx->peer_addr.sin_addr.s_addr = peer_ip ? inet_addr(peer_ip) : INADDR_ANY;
|
||||
ctx->peer_len = sizeof(ctx->peer_addr);
|
||||
|
||||
ctx->fd = fd;
|
||||
|
||||
logger_log("INFO", "udp",
|
||||
"init bind_port=%u peer_ip=%s peer_port=%u",
|
||||
(unsigned)bind_port,
|
||||
peer_ip ? peer_ip : "NULL",
|
||||
(unsigned)peer_port);
|
||||
|
||||
return (OmniContext *)ctx;
|
||||
}
|
||||
|
||||
static ssize_t udp_send(OmniContext *c, const void *buf, size_t len)
|
||||
{
|
||||
struct UdpContext *ctx = (struct UdpContext *)c;
|
||||
if (!ctx || ctx->fd < 0) return OMNI_ERR_PARAM;
|
||||
|
||||
ssize_t n = sendto(ctx->fd, buf, len, 0,
|
||||
(struct sockaddr *)&ctx->peer_addr, ctx->peer_len);
|
||||
if (n < 0) {
|
||||
logger_log("ERROR", "udp", "sendto_failed errno=%d", errno);
|
||||
return OMNI_ERR_IO;
|
||||
}
|
||||
return n;
|
||||
}
|
||||
|
||||
static ssize_t udp_recv(OmniContext *c, void *buf, size_t len)
|
||||
{
|
||||
struct UdpContext *ctx = (struct UdpContext *)c;
|
||||
if (!ctx || ctx->fd < 0) return OMNI_ERR_PARAM;
|
||||
|
||||
struct sockaddr_in from;
|
||||
socklen_t fromlen = sizeof(from);
|
||||
ssize_t n = recvfrom(ctx->fd, buf, len, 0,
|
||||
(struct sockaddr *)&from, &fromlen);
|
||||
if (n < 0) {
|
||||
if (errno == EINTR) return 0;
|
||||
logger_log("ERROR", "udp", "recvfrom_failed errno=%d", errno);
|
||||
return OMNI_ERR_IO;
|
||||
}
|
||||
|
||||
/* 默认更新 peer 为最近一次通信对端,便于“伪长连接” */
|
||||
ctx->peer_addr = from;
|
||||
ctx->peer_len = fromlen;
|
||||
|
||||
return n;
|
||||
}
|
||||
|
||||
static void udp_close(OmniContext *c)
|
||||
{
|
||||
struct UdpContext *ctx = (struct UdpContext *)c;
|
||||
if (!ctx) return;
|
||||
if (ctx->fd >= 0) {
|
||||
close(ctx->fd);
|
||||
}
|
||||
free(ctx);
|
||||
}
|
||||
|
||||
const struct ProtoVTable UDP_PROTO_VTABLE = {
|
||||
.init = udp_init,
|
||||
.send = udp_send,
|
||||
.recv = udp_recv,
|
||||
.close = udp_close,
|
||||
};
|
||||
|
||||
BIN
tcp_impl.o
Normal file
BIN
tcp_impl.o
Normal file
Binary file not shown.
BIN
udp_impl.o
Normal file
BIN
udp_impl.o
Normal file
Binary file not shown.
Reference in New Issue
Block a user