socket.h 10.8 KB
Newer Older
lishen's avatar
lishen committed
1
2
3
4
5
6
7
8
9
10
11
12
13
14
#pragma once

#include <sys/socket.h>
#include <arpa/inet.h>
#include <netinet/tcp.h>
#include <netdb.h>
#include <fcntl.h>
#include <poll.h>
#include "base.h"

namespace sccl {
namespace hardware {
namespace net {

15
16
17
18
19
20
21
22
23
#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 {
lishen's avatar
lishen committed
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99

/* 用于存储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);
100
101
102
103
104

// 创建一个监听socket,用于服务器端等待客户端的连接请求。
// 是否阻塞: 阻塞,直到有客户端连接。
// 使用端: 服务器端。
// 使用时机: 当服务器准备好接受客户端连接时使用。
lishen's avatar
lishen committed
105
scclResult_t scclSocketListen(struct scclSocket* sock);
106
107
108
109
110

// 获取socket的地址信息。
// 是否阻塞: 不阻塞。
// 使用端: 通用(客户端和服务器端均可使用)。
// 使用时机: 需要获取当前socket绑定的地址和端口信息时使用。
lishen's avatar
lishen committed
111
scclResult_t scclSocketGetAddr(struct scclSocket* sock, union scclSocketAddress* addr);
112
113
114
115
116

// 客户端主动连接到服务器端的套接字。
// 是否阻塞: 可以选择阻塞或非阻塞,取决于实现和调用方式。
// 使用端: 客户端。
// 使用时机: 客户端需要与服务器建立连接时使用。
lishen's avatar
lishen committed
117
scclResult_t scclSocketConnect(struct scclSocket* sock, int portReuse = 0);
118
119
120
121
122

// 检查socket连接状态,判断是否已准备好进行通信。
// 是否阻塞: 不阻塞。
// 使用端: 通用(客户端和服务器端均可使用)。
// 使用时机: 在进行数据传输之前,检查连接是否已建立。
lishen's avatar
lishen committed
123
scclResult_t scclSocketReady(struct scclSocket* sock, int* running);
124
125
126
127
128
129
130
131
132
133
134

// 服务器端接受客户端的连接请求,创建一个新的套接字用于通信。
// 是否阻塞: 可以选择阻塞或非阻塞,取决于实现和调用方式。
// 使用端: 服务器端。
// 使用时机: 服务器端在监听socket上收到客户端连接请求后使用。
scclResult_t scclSocketAccept(struct scclSocket* sock, struct scclSocket* listenSock);

// 获取socket的文件描述符。
// 是否阻塞: 不阻塞。
// 使用端: 通用(客户端和服务器端均可使用)。
// 使用时机: 需要直接操作socket的文件描述符时使用,例如在多路复用中。
lishen's avatar
lishen committed
135
scclResult_t scclSocketGetFd(struct scclSocket* sock, int* fd);
136
137
138
139
140

// 设置socket的文件描述符。
// 是否阻塞: 不阻塞。
// 使用端: 通用(客户端和服务器端均可使用)。
// 使用时机: 在已有文件描述符的基础上创建socket对象时使用。
lishen's avatar
lishen committed
141
scclResult_t scclSocketSetFd(int fd, struct scclSocket* sock);
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
//////////////////////////////////////////////////////////////////////////////////////////////////
/**
 * @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&);
};
lishen's avatar
lishen committed
184

185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
/**
 * @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);
    }
};
242
} // namespace net_socket
lishen's avatar
lishen committed
243
244
245
} // namespace net
} // namespace hardware
} // namespace sccl