|
@@ -0,0 +1,213 @@
|
|
|
|
+# 网络模块
|
|
|
|
+
|
|
|
|
+## 2、网络实例
|
|
|
|
+
|
|
|
|
+在每个使用网络模块的程序中,我们都需要创建一个网络实例。
|
|
|
|
+
|
|
|
|
+### 2.1 网络实例结构体
|
|
|
|
+```c
|
|
|
|
+// 网络实例结构体
|
|
|
|
+typedef struct network {
|
|
|
|
+ struct network_event_manager event_manger; // 网络事件管理器
|
|
|
|
+ int network_event_count; // 当前网络事件数量(todo 暂时我认为是当前)
|
|
|
|
+ struct network_poll_event *poll_event; // 网络轮询事件(网络事件内部机制就是轮询)
|
|
|
|
+
|
|
|
|
+ int error_code; // 错误代码
|
|
|
|
+ int max_sockets; // 最大套接字数量
|
|
|
|
+
|
|
|
|
+ struct network_message *messages; // 网络消息数组
|
|
|
|
+ struct network_socket *sockets; // 套接字数组
|
|
|
|
+ struct network_socket *free_socket; // 空闲套接字链表
|
|
|
|
+ struct network_socket *tail_socket; // 套接字链表的尾部
|
|
|
|
+ struct network_buffer *receive_pool; // 接收缓冲区池
|
|
|
|
+} TNetwork;
|
|
|
|
+```
|
|
|
|
+
|
|
|
|
+### 2.2 创建网络实例
|
|
|
|
+
|
|
|
|
+创建一个网络实例(network)时, 需要设定一个允许的最大连接数(`max_connection_count`) 和 每个连接需要用到的接收缓冲区大小(`pef_connection_recevie_buffer_size`)。
|
|
|
|
+
|
|
|
|
+```c
|
|
|
|
+/**
|
|
|
|
+ * @brief 创建并初始化一个网络实例
|
|
|
|
+ *
|
|
|
|
+ * 这个方法主要的功能是给网络实例需要用到的参数分配合适的空间;
|
|
|
|
+ * 这个空间基本是所有空间的分配,根据最大连接数来决定。、
|
|
|
|
+ *
|
|
|
|
+ * @参数 max_connection_count 最大连接数,也就是这个网络实例允许的最大连接数
|
|
|
|
+ * @参数 per_connection_receive_buffer_size 每个连接的接收缓冲区的大小
|
|
|
|
+ * @返回 指向这个网络实例的指针
|
|
|
|
+ * @例子
|
|
|
|
+ * N = network_create(65535, 64*1024);
|
|
|
|
+ * 创建一个支持最大连接数为65535,每个连接的接收缓冲区为64KB的网络实例;
|
|
|
|
+ * 这个方法一般都是网络初始化中被调用。
|
|
|
|
+ */
|
|
|
|
+struct network *network_create(int max_connection_count, int per_connection_receive_buffer_size) {
|
|
|
|
+ // 验证必要参数:最大连接数和每个连接对应的接收缓冲区大小不能为0
|
|
|
|
+ if (max_connection_count == 0 || per_connection_receive_buffer_size == 0) {
|
|
|
|
+ return NULL;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ // 分配网络实例结构的内存空间
|
|
|
|
+ struct network *self = malloc(sizeof(struct network));
|
|
|
|
+ if (self == NULL) {
|
|
|
|
+ return NULL;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ // 初始化网络事件管理器
|
|
|
|
+ if (network_event_manager_init(&self->event_manger, max_connection_count)) {
|
|
|
|
+ free(self);
|
|
|
|
+ return NULL;
|
|
|
|
+ };
|
|
|
|
+
|
|
|
|
+ // 分配并初始化网络事件数组的空间
|
|
|
|
+ self->network_event_count = 0; // 设置当前网络事件的数量为0
|
|
|
|
+ self->events = malloc(max_connection_count * sizeof(struct network_event)); // 给事件数组分配空间
|
|
|
|
+ if (self->events == NULL) {
|
|
|
|
+ free(self);
|
|
|
|
+ return NULL;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ // 设置可以处理的最大套接字数量并分配套接字数组的空间
|
|
|
|
+ self->max_sockets = max_connection_count; // 设置可以处理的最大套接字数量
|
|
|
|
+ self->sockets = network_socket_alloc_all_sockets(max_connection_count); // 给套接字数组分配空间
|
|
|
|
+ if (self->sockets == NULL) {
|
|
|
|
+ free(self->events);
|
|
|
|
+ free(self);
|
|
|
|
+ return NULL;
|
|
|
|
+ }
|
|
|
|
+ self->free_socket = &self->sockets[0]; // 空闲套接字默认指向 套接字数组顶部
|
|
|
|
+ self->tail_socket = &self->sockets[max_connection_count - 1]; // 套接字数组的尾部
|
|
|
|
+
|
|
|
|
+ // 分配并初始化网络消息数组的空间
|
|
|
|
+ self->messages = malloc(max_connection_count * sizeof(struct network_message)); // 给网络消息分配空间
|
|
|
|
+ if (self->messages == NULL) {
|
|
|
|
+ free(self->sockets);
|
|
|
|
+ free(self->events);
|
|
|
|
+ free(self);
|
|
|
|
+ return NULL;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ // 创建一个接收缓冲区,这个接受缓冲区由max个read_buffer组成
|
|
|
|
+ self->receive_pool = network_buffer_create(max_connection_count, per_connection_receive_buffer_size);
|
|
|
|
+ if (self->receive_pool == NULL) {
|
|
|
|
+ free(self->messages);
|
|
|
|
+ free(self->sockets);
|
|
|
|
+ free(self->events);
|
|
|
|
+ free(self);
|
|
|
|
+ return NULL;
|
|
|
|
+ }
|
|
|
|
+ return self;
|
|
|
|
+}
|
|
|
|
+```
|
|
|
|
+
|
|
|
|
+### 2.3 释放网络实例
|
|
|
|
+```c
|
|
|
|
+/**
|
|
|
|
+ * @brief 释放指定的网络实例
|
|
|
|
+ * @param self 指定的网络结构
|
|
|
|
+ */
|
|
|
|
+void network_free(struct network *self) {
|
|
|
|
+ // 如果需要释放的实例为空,直接返回
|
|
|
|
+ if (self == NULL) return;
|
|
|
|
+
|
|
|
|
+ // 把网络实例中所有的套接字关闭
|
|
|
|
+ int i;
|
|
|
|
+ for (i = 0; i < self->max_sockets; ++i) {
|
|
|
|
+ struct network_socket *s = &self->sockets[i];
|
|
|
|
+ if (s->status >= SOCKET_STATUS_OPENED) {
|
|
|
|
+ close_socket(self, s);
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ // 释放相关资源
|
|
|
|
+ free(self->sockets);
|
|
|
|
+ self->free_socket = NULL;
|
|
|
|
+ self->tail_socket = NULL;
|
|
|
|
+ free(self->events);
|
|
|
|
+ free(self->messages);
|
|
|
|
+ network_buffer_free(self->receive_pool);
|
|
|
|
+
|
|
|
|
+ // 销毁网络事件管理器
|
|
|
|
+ network_event_manager_destroy(&self->event_manger);
|
|
|
|
+ free(self);
|
|
|
|
+}
|
|
|
|
+```
|
|
|
|
+
|
|
|
|
+### 2.4 网络实例开启监听
|
|
|
|
+```c
|
|
|
|
+/**
|
|
|
|
+ * @brief 开始监听指定地址和端口的连接请求
|
|
|
|
+ * 此函数创建一个套接字并将其绑定到指定的地址和端口,在该套接字上开始监听连接请求。
|
|
|
|
+ * 随后,它创建一个网络套接字结构,将其添加到网络结构中,并将其订阅为可读事件。
|
|
|
|
+ * @param self 指向网络结构体的指针。
|
|
|
|
+ * @param addr 要绑定的IPv4地址。
|
|
|
|
+ * @param port 要绑定的端口号。
|
|
|
|
+ * @param write_buffer_max 写缓冲区的最大大小。
|
|
|
|
+ * @param user_data 用户指定的数据。
|
|
|
|
+ * @param user_type 用户指定的类型。
|
|
|
|
+ * @return 0 表示成功;否则表示错误代码。
|
|
|
|
+ */
|
|
|
|
+int network_listen(struct network *self, uint32_t addr, uint16_t port, int write_buffer_max, int user_data, int user_type) {
|
|
|
|
+ int error = 0;
|
|
|
|
+
|
|
|
|
+ // 创建套接字,用于后续的操作
|
|
|
|
+ SOCKET_T fd = socket(AF_INET, SOCK_STREAM, 0);
|
|
|
|
+
|
|
|
|
+ // 创建套接字失败
|
|
|
|
+ if (fd == SOCKET_INVALID) {
|
|
|
|
+ error = SOCKET_ERROR_CODE;
|
|
|
|
+ return NETWORK_ERROR(error);
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ // 将套接字设置为非阻塞、设置关闭时不作处理、设置地址重用
|
|
|
|
+ if (SOCKET_NON_BLOCKING(fd) == -1 ||
|
|
|
|
+ SOCKET_CLOSE_ONE_EXEC(fd) == -1 ||
|
|
|
|
+ SOCKET_REUSE_ADDR(fd) == -1) {
|
|
|
|
+ error = SOCKET_ERROR_CODE;
|
|
|
|
+ SOCKET_CLOSE(fd);
|
|
|
|
+ return NETWORK_ERROR(error);
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ // 设置绑定的地址和端口
|
|
|
|
+ struct sockaddr_in my_addr;
|
|
|
|
+ memset(&my_addr, 0, sizeof(struct sockaddr_in));
|
|
|
|
+ my_addr.sin_family = AF_INET;
|
|
|
|
+ my_addr.sin_port = htons(port);
|
|
|
|
+ my_addr.sin_addr.s_addr = addr;
|
|
|
|
+ if (bind(fd, (struct sockaddr *) &my_addr, sizeof(struct sockaddr)) == -1) {
|
|
|
|
+ error = SOCKET_ERROR_CODE;
|
|
|
|
+ SOCKET_CLOSE(fd);
|
|
|
|
+ return NETWORK_ERROR(error);
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ // 开始监听连接请求
|
|
|
|
+ if (listen(fd, LISTEN_BACK_LOG) == -1) {
|
|
|
|
+ error = SOCKET_ERROR_CODE;
|
|
|
|
+ SOCKET_CLOSE(fd);
|
|
|
|
+ return NETWORK_ERROR(error);
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ // 初始化网络套接字
|
|
|
|
+ struct network_socket *s = network_socket_init(self, fd, addr, port, write_buffer_max, user_data, user_type);
|
|
|
|
+ // 初始化失败
|
|
|
|
+ if (s == NULL) {
|
|
|
|
+ error = NETWORK_ERROR_CREATE_SOCKET;
|
|
|
|
+ SOCKET_CLOSE(fd);
|
|
|
|
+ return NETWORK_ERROR(error);
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ // 订阅套接字,将套接字纳入事件管理器中
|
|
|
|
+ if (network_socket_subscribe(self, s, NETWORK_EVENT_READABLE)) {
|
|
|
|
+ error = SOCKET_ERROR_CODE;
|
|
|
|
+ network_socket_close(self, s);
|
|
|
|
+ return NETWORK_ERROR(error);
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ // 设置套接字状态为正在监听
|
|
|
|
+ s->status = SOCKET_STATUS_LISTENING;
|
|
|
|
+ return 0;
|
|
|
|
+}
|
|
|
|
+```
|
|
|
|
+
|
|
|
|
+### 开启网络监听
|