feat: 初始化 OmniSocket 目录结构与底层网络接口
This commit is contained in:
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,
|
||||
};
|
||||
|
||||
Reference in New Issue
Block a user