• 十、DPDK协议栈之ddos和epoll


    同样,我们在之前的代码中进行迭代,增加ddos和epoll实现并发的功能

    DDOS

    ddos这里从网上“借鉴”一下,不过多的去梳理,能用就行
    代码如下:

    #if ENABLE_DDOS_DETECT
    // 如果启用DDoS攻击检测
    
    #define CAPTURE_WINDOWS		256
    
    static double tresh = 1200.0;  // 阈值,用于判断DDoS攻击的阈值
    
    static uint32_t p_setbits[CAPTURE_WINDOWS] = {0};  // 存储每个窗口的设置位数
    static uint32_t p_totbits[CAPTURE_WINDOWS] = {0};  // 存储每个窗口的总位数
    static double p_entropy[CAPTURE_WINDOWS] = {0};    // 存储每个窗口的熵
    static int pkt_idx = 0;  // 当前窗口的索引
    
    /*
     23651/17408, E(nan)
     1805328/4456448 Entropy(nan), Total_Entropy(4339971.575790)
     
      23526/17408, E(nan)
     1805203/4456448 Entropy(nan), Total_Entropy(4339902.272671)
     
      17847/17408, E(nan)
     1799524/4456448 Entropy(nan), Total_Entropy(4336731.547140)
     
      17670/17408, E(nan)
     1799347/4456448 Entropy(nan), Total_Entropy(4336632.027009)
     
      17774/17408, E(nan)
     1799451/4456448 Entropy(nan), Total_Entropy(4336690.507219)
     
     */ 
    // 一些示例数据,可能是用于调试和演示的数据
    
    static double ddos_entropy(double set_bits, double total_bits) {
    	// 计算熵的函数
    	return ( - set_bits) * (log2(set_bits) - log2(total_bits)) // 1 
    	- (total_bits - set_bits) * (log2(total_bits - set_bits) - log2(total_bits))
    	+ log2(total_bits);
    }
    
    static uint32_t count_bit(uint8_t *msg, const uint32_t length) {
    	// 计算位数的函数,根据输入消息和长度返回设置的位数
    }
    
    static int ddos_detect(struct rte_mbuf *pkt) {
    	// DDoS攻击检测函数,用于检测DDoS攻击
    
    	static char flag = 0; // 1表示DDoS攻击,0表示没有攻击
    
    	uint8_t *msg = rte_pktmbuf_mtod(pkt, uint8_t *);
    	uint32_t set_bits = count_bit(msg, pkt->buf_len);  // 计算消息中的设置位数
    	uint32_t tot_bits = pkt->buf_len * 8;  // 消息的总位数
    
    	p_setbits[pkt_idx % CAPTURE_WINDOWS] = set_bits;  // 存储设置位数
    	p_totbits[pkt_idx % CAPTURE_WINDOWS] = tot_bits;  // 存储总位数
    	p_entropy[pkt_idx % CAPTURE_WINDOWS] = ddos_entropy(set_bits, tot_bits);  // 存储熵值
    
    	if (pkt_idx >= CAPTURE_WINDOWS) {
    		// 如果达到了窗口大小
    
    		int i = 0;
    		uint32_t total_set = 0, total_bit = 0;
    		double sum_entropy = 0.0;
    
    		for (i = 0; i < CAPTURE_WINDOWS; i++) {
    			total_set += p_setbits[i]; // 累计设置位数
    			total_bit += p_totbits[i]; // 累计总位数
    			sum_entropy += p_entropy[i]; // 累计熵值
    		}
    
    		double entropy = ddos_entropy(total_set, total_bit); // 计算总熵值
    
    		if (tresh <  sum_entropy - entropy) { // 如果总熵值超过阈值,表示DDoS攻击
    			if (!flag) { // 如果之前没有检测到攻击
    				rte_exit(EXIT_FAILURE, "ddos attack !!! Entropy(%f) < Total_Entropy(%f)\n", 
    					entropy, sum_entropy); // 输出攻击信息并退出
    			}
    			flag = 1; // 设置攻击标志
    		} else {
    			if (flag) { // 如果之前检测到攻击
    				printf("no new !!! Entropy(%f) < Total_Entropy(%f)\n", 
    					entropy, sum_entropy); // 输出没有新攻击的信息
    			}
    			flag = 0; // 清除攻击标志
    		}
    
    		pkt_idx = (pkt_idx + 1) % CAPTURE_WINDOWS + CAPTURE_WINDOWS; // 更新窗口索引
    	} else {
    		pkt_idx++; // 增加窗口索引
    	}
    
    	return 0; // 返回检测结果
    }
    
    #endif
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 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

    main函数中通过调用ddos_detect进行防ddos攻击

    #if ENABLE_DDOS_DETECT
    		
    			unsigned i = 0;
    			for (i = 0;i < num_recvd;i ++) {
    				ddos_detect(rx[i]);
    			}
    #endif
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7

    EPOLL

    在之前的文章中,我们已经实现了完整的协议栈,但是只能用于处理一个客户端的请求,接下来我们就在原有的代码中增加epoll来实现并发的功能

    先定义几个用于epoll的结构体

    typedef union epoll_data {
        void *ptr;        // 用于存储一个指针,用于关联自定义数据
        int fd;           // 用于存储一个文件描述符
        uint32_t u32;     
        uint64_t u64;     
    } epoll_data_t;
    
    
    
    // 定义一个名为 'struct epoll_event' 的数据结构,用于描述 epoll 事件的信息
    
    struct epoll_event {
        uint32_t events;  // 表示事件的类型和状态,可以是多个标志的组合
    
        epoll_data_t data;  // 存储与事件相关的数据,可以是一个指针或文件描述符
    };
    
    // 表示 epoll 实例中的事件项
    
    struct epitem {
        RB_ENTRY(epitem) rbn;        // 红黑树节点,用于按套接字进行快速查找
        LIST_ENTRY(epitem) rdlink;   // 链表节点,用于存储就绪的事件项
        int rdy;                     // 是否在链表中的标志(存在于链表中则为非零)
    
        int sockfd;                  // 套接字描述符
        struct epoll_event event;    // 关联的 epoll 事件
    };
    // 定义一个名为 'struct eventpoll' 的数据结构,用于管理 epoll 实例的状态
    
    struct eventpoll {
        int fd;  // epoll 实例的文件描述符(fd)
    
        ep_rb_tree rbr;  // 红黑树,用于存储注册的事件
        int rbcnt;  // 红黑树节点计数
    
        LIST_HEAD( ,epitem) rdlist;  // 链表,用于存储就绪的事件项
        int rdnum;  // 就绪事件项的数量
    
        int waiting;  // 表示是否有线程正在等待事件的发生
    
        pthread_mutex_t mtx;  // 用于对红黑树进行更新的互斥锁
        pthread_spinlock_t lock;  // 用于对就绪事件链表进行更新的自旋锁
    
        pthread_cond_t cond;  // 用于线程等待事件的条件变量
        pthread_mutex_t cdmtx;  // 用于条件变量的互斥锁
    };
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46

    接下来是实现epoll的主要函数

    #if ENABLE_SINGLE_EPOLL
    // epoll 事件回调函数,用于处理套接字事件
    
    int epoll_event_callback(struct eventpoll *ep, int sockid, uint32_t event) {
    
    	// 创建一个临时的 epitem 结构体,用于查找对应套接字的事件项
    	struct epitem tmp;
    	tmp.sockfd = sockid;
    	struct epitem *epi = RB_FIND(_epoll_rb_socket, &ep->rbr, &tmp);
    	if (!epi) {
    		printf("rbtree not exist\n"); // 如果找不到对应的事件项,输出错误信息
    		return -1;
    	}
    
    	if (epi->rdy) {
    		// 如果事件项已经在就绪链表中,更新事件类型并返回 1
    		epi->event.events |= event;
    		return 1;
    	} 
    
    	printf("epoll_event_callback --> %d\n", epi->sockfd);
    
    	// 如果事件项不在就绪链表中,加锁处理
    	pthread_spin_lock(&ep->lock);
    
    	epi->rdy = 1; // 设置事件项为就绪状态
    	LIST_INSERT_HEAD(&ep->rdlist, epi, rdlink); // 将事件项添加到就绪链表头部
    	ep->rdnum ++; // 增加就绪事件数量
    
    	pthread_spin_unlock(&ep->lock); // 解锁就绪链表
    
    	pthread_mutex_lock(&ep->cdmtx); // 加锁条件变量互斥锁
    
    	pthread_cond_signal(&ep->cond); // 发送条件信号,通知等待的线程有新事件就绪
    	pthread_mutex_unlock(&ep->cdmtx); // 解锁条件变量互斥锁
    }
    // 创建一个新的 epoll 实例
    
    int nepoll_create(int size) {
        // 参数检查
    	if (size <= 0) return -1;
    
    	// 从位图中获取一个可用的文件描述符(fd),用于 epoll 实例
    	int epfd = get_fd_frombitmap(); //tcp, udp
    	
    	// 为 epoll 实例分配内存
    	struct eventpoll *ep = (struct eventpoll*)rte_malloc("eventpoll", sizeof(struct eventpoll), 0);
    	if (!ep) {
            // 如果内存分配失败,释放已分配的 fd 并返回错误
    		set_fd_frombitmap(epfd);
    		return -1;
    	}
    
    	// 获取全局的 TCP 表格实例,并将 epoll 实例关联到其中
    	struct ng_tcp_table *table = tcpInstance();
    	table->ep = ep;
    	
    	// 初始化 epoll 实例的各个字段
    	ep->fd = epfd;
    	ep->rbcnt = 0;
    	RB_INIT(&ep->rbr);
    	LIST_INIT(&ep->rdlist);
    
    	// 初始化 epoll 实例的互斥锁
    	if (pthread_mutex_init(&ep->mtx, NULL)) {
    		free(ep);
    		set_fd_frombitmap(epfd);
    		return -2;
    	}
    
    	// 初始化 epoll 实例的条件变量互斥锁
    	if (pthread_mutex_init(&ep->cdmtx, NULL)) {
    		pthread_mutex_destroy(&ep->mtx);
    		free(ep);
    		set_fd_frombitmap(epfd);
    		return -2;
    	}
    
    	// 初始化 epoll 实例的条件变量
    	if (pthread_cond_init(&ep->cond, NULL)) {
    		pthread_mutex_destroy(&ep->cdmtx);
    		pthread_mutex_destroy(&ep->mtx);
    		free(ep);
    		set_fd_frombitmap(epfd);
    		return -2;
    	}
    
    	// 初始化 epoll 实例的自旋锁
    	if (pthread_spin_init(&ep->lock, PTHREAD_PROCESS_SHARED)) {
    		pthread_cond_destroy(&ep->cond);
    		pthread_mutex_destroy(&ep->cdmtx);
    		pthread_mutex_destroy(&ep->mtx);
    		free(ep);
    		set_fd_frombitmap(epfd);
    		return -2;
    	}
    
    	// 返回创建的 epoll 实例的文件描述符(fd)
    	return epfd;
    }
    // 管理 epoll 实例中的事件关联
    
    int nepoll_ctl(int epfd, int op, int sockid, struct epoll_event *event) {
    	
        // 从文件描述符获取 epoll 实例的信息
    	struct eventpoll *ep = (struct eventpoll*)get_hostinfo_fromfd(epfd);
    	
        // 如果 epoll 实例无效或事件为空且操作不是删除操作,返回错误
    	if (!ep || (!event && op != EPOLL_CTL_DEL)) {
    		errno = -EINVAL; // 设置错误码为无效参数
    		return -1;
    	}
    
        // 如果操作是添加操作
    	if (op == EPOLL_CTL_ADD) {
            // 获取 epoll 实例的互斥锁,用于保护对 epoll 实例的操作
    		pthread_mutex_lock(&ep->mtx);
    
    		// 创建一个临时的事件项,用于查找是否已存在相同的套接字
    		struct epitem tmp;
    		tmp.sockfd = sockid;
    		struct epitem *epi = RB_FIND(_epoll_rb_socket, &ep->rbr, &tmp);
    
    		// 如果已经存在相同的套接字,解锁互斥锁并返回错误
    		if (epi) {
    			pthread_mutex_unlock(&ep->mtx);
    			return -1;
    		}
    
    		// 分配内存以存储事件项
    		epi = (struct epitem*)rte_malloc("epitem", sizeof(struct epitem), 0);
    
            // 如果内存分配失败,解锁互斥锁并返回错误
    		if (!epi) {
    			pthread_mutex_unlock(&ep->mtx);
    			rte_errno = -ENOMEM; // 设置错误码为内存不足
    			return -1;
    		}
    		
    		epi->sockfd = sockid;
    		memcpy(&epi->event, event, sizeof(struct epoll_event));
    
            // 在红黑树中插入事件项,表示注册了新的套接字
    		epi = RB_INSERT(_epoll_rb_socket, &ep->rbr, epi);
    
            // 增加 epoll 实例中的事件项计数
    		ep->rbcnt++;
    		
            // 解锁互斥锁
    		pthread_mutex_unlock(&ep->mtx);
    
    	} else if (op == EPOLL_CTL_DEL) {
            // 如果操作是删除操作
    
            // 获取 epoll 实例的互斥锁
    		pthread_mutex_lock(&ep->mtx);
    
    		// 创建一个临时的事件项,用于查找要删除的套接字
    		struct epitem tmp;
    		tmp.sockfd = sockid;
    		struct epitem *epi = RB_FIND(_epoll_rb_socket, &ep->rbr, &tmp);
    
            // 如果未找到要删除的套接字,解锁互斥锁并返回错误
    		if (!epi) {
    			pthread_mutex_unlock(&ep->mtx);
    			return -1;
    		}
    		
            // 从红黑树中移除事件项,表示取消了套接字的注册
    		epi = RB_REMOVE(_epoll_rb_socket, &ep->rbr, epi);
    		if (!epi) {
    			
    			pthread_mutex_unlock(&ep->mtx);
    			return -1;
    		}
            // 减少 epoll 实例中的事件项计数
    		ep->rbcnt--;
    
            // 释放事件项的内存
    		free(epi);
    		
            // 解锁互斥锁
    		pthread_mutex_unlock(&ep->mtx);
    
    	} else if (op == EPOLL_CTL_MOD) {
            // 如果操作是修改操作
    
            // 创建一个临时的事件项,用于查找要修改的套接字
    		struct epitem tmp;
    		tmp.sockfd = sockid;
    		struct epitem *epi = RB_FIND(_epoll_rb_socket, &ep->rbr, &tmp);
    
            // 如果找到要修改的套接字
    		if (epi) {
                // 更新套接字的事件类型
    			epi->event.events = event->events;
                // 同时添加 EPOLLERR 和 EPOLLHUP 标志,表示关注错误和挂起事件
    			epi->event.events |= EPOLLERR | EPOLLHUP;
    		} else {
    			rte_errno = -ENOENT; // 设置错误码为未找到
    			return -1;
    		}
    
    	} 
    
    	return 0; // 操作成功,返回 0
    }
    
    // 等待 epoll 事件的发生
    int nepoll_wait(int epfd, struct epoll_event *events, int maxevents, int timeout) {
    	
    	// 从文件描述符获取 epoll 实例的信息
    	struct eventpoll *ep = (struct eventpoll*)get_hostinfo_fromfd(epfd);
    	
    	// 如果 epoll 实例无效、事件数组为空或 maxevents 不合法,返回错误
    	if (!ep || !events || maxevents <= 0) {
    		rte_errno = -EINVAL; // 设置错误码为无效参数
    		return -1;
    	}
    
    	// 尝试获取 epoll 实例的条件变量互斥锁
    	if (pthread_mutex_lock(&ep->cdmtx)) {
    		// 如果获取锁失败,根据错误码输出相应信息
    		if (rte_errno == EDEADLK) {
    			printf("epoll lock blocked\n"); // 输出锁阻塞信息
    		}
    	}
    
    	// 当没有就绪的事件项并且超时时间不为零时,进入等待循环
    	while (ep->rdnum == 0 && timeout != 0) {
    
    		ep->waiting = 1; // 表示有线程正在等待事件的发生
    		if (timeout > 0) {
    			// 计算绝对超时时间
    
    			struct timespec deadline;
    
    			clock_gettime(CLOCK_REALTIME, &deadline);
    			if (timeout >= 1000) {
    				int sec;
    				sec = timeout / 1000;
    				deadline.tv_sec += sec;
    				timeout -= sec * 1000;
    			}
    
    			deadline.tv_nsec += timeout * 1000000;
    
    			if (deadline.tv_nsec >= 1000000000) {
    				deadline.tv_sec++;
    				deadline.tv_nsec -= 1000000000;
    			}
    
    			// 使用条件变量的超时等待
    			int ret = pthread_cond_timedwait(&ep->cond, &ep->cdmtx, &deadline);
    			if (ret && ret != ETIMEDOUT) {
    				printf("pthread_cond_timewait\n");
    				pthread_mutex_unlock(&ep->cdmtx);
    				return -1;
    			}
    			timeout = 0; // 清除超时标志
    		} else if (timeout < 0) {
    			// 使用条件变量的等待,直到事件发生
    			int ret = pthread_cond_wait(&ep->cond, &ep->cdmtx);
    			if (ret) {
    				printf("pthread_cond_wait\n");
    				pthread_mutex_unlock(&ep->cdmtx);
    				return -1;
    			}
    		}
    		ep->waiting = 0; // 表示等待结束
    	}
    
    	// 解锁条件变量互斥锁
    	pthread_mutex_unlock(&ep->cdmtx);
    
    	// 使用自旋锁对就绪事件链表进行操作
    	pthread_spin_lock(&ep->lock);
    
    	int cnt = 0; // 记录就绪事件的数量
    	int num = (ep->rdnum > maxevents ? maxevents : ep->rdnum); // 计算最多处理的事件数
    	int i = 0;
    	
    	// 遍历就绪事件链表,将事件拷贝到用户提供的事件数组中
    	while (num != 0 && !LIST_EMPTY(&ep->rdlist)) {
    
    		struct epitem *epi = LIST_FIRST(&ep->rdlist);
    		LIST_REMOVE(epi, rdlink);
    		epi->rdy = 0;
    
    		memcpy(&events[i++], &epi->event, sizeof(struct epoll_event));
    		
    		num --;
    		cnt ++;
    		ep->rdnum --;
    	}
    	
    	// 解锁自旋锁
    	pthread_spin_unlock(&ep->lock);
    
    	return cnt; // 返回处理的就绪事件数量
    }
    
    
    #endif
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 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
    • 100
    • 101
    • 102
    • 103
    • 104
    • 105
    • 106
    • 107
    • 108
    • 109
    • 110
    • 111
    • 112
    • 113
    • 114
    • 115
    • 116
    • 117
    • 118
    • 119
    • 120
    • 121
    • 122
    • 123
    • 124
    • 125
    • 126
    • 127
    • 128
    • 129
    • 130
    • 131
    • 132
    • 133
    • 134
    • 135
    • 136
    • 137
    • 138
    • 139
    • 140
    • 141
    • 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
    • 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
    • 242
    • 243
    • 244
    • 245
    • 246
    • 247
    • 248
    • 249
    • 250
    • 251
    • 252
    • 253
    • 254
    • 255
    • 256
    • 257
    • 258
    • 259
    • 260
    • 261
    • 262
    • 263
    • 264
    • 265
    • 266
    • 267
    • 268
    • 269
    • 270
    • 271
    • 272
    • 273
    • 274
    • 275
    • 276
    • 277
    • 278
    • 279
    • 280
    • 281
    • 282
    • 283
    • 284
    • 285
    • 286
    • 287
    • 288
    • 289
    • 290
    • 291
    • 292
    • 293
    • 294
    • 295
    • 296
    • 297
    • 298
    • 299
    • 300
    • 301
    • 302
    • 303
    • 304
    • 305

    接下来修改我们之前的tcp服务器入口函数

    // TCP 服务器入口函数
    
    static int tcp_server_entry(__attribute__((unused))  void *arg)  {
    	
    	// 创建 TCP 套接字用于监听连接请求
    	int listenfd = nsocket(AF_INET, SOCK_STREAM, 0);
    	if (listenfd == -1) {
    		return -1;
    	}
    
    	// 配置服务器监听地址
    	struct sockaddr_in servaddr;
    	memset(&servaddr, 0, sizeof(struct sockaddr));
    	servaddr.sin_family = AF_INET;
    	servaddr.sin_addr.s_addr = htonl(INADDR_ANY);
    	servaddr.sin_port = htons(9999);
    	
    	// 将套接字绑定到监听地址
    	nbind(listenfd, (struct sockaddr*)&servaddr, sizeof(servaddr));
    
    	// 监听连接请求,最大等待队列长度为 10
    	nlisten(listenfd, 10);
    
    	// 创建 epoll 实例,用于事件管理
    	int epfd = nepoll_create(1); // event poll
    
    	// 初始化要监听的事件,关注可读事件
    	struct epoll_event ev, events[128];
    	ev.events = EPOLLIN;
    	ev.data.fd = listenfd;
    
    	// 将监听套接字添加到 epoll 实例中
    	nepoll_ctl(epfd, EPOLL_CTL_ADD, listenfd, &ev);
    	
    	char buff[BUFFER_SIZE] = {0};
    	while(1) {
    
    		// 使用 epoll 等待就绪事件
    		int nready = nepoll_wait(epfd, events, 128, 5);
    		if (nready < 0) continue;
    		
    		int i = 0;
    		for (i = 0;i < nready;i ++) {
    
    			if (listenfd == events[i].data.fd) {
    				// 如果监听套接字有事件,表示有新的客户端连接请求
    
    				struct sockaddr_in client;
    				socklen_t len = sizeof(client);
    
    				// 接受客户端连接请求,创建新的连接套接字
    				int connfd = naccept(listenfd, (struct sockaddr*)&client, &len);
    
    				// 初始化要监听的事件,关注可读事件
    				struct epoll_event ev;
    				ev.events = EPOLLIN;
    				ev.data.fd = connfd;
    
    				// 将连接套接字添加到 epoll 实例中
    				nepoll_ctl(epfd, EPOLL_CTL_ADD, connfd, &ev);
    
    			} else { // clientfd
    
    				int connfd = events[i].data.fd;
    				
    				// 接收客户端发送的数据
    				int n = nrecv(connfd, buff, BUFFER_SIZE, 0); // 阻塞
    
    				if (n > 0) {
    					// 如果成功接收数据,打印并回传给客户端
    					printf("recv: %s\n", buff);
    					nsend(connfd, buff, n, 0);
    
    				} else {
    
    					// 如果连接断开,从 epoll 实例中删除连接套接字并关闭连接
    					nepoll_ctl(epfd, EPOLL_CTL_DEL, connfd, NULL);
    					nclose(connfd);
    					
    				} 
    
    			}
    
    		}
    	}
    
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 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

    这次更新完,代码就有点多了,不在这里展示了,完整链接,方便自己以后回顾

    结果

  • 相关阅读:
    SAP Scripting Tracker基本使用技巧
    2023年四川省网络与信息安全技能大赛初赛 团队赛 Writeup
    每周只需几分钟轻松记录初始金额
    跟我学UDS(ISO14229) ———— 0x29(Authentication)
    0020__如何获取windows系统的串口列表
    Dreambooth工作原理
    香草酸豌豆白蛋白1b纳米粒Vanillic Acid-PA1b|汉黄芩苷牛血清白蛋白纳米粒Wogonoside-BSA|科研试剂
    诚迈科技旗下智达诚远亮相2023世界新汽车技术合作生态展
    DETR:End to End Object Detection with Transformers
    【无标题】
  • 原文地址:https://blog.csdn.net/Phoenix_zxk/article/details/133165256