Files
STM32F4-Base/IEC60870-Slave/104/IEC104_Net.h
T
STF 4c55544f05
CI / L0 静态门禁 (pre-commit) (push) Failing after 45s
CI / L0.5 主机侧单测 (MSVC) (push) Failing after 42s
通信协议代码
2026-10-26 08:31:48 -04:00

203 lines
13 KiB
C
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
/*
* TCP平台接口:只负责同步非阻塞I/O、就绪等待和单调计时,不依赖104协议模块。
* 工程须链接且只链接一个实现;当前提供IEC104_Net_Windows.c。
* 不创建线程、不保存业务回调,也不决定协议故障、EOF排空或重连保留策略。
*
* 职责边界:TcpServer决定何时接入、读写和关闭,Net负责执行一次平台操作。
* 典型生命周期:Init -> Listen -> Wait/Accept/Recv/Send -> Close各句柄 -> Cleanup。
* 调用方持有监听和客户端句柄;本层不会为其保存连接对象或业务状态。
*
* 通用约定:native_error输出均允许NULL;成功时写0,失败时保留平台诊断码。
* 上层应按result调度,native_error只用于诊断,不应依赖某个平台的错误常量。
* 所有缓冲区均只在调用期间使用,函数返回后本层不再持有调用方的内存。
*/
#ifndef IEC104_NET_H
#define IEC104_NET_H
#include <stddef.h>
#include <stdint.h>
#ifdef __cplusplus
extern "C" {
#endif
/* TCP内部拥有的数值句柄;0可以是有效句柄。
* TcpServer可按其公开契约借给应用事件循环注册,应用不得接管I/O、关闭或修改句柄属性。
* 使用整数类型承载平台句柄,使公共头文件无需包含SOCKET等系统类型。
* 是否无效只能与IEC104_NET_INVALID_SOCKET比较,不能用if (!handle)判断。 */
typedef uintptr_t IEC104_NetSocket_t;
#define IEC104_NET_INVALID_SOCKET ((IEC104_NetSocket_t)UINTPTR_MAX)
/* 必须按枚举值判断:OK为0,正数不统一表示成功或有数据。
* WOULD_BLOCK表示当前没有进度,通常等待下一次就绪;RETRY表示本次操作可重试。
* RETRY_LATER仅用于接入资源暂缺,须由调用方暂停接入并安排定时重试。
* 重试次数、收发预算及超时期限由调用方限制,不能在后端无限循环。 */
typedef enum {
IEC104_NET_ERROR = -1, /* 最终失败,native_error保存平台错误码。 */
IEC104_NET_OK = 0, /* 操作完成;Wait的就绪标志可为0,表示未报告socket就绪。 */
IEC104_NET_WOULD_BLOCK, /* 暂不可接受连接/读写,保留原有状态后续再试。 */
IEC104_NET_RETRY, /* 本次被中断或单个待接入客户端失效,可有界重试。 */
IEC104_NET_EOF, /* 仅Recv使用:对端发送方向有序关闭。 */
IEC104_NET_RETRY_LATER /* 仅Accept使用:资源暂缺,不能立即循环重试。 */
} IEC104_NetResult_t;
/* 一次收发的完整结果;把状态、字节数和平台错误分开,避免把EOF当成暂不可读。 */
typedef struct {
IEC104_NetResult_t result; /* 调度依据;读取transferred前先判断此字段。 */
size_t transferred; /* OK时为本次实际读写字节数,其余结果恒为0。 */
int native_error; /* 平台诊断码,OK/EOF为0;上层按result决定调度。 */
} IEC104_NetIoResult_t;
/* Accept交付的对端地址副本,平台sockaddr和网络字节序不越过Net边界。
* 当前监听仅支持IPv4;address不包含端口,port使用主机字节序。 */
typedef struct {
char address[16]; /* NUL结尾数字IPv4,最长15字符。 */
uint16_t port; /* 客户端源端口,并非本地监听端口。 */
} IEC104_NetPeer_t;
/* Wait的关注位和就绪位使用同一组掩码,可以按位或组合。
* 就绪只表示可以尝试操作,最终状态仍由Accept/Recv/Send的返回值确定。 */
#define IEC104_NET_EVENT_ACCEPT 1U /* 监听句柄可尝试接入客户端。 */
#define IEC104_NET_EVENT_READ 2U /* 客户端可尝试接收,也可能读到EOF或错误。 */
#define IEC104_NET_EVENT_WRITE 4U /* 客户端可尝试发送,不保证整段数据一次写完。 */
/**
* 功能:取得本次网络使用所需的平台初始化资源。
* 输出参数:native_error可为NULL;成功写0,失败写平台错误码。
* 返回值:OK表示初始化成功;ERROR表示失败,此时无需为本次调用Cleanup。
* 说明:在创建socket前调用;每次成功Init均须配对一次Cleanup。
* 重复调用并非幂等操作,不能把多次成功Init只配对一次Cleanup。
*/
extern IEC104_NetResult_t IEC104_Net_Init(int *native_error);
/**
* 功能:释放一次成功Init取得的平台初始化资源。
* 说明:调用前先关闭本次持有的全部socket;本函数不逐一管理调用方的句柄。
* 无返回值,也不负责清空TcpServer中的连接状态。
*/
extern void IEC104_Net_Cleanup(void);
/**
* 功能:取得平台对应的“参数无效”诊断码,不执行网络操作。
* 返回值:当前后端的参数错误码,用于上层自身参数校验失败时填写诊断信息。
* 说明:避免TcpServer为记录错误而包含Windows等平台的系统错误常量。
*/
extern int IEC104_Net_Invalid_Argument_Error(void);
/**
* 功能:创建、绑定并启动一个非阻塞IPv4 TCP监听socket。
* 输入参数:bind_address为有效的NUL结尾数字IPv4字符串,不进行域名解析。
* port使用主机字节序;为0时由系统分配可用端口。
* 输出参数:listener、bound_port必填,分别接收新监听句柄和实际端口。
* bound_port同样使用主机字节序;native_error可为NULL。
* 各输出存储区不得重叠,也不得覆盖输入字符串。
* 返回值:OK时将新句柄的所有权交给调用方;ERROR时回收本次创建的socket,
* 并将有效输出指针指向的listener/bound_port置为INVALID_SOCKET/0。
* 说明:只用于创建新句柄,不会关闭listener原来保存的句柄;先处理旧资源再调用。
* 失败不撤销此前成功的Init,调用方仍须按生命周期执行Cleanup。
*/
extern IEC104_NetResult_t IEC104_Net_Listen(
const char *bind_address, uint16_t port, IEC104_NetSocket_t *listener,
uint16_t *bound_port, int *native_error);
/**
* 功能:从非阻塞监听socket接入至多一个客户端,不在内部循环accept。
* 输入参数:listener为Listen成功交付的监听句柄。
* 输出参数:client、peer必填且不得重叠,OK时交付非阻塞且启用TCP_NODELAY的客户端及其IPv4地址/源端口;
* native_error可为NULL。
* 返回值:OK表示接入成功,调用方负责关闭客户端;WOULD_BLOCK表示暂无待接入连接;
* RETRY表示本次被中断、待接入客户端失效、非阻塞/TCP_NODELAY设置或地址转换失败;
* RETRY_LATER表示接入资源暂缺,调用方应暂停关注监听就绪并定时重试;
* ERROR表示参数无效或其他失败。非OK时有效client输出为INVALID_SOCKET,peer保持原值。
* 说明:新客户端设置或地址转换失败时,先关闭它再返回RETRY(资源暂缺为RETRY_LATER),
* 监听句柄仍由调用方持有,后端本身不等待。
* client只接收新句柄,不会自动关闭其中的旧句柄;不得覆盖仍在使用的连接。
* 是否接受额外客户端及重试多少次,由TcpServer的连接策略和预算决定。
*/
extern IEC104_NetResult_t IEC104_Net_Accept(
IEC104_NetSocket_t listener, IEC104_NetSocket_t *client, IEC104_NetPeer_t *peer, int *native_error);
/**
* 功能:对已接入的非阻塞客户端执行至多一次同步recv。
* 输入参数:socket_handle为有效客户端句柄;data非NULL,指向可写缓冲区;
* capacity为缓冲区容量,必须大于0,当前Windows后端还要求不大于INT_MAX。
* 输出参数:OK时将transferred个字节写入data,不追加字符串结束符。
* 返回值:OK表示收到数据;EOF表示对端发送方向有序关闭;WOULD_BLOCK表示暂不可读;
* RETRY表示本次操作被中断;ERROR表示参数或socket错误。
* 非OK时transferred为0;OK/EOF时native_error为0,其余状态保留诊断码。
* 说明:TCP是字节流,一次接收不对应一个完整104报文;拆帧由上层负责。
* EOF不会自动关闭句柄,调用方仍可排空待发送数据;函数返回后不再借用data。
* WOULD_BLOCK/RETRY不代表数据进度,不能据此重新计算或延长原超时期限。
*/
extern IEC104_NetIoResult_t IEC104_Net_Recv(
IEC104_NetSocket_t socket_handle, void *data, size_t capacity);
/* 只读取得当前内核接收队列的字节数,不等待、不消费输入、不包含后来到达的字节。
* 用于给跨调度轮次的续收设定有限边界;不是完整APDU数,也不表示连接仍存活。
* available必填,仅OK时写入;失败返回ERROR并保留平台诊断,由上层结束连接。 */
extern IEC104_NetResult_t IEC104_Net_Pending_Input(
IEC104_NetSocket_t socket_handle, uint32_t *available, int *native_error);
/**
* 功能:对已接入的非阻塞客户端执行至多一次同步send。
* 输入参数:socket_handle为有效客户端句柄;data非NULL,指向待发送数据;
* length为待发送长度,必须大于0,当前Windows后端还要求不大于INT_MAX。
* 返回值:OK时transferred表示本次被本机网络栈接受的字节数,可能小于length或为0;
* WOULD_BLOCK表示暂不可写;RETRY表示本次被中断;ERROR表示参数或socket错误。
* 非OK时transferred为0;此接口不会返回EOF,写入0字节仍属于OK。
* 说明:只按transferred推进发送位置,未写入的后缀由调用方保留并择机继续发送。
* 函数返回后不再借用data;OK不代表对端应用收到数据或完成104协议确认。
* WOULD_BLOCK/RETRY以及OK但写入0字节都不应被视为发送进度。
* 调用方须限制连续无进展的立即重试,并安排延时恢复;不能仅依赖新的可写边沿。
*/
extern IEC104_NetIoResult_t IEC104_Net_Send(
IEC104_NetSocket_t socket_handle, const void *data, size_t length);
/**
* 功能:等待本轮关注的监听或客户端事件;空集合只作有限定时等待,不执行I/O。
* 输入参数:interest为上述事件位的组合,允许为0,不允许包含未知位。
* 关注ACCEPT时listener须有效;关注READ/WRITE时client须有效;
* 未被关注的句柄可以为INVALID_SOCKET。
* timeout_ms为本次等待时长,单位毫秒;为0时只查询,空集合直接返回。
* UINT32_MAX仍为有限时长;后端须避开系统无限等待哨兵,必要时提前返回。
* 输出参数:ready_events必填,每次先清零;native_error可为NULL。
* 返回值:OK时ready_events给出就绪事件的位组合,0表示未报告就绪(包括纯定时等待);
* RETRY表示等待被中断,ERROR表示参数或等待失败,两者就绪位均为0。
* 说明:最多调用一次平台等待;RETRY交由调用方按剩余期限重新调度,后端不无限重试。
* 等待可能提前结束,是否到期由调用方按单调时钟重新判断。
* 就绪后实际I/O仍可能暂不可读写、部分发送或遇到EOF,必须检查后续操作结果。
*/
extern IEC104_NetResult_t IEC104_Net_Wait(
IEC104_NetSocket_t listener, IEC104_NetSocket_t client, uint32_t interest,
uint32_t timeout_ms, uint32_t *ready_events, int *native_error);
/**
* 功能:关闭一个监听或客户端句柄,不调用Cleanup或业务回调。
* 输入参数:socket_handle为要关闭的句柄;INVALID_SOCKET视为已关闭。
* 输出参数:native_error可为NULL,失败时保留关闭操作的诊断码。
* 返回值:OK表示关闭成功或传入无效哨兵值;ERROR表示关闭操作失败。
* 说明:句柄按值传入,本函数无法修改调用方字段;调用方需维护自己的句柄状态。
* 成功关闭后应将保存的句柄置为INVALID_SOCKET,避免重复关闭已被系统复用的值。
*/
extern IEC104_NetResult_t IEC104_Net_Close(
IEC104_NetSocket_t socket_handle, int *native_error);
/**
* 功能:读取平台单调时钟,供连接超时和收发期限计算使用。
* 返回值:64位单调毫秒计数,不是UTC时间戳,也不用于显示日期;0及连续读取相等值有效。
* 说明:与日历时间及校时无关;同一会话的时间差和期限必须使用同一时钟来源。
* 有效运行期间不得倒退、复位或回绕。
* 若底层仅提供32位节拍,须通过回绕计数或等效机制扩展为64位,
* 再按节拍频率换算为毫秒;不得仅作uint64_t强制转换。
* 扩展机制必须保证不漏记回绕,不能依赖本函数被频繁调用。
* 扩展计数的更新与读取须正确同步,取得高低位一致的快照,
* 防止节拍中断或其他并发更新造成读值错误;这不构成从中断上下文调用本接口的保证。
* 本函数只读取时钟,不创建定时器,也不触发协议超时处理。
*/
extern uint64_t IEC104_Net_Now_Ms(void);
#ifdef __cplusplus
}
#endif
#endif /* IEC104_NET_H */