#pragma once #include #include #include #include #include #include #include "base.h" namespace sccl { namespace hardware { namespace net { #define SLEEP_INT 1000 // 连接重试的休眠间隔,单位为微秒 constexpr int MAX_IFS = 16; // 最大接口数量 constexpr int MAX_IF_NAME_SIZE = 16; // 每个接口名称的最大长度 constexpr int RETRY_REFUSED_TIMES = 2e4; // 在报告超时之前,连接被拒绝的重试次数(总计20秒) constexpr int RETRY_TIMEDOUT_TIMES = 3; // 连接超时的重试次数(每次重试可能需要20秒) constexpr int SOCKET_NAME_MAXLEN = (NI_MAXHOST + NI_MAXSERV); // 套接字名称的最大长度,包括主机名和服务名 constexpr uint64_t SCCL_SOCKET_MAGIC = 0x564ab9f2fc4b9d6cULL; // 用于标识套接字的魔数 namespace net_socket { /* 用于存储IPv4/IPv6通用套接字地址的联合体 */ union scclSocketAddress { // 联合体用于存储不同类型的套接字地址 struct sockaddr sa; // 通用套接字地址 struct sockaddr_in sin; // IPv4套接字地址 struct sockaddr_in6 sin6; // IPv6套接字地址 }; enum scclSocketState : uint8_t { scclSocketStateNone = 0, // 未定义状态 scclSocketStateInitialized = 1, // 已初始化状态 scclSocketStateAccepting = 2, // 正在接受连接状态 scclSocketStateAccepted = 3, // 已接受连接状态 scclSocketStateConnecting = 4, // 正在连接状态 scclSocketStateConnectPolling = 5, // 连接轮询状态 scclSocketStateConnected = 6, // 已连接状态 scclSocketStateReady = 7, // 准备就绪状态 scclSocketStateClosed = 8, // 已关闭状态 scclSocketStateError = 9, // 错误状态 scclSocketStateNum = 10 // 状态总数 }; enum scclSocketType : uint8_t { scclSocketTypeUnknown = 0, // 未知类型 scclSocketTypeBootstrap = 1, // 启动类型 scclSocketTypeProxy = 2, // 代理类型 scclSocketTypeNetSocket = 3, // 网络套接字类型 scclSocketTypeNetIb = 4 // 网络Infiniband类型 }; struct scclSocket { int fd; // 文件描述符 int acceptFd; // 接受连接的文件描述符 int timedOutRetries; // 超时重试次数 int refusedRetries; // 被拒绝重试次数 union scclSocketAddress addr; // 套接字地址 volatile uint32_t* abortFlag; // 中止标志 int asyncFlag; // 异步标志 enum scclSocketState state; // 套接字状态 int salen; // 地址长度 uint64_t magic; // 魔术数 enum scclSocketType type; // 套接字类型 }; //////////////////////////////////// socket工具 //////////////////////////////////// // 将地址转换为字符串 const char* scclSocketToString(const union scclSocketAddress* addr, char* buf, const int numericHostForm = 1); // 从字符串中获取地址 scclResult_t scclSocketGetAddrFromString(union scclSocketAddress* ua, const char* ip_port_pair); // 查找与子网匹配的接口 int scclFindInterfaceMatchSubnet(char* ifNames, union scclSocketAddress* localAddrs, union scclSocketAddress* remoteAddr, int ifNameMaxSize, int maxIfs); // 查找可用的socket网络接口 int scclFindSocketInterfaces(char* ifNames, union scclSocketAddress* ifAddrs, int ifNameMaxSize, int maxIfs); //////////////////////////////////// socket基础操作 //////////////////////////////////// // 进行socket操作 scclResult_t scclSocketProgress(int op, struct scclSocket* sock, void* ptr, int size, int* offset); // 等待socket操作完成 scclResult_t scclSocketWait(int op, struct scclSocket* sock, void* ptr, int size, int* offset); // 发送数据 scclResult_t scclSocketSend(struct scclSocket* sock, void* ptr, int size); // 接收数据 scclResult_t scclSocketRecv(struct scclSocket* sock, void* ptr, int size); // 尝试接收数据 scclResult_t scclSocketTryRecv(struct scclSocket* sock, void* ptr, int size, int* closed, bool blocking); // 关闭socket scclResult_t scclSocketClose(struct scclSocket* sock); //////////////////////////////////// 应用socket //////////////////////////////////// // 初始化一个socket scclResult_t scclSocketInit(struct scclSocket* sock, union scclSocketAddress* addr = NULL, uint64_t magic = SCCL_SOCKET_MAGIC, enum scclSocketType type = scclSocketTypeUnknown, volatile uint32_t* abortFlag = NULL, int asyncFlag = 0); // 创建一个监听socket,用于服务器端等待客户端的连接请求。 // 是否阻塞: 阻塞,直到有客户端连接。 // 使用端: 服务器端。 // 使用时机: 当服务器准备好接受客户端连接时使用。 scclResult_t scclSocketListen(struct scclSocket* sock); // 获取socket的地址信息。 // 是否阻塞: 不阻塞。 // 使用端: 通用(客户端和服务器端均可使用)。 // 使用时机: 需要获取当前socket绑定的地址和端口信息时使用。 scclResult_t scclSocketGetAddr(struct scclSocket* sock, union scclSocketAddress* addr); // 客户端主动连接到服务器端的套接字。 // 是否阻塞: 可以选择阻塞或非阻塞,取决于实现和调用方式。 // 使用端: 客户端。 // 使用时机: 客户端需要与服务器建立连接时使用。 scclResult_t scclSocketConnect(struct scclSocket* sock, int portReuse = 0); // 检查socket连接状态,判断是否已准备好进行通信。 // 是否阻塞: 不阻塞。 // 使用端: 通用(客户端和服务器端均可使用)。 // 使用时机: 在进行数据传输之前,检查连接是否已建立。 scclResult_t scclSocketReady(struct scclSocket* sock, int* running); // 服务器端接受客户端的连接请求,创建一个新的套接字用于通信。 // 是否阻塞: 可以选择阻塞或非阻塞,取决于实现和调用方式。 // 使用端: 服务器端。 // 使用时机: 服务器端在监听socket上收到客户端连接请求后使用。 scclResult_t scclSocketAccept(struct scclSocket* sock, struct scclSocket* listenSock); // 获取socket的文件描述符。 // 是否阻塞: 不阻塞。 // 使用端: 通用(客户端和服务器端均可使用)。 // 使用时机: 需要直接操作socket的文件描述符时使用,例如在多路复用中。 scclResult_t scclSocketGetFd(struct scclSocket* sock, int* fd); // 设置socket的文件描述符。 // 是否阻塞: 不阻塞。 // 使用端: 通用(客户端和服务器端均可使用)。 // 使用时机: 在已有文件描述符的基础上创建socket对象时使用。 scclResult_t scclSocketSetFd(int fd, struct scclSocket* sock); ////////////////////////////////////////////////////////////////////////////////////////////////// /** * @brief 管理套接字的基类,提供套接字的获取、释放和初始化功能。 * * 该类包含一个指向scclSocket结构体的指针sock_和一个布尔值owned_, * 用于跟踪套接字的所有权。提供了获取和释放套接字的方法, * 以及一个初始化方法来设置套接字的各种参数。 */ class scclSocketManager { public: struct scclSocket* getSocket() { return sock_; } struct scclSocket* releaseSocket() { owned_ = false; return sock_; } protected: struct scclSocket* sock_; bool owned_; scclSocketManager() : sock_(nullptr), owned_(true) {} virtual ~scclSocketManager() { if(owned_ && sock_) { scclSocketClose(sock_); delete sock_; } } // 默认的初始化过程 scclResult_t init(union scclSocketAddress* addr = NULL, uint64_t magic = SCCL_SOCKET_MAGIC, enum scclSocketType type = scclSocketTypeUnknown, volatile uint32_t* abortFlag = NULL, int asyncFlag = 0) { sock_ = new struct scclSocket; return scclSocketInit(sock_, addr, magic, type, abortFlag, asyncFlag); } private: // 禁止拷贝构造和赋值操作 scclSocketManager(const scclSocketManager&); scclSocketManager& operator=(const scclSocketManager&); }; /** * @brief 管理服务器端套接字的类,继承自scclSocketManager。 * * 该类在构造时初始化套接字,并将其设置为监听模式。 */ class scclSocketServerManager : public scclSocketManager { public: scclSocketServerManager(union scclSocketAddress* addr, uint64_t magic = SCCL_SOCKET_MAGIC, enum scclSocketType type = scclSocketTypeUnknown, volatile uint32_t* abortFlag = NULL, int asyncFlag = 0) { SCCLCHECK(init(addr, magic, type, abortFlag, asyncFlag)); SCCLCHECK(scclSocketListen(sock_)); SCCLCHECK(scclSocketGetAddr(sock_, addr)); #if 0 char line[SOCKET_NAME_MAXLEN + MAX_IF_NAME_SIZE + 2]; net::net_socket::scclSocketToString(addr, line); printf("server listening addr: %s\n", line); #endif } }; /** * @brief 管理客户端套接字的类,继承自scclSocketManager。 * * 该类在构造时初始化套接字,并尝试连接到服务器。 */ class scclSocketClientManager : public scclSocketManager { public: scclSocketClientManager(union scclSocketAddress* addr, uint64_t magic = SCCL_SOCKET_MAGIC, enum scclSocketType type = scclSocketTypeUnknown, volatile uint32_t* abortFlag = NULL, int asyncFlag = 0) { SCCLCHECK(init(addr, magic, type, abortFlag, asyncFlag)); SCCLCHECK(scclSocketConnect(sock_)); #if 0 char line[SOCKET_NAME_MAXLEN + MAX_IF_NAME_SIZE + 2]; net::net_socket::scclSocketToString(addr, line); printf("client connect addr: %s\n", line); #endif } }; /** * @brief 管理接受连接的套接字的类,继承自scclSocketManager。 * * 该类在构造时初始化套接字,并接受来自监听套接字的连接。 */ class scclSocketAcceptManager : public scclSocketManager { public: scclSocketAcceptManager(struct scclSocket* listenSock, int asyncFlag = 0) : scclSocketManager() { init(NULL, SCCL_SOCKET_MAGIC, scclSocketTypeUnknown, NULL, /*asyncFlag=*/asyncFlag); scclSocketAccept(sock_, listenSock); } }; } // namespace net_socket } // namespace net } // namespace hardware } // namespace sccl