admin管理员组文章数量:1516870
代码示例
select_list
1.头文件
1 #pragma once
2
3 #include <stdio.h>
4 #include <stdlib.h>
5 #include <sys/types.h>
6 #include <sys/select.h>
7 #include <sys/time.h>
8 #include <sys/socket.h>
9 #include <strings.h>
10 #include <string.h>
11 #include <arpa/inet.h>
12 #include "list.h"
13
14 #define IP "192.168.2.150"
15 #define PORT 9999
16 #define SIZE 128
17
18 struct cli_info
19 {
20 struct list_head list;
21 int cli_fd;
22 };
2.client_delect.c
1 #include "net.h"
2
3 int main(void)
4 {
5 //1.创建套接字
6 int fd = socket(AF_INET,SOCK_STREAM,0);
7 if(fd<0){
8 perror("socket failed");
9 exit(1);
10 }
11
12
13 //2.初始服务器地址
14 struct sockaddr_in cli;
15 cli.sin_family = AF_INET;
16 cli.sin_port = htons(PORT);
17 cli.sin_addr.s_addr=inet_addr(IP);
18
19
20 //3.发送连接请求
21 if( connect(fd,(struct sockaddr*)&cli,sizeof(cli))<0 ){
22 perror("connect failed");
23 exit(1);
24 }
25
26
27 //4.写
28 char buf[SIZE];
29 while(1){
30 bzero(buf,SIZE);
31 printf("please input:\t");
32 fgets(buf,SIZE,stdin);
33 write(fd,buf,strlen(buf));
34 if(!strncmp(buf,"quit",4))
35 break;
36 }
37
38
39 //5.关闭
40 close(fd);
41
42
43 return 0 ;
44 }
3.server_select.c
1 #include "net.h"
2
3 int main(void)
4 {
5 char buf[SIZE];
6 int newfd = -1;
7
8
9 //*****定义初始头结点
10 struct cli_info head;
11 INIT_LIST_HEAD(&head.list);
12
13
14 //1.创建套接字
15 int fd = socket(AF_INET,SOCK_STREAM,0);
16 if(fd<0){
17 perror("socket failed");
18 exit(1);
19 }
20
21
22 //2.初始本地地址
23 struct sockaddr_in ser;
24 bzero(&ser,sizeof(ser));
25 ser.sin_family = AF_INET;
26 ser.sin_port = htons(PORT);
27 ser.sin_addr.s_addr=htonl(INADDR_ANY);
28
29
30 //3.绑定
31 if( bind(fd,(struct sockaddr*)&ser,sizeof(ser))<0 ){
32 perror("bind failed");
33 exit(1);
34 }
35
36
37 //4.监听
38 if( listen(fd,5)<0 ){
39 perror("listen failed");
40 exit(1);
41 }
42
43 int len = sizeof(ser);
44 int maxfd = -1;
45
46 //5.多路复用
47 fd_set read_fds; //定义读集合
48 FD_ZERO(&read_fds); //清空读集合
49 maxfd = fd; //最大描述符
50
51
52 //*****
53 struct cli_info *cur=NULL;
54 struct list_head *ptr,*q;
55
56
57 //6.加入监听处理 读集合
58 while(1){
59 FD_SET(0,&read_fds); //将标准输入加入读集合
60 FD_SET(fd,&read_fds); //将fd加入读集合
61 maxfd = fd; //表示最大读集合
62
63 #if 0
64 if(maxfd < newfd){
65 FD_SET(newfd,&read_fds);
66 maxfd=newfd;
67 }
68 #endif
69
70 //*****将文件描述符加入到读集合
71 list_for_each(ptr,&head.list){
72 cur = list_entry(ptr,struct cli_info,list);
73 FD_SET(cur->cli_fd,&read_fds);
74 if(maxfd<cur->cli_fd)
75 maxfd = cur->cli_fd;
76 }
77
78
79 int ret = select(maxfd+1,&read_fds,NULL,NULL,NULL);
80 if(ret<0){
81 perror("select failed");
82 exit(1);
83 }
84 else if(ret==0){
85 perror("time out");
86 exit(1);
87 }
88 else{
89 //6.1 判断输入端是否有相应
90 if(FD_ISSET(0,&read_fds)){
91 bzero(buf,SIZE);
92 fgets(buf,SIZE,stdin);
93 printf("shell :%s",buf);
94 }
95
96
97 //6.2 判断客户端是否有相应
98 if(FD_ISSET(fd,&read_fds)){
99 //*****给新节点申请空间
100 struct cli_info *temp=(struct cli_info*)malloc(sizeof(struct cli_info));
101 if(temp==NULL){
102 perror("malloc failed");
103 exit(1);
104 }
105 //接收
106 newfd=accept(fd,(struct sockaddr*)&ser,&len);
107 if(newfd<0){
108 perror("accept failed");
109 exit(1);
110 }
111 printf("client connect succsee : ip=%s port=%d 用户上线啦 ^.^ ...\n",inet_ntoa(ser.sin_addr),ntohs(ser.sin_port));
112 //*****将连接的新客户文件描述符加入到新节点
113 temp->cli_fd=newfd;
114 //*****把新节点插入到链表
115 list_add(&temp->list,&head.list);
116 }
117
118
119 //6.3 判断客户端是否有数据
120 list_for_each_safe(ptr,q,&head.list){
121 cur = list_entry(ptr,struct cli_info,list);
122 if(FD_ISSET(cur->cli_fd,&read_fds)){
123 bzero(buf,SIZE);
124 int val = read(cur->cli_fd,buf,SIZE);
125 if(val<0){
126 perror("read failed");
127 exit(1);
128 }
129 else if(val==0){
130 FD_CLR(cur->cli_fd,&read_fds);
131 list_del(&cur->list);
132 close(cur->cli_fd);
133 free(cur);
134 cur->cli_fd = -1;
135 }
136 else{
137 printf("client info :\nip=%s port=%d\ndata=%s\n",inet_ntoa(ser.sin_addr),ntohs(ser.sin_port),buf);
138 if(!strncmp(buf,"quit",4))
139 printf("client info :\nip=%s port=%d 用户下线啦 ^.^ ...\n",inet_ntoa(ser.sin_addr),ntohs(ser.sin_port));
140
141 }
142 }
143 }
144 }
145 }
146
147
148 return 0 ;
149 }
4.list.h
1 #ifndef _LINUX_LIST_H
2 #define _LINUX_LIST_H
3
4 /*
5 * Simple doubly linked list implementation.
6 *
7 * Some of the internal functions ("__xxx") are useful when
8 * manipulating whole lists rather than single entries, as
9 * sometimes we already know the next/prev entries and we can
10 * generate better code by using them directly rather than
11 * using the generic single-entry routines.
12 */
13
14 struct list_head{
15 struct list_head *next,*prev;
16 };
17
18 #define LIST_POISON1 ((void *) 0x00100100 + 0)
19 #define LIST_POISON2 ((void *) 0x00200200 + 0)
20
21 #define offsetof(TYPE, MEMBER) ((size_t) &((TYPE *)0)->MEMBER)
22 #define container_of(ptr, type, member) ({ \
23 const typeof( ((type *)0)->member ) *__mptr = (ptr); \
24 (type *)( (char *)__mptr - offsetof(type,member) );})
25 #define LIST_HEAD_INIT(name) { &(name), &(name) }
26
27 #define LIST_HEAD(name) \
28 struct list_head name = LIST_HEAD_INIT(name)
29
30 static inline void INIT_LIST_HEAD(struct list_head *list)
31 {
32 list->next = list;
33 list->prev = list;
34 }
35
36 /*
37 * Insert a new entry between two known consecutive entries.
38 *
39 * This is only for internal list manipulation where we know
40 * the prev/next entries already!
41 */
42 #ifndef CONFIG_DEBUG_LIST
43 static inline void __list_add(struct list_head *new,
44 struct list_head *prev,
45 struct list_head *next)
46 {
47 next->prev = new;
48 new->next = next;
49 new->prev = prev;
50 prev->next = new;
51 }
52 #else
53 extern void __list_add(struct list_head *new,
54 struct list_head *prev,
55 struct list_head *next);
56 #endif
57
58 /**
59 * list_add - add a new entry
60 * @new: new entry to be added
61 * @head: list head to add it after
62 *
63 * Insert a new entry after the specified head.
64 * This is good for implementing stacks.
65 */
66 static inline void list_add(struct list_head *new, struct list_head *head)
67 {
68 __list_add(new, head, head->next);
69 }
70
71
72 /**
73 * list_add_tail - add a new entry
74 * @new: new entry to be added
75 * @head: list head to add it before
76 *
77 * Insert a new entry before the specified head.
78 * This is useful for implementing queues.
79 */
80 static inline void list_add_tail(struct list_head *new, struct list_head *head)
81 {
82 __list_add(new, head->prev, head);
83 }
84
85 /*
86 * Delete a list entry by making the prev/next entries
87 * point to each other.
88 *
89 * This is only for internal list manipulation where we know
90 * the prev/next entries already!
91 */
92 static inline void __list_del(struct list_head * prev, struct list_head * next)
93 {
94 next->prev = prev;
95 prev->next = next;
96 }
97
98 /**
99 * list_del - deletes entry from list.
100 * @entry: the element to delete from the list.
101 * Note: list_empty() on entry does not return true after this, the entry is
102 * in an undefined state.
103 */
104 #ifndef CONFIG_DEBUG_LIST
105 static inline void __list_del_entry(struct list_head *entry)
106 {
107 __list_del(entry->prev, entry->next);
108 }
109
110 static inline void list_del(struct list_head *entry)
111 {
112 __list_del(entry->prev, entry->next);
113 entry->next = LIST_POISON1;
114 entry->prev = LIST_POISON2;
115 }
116 #else
117 extern void __list_del_entry(struct list_head *entry);
118 extern void list_del(struct list_head *entry);
119 #endif
120
121 /**
122 * list_replace - replace old entry by new one
123 * @old : the element to be replaced
124 * @new : the new element to insert
125 *
126 * If @old was empty, it will be overwritten.
127 */
128 static inline void list_replace(struct list_head *old,
129 struct list_head *new)
130 {
131 new->next = old->next;
132 new->next->prev = new;
133 new->prev = old->prev;
134 new->prev->next = new;
135 }
136
137 static inline void list_replace_init(struct list_head *old,
138 struct list_head *new)
139 {
140 list_replace(old, new);
141 INIT_LIST_HEAD(old);
142 }
143
144 /**
145 * list_del_init - deletes entry from list and reinitialize it.
146 * @entry: the element to delete from the list.
147 */
148 static inline void list_del_init(struct list_head *entry)
149 {
150 __list_del_entry(entry);
151 INIT_LIST_HEAD(entry);
152 }
153
154 /**
155 * list_move - delete from one list and add as another's head
156 * @list: the entry to move
157 * @head: the head that will precede our entry
158 */
159 static inline void list_move(struct list_head *list, struct list_head *head)
160 {
161 __list_del_entry(list);
162 list_add(list, head);
163 }
164
165 /**
166 * list_move_tail - delete from one list and add as another's tail
167 * @list: the entry to move
168 * @head: the head that will follow our entry
169 */
170 static inline void list_move_tail(struct list_head *list,
171 struct list_head *head)
172 {
173 __list_del_entry(list);
174 list_add_tail(list, head);
175 }
176
177 /**
178 * list_is_last - tests whether @list is the last entry in list @head
179 * @list: the entry to test
180 * @head: the head of the list
181 */
182 static inline int list_is_last(const struct list_head *list,
183 const struct list_head *head)
184 {
185 return list->next == head;
186 }
187
188 /**
189 * list_empty - tests whether a list is empty
190 * @head: the list to test.
191 */
192 static inline int list_empty(const struct list_head *head)
193 {
194 return head->next == head;
195 }
196
197 /**
198 * list_empty_careful - tests whether a list is empty and not being modified
199 * @head: the list to test
200 *
201 * Description:
202 * tests whether a list is empty _and_ checks that no other CPU might be
203 * in the process of modifying either member (next or prev)
204 *
205 * NOTE: using list_empty_careful() without synchronization
206 * can only be safe if the only activity that can happen
207 * to the list entry is list_del_init(). Eg. it cannot be used
208 * if another CPU could re-list_add() it.
209 */
210 static inline int list_empty_careful(const struct list_head *head)
211 {
212 struct list_head *next = head->next;
213 return (next == head) && (next == head->prev);
214 }
215
216 /**
217 * list_rotate_left - rotate the list to the left
218 * @head: the head of the list
219 */
220 static inline void list_rotate_left(struct list_head *head)
221 {
222 struct list_head *first;
223
224 if (!list_empty(head)) {
225 first = head->next;
226 list_move_tail(first, head);
227 }
228 }
229
230 /**
231 * list_is_singular - tests whether a list has just one entry.
232 * @head: the list to test.
233 */
234 static inline int list_is_singular(const struct list_head *head)
235 {
236 return !list_empty(head) && (head->next == head->prev);
237 }
238
239 static inline void __list_cut_position(struct list_head *list,
240 struct list_head *head, struct list_head *entry)
241 {
242 struct list_head *new_first = entry->next;
243 list->next = head->next;
244 list->next->prev = list;
245 list->prev = entry;
246 entry->next = list;
247 head->next = new_first;
248 new_first->prev = head;
249 }
250
251 /**
252 * list_cut_position - cut a list into two
253 * @list: a new list to add all removed entries
254 * @head: a list with entries
255 * @entry: an entry within head, could be the head itself
256 * and if so we won't cut the list
257 *
258 * This helper moves the initial part of @head, up to and
259 * including @entry, from @head to @list. You should
260 * pass on @entry an element you know is on @head. @list
261 * should be an empty list or a list you do not care about
262 * losing its data.
263 *
264 */
265 static inline void list_cut_position(struct list_head *list,
266 struct list_head *head, struct list_head *entry)
267 {
268 if (list_empty(head))
269 return;
270 if (list_is_singular(head) &&
271 (head->next != entry && head != entry))
272 return;
273 if (entry == head)
274 INIT_LIST_HEAD(list);
275 else
276 __list_cut_position(list, head, entry);
277 }
278
279 static inline void __list_splice(const struct list_head *list,
280 struct list_head *prev,
281 struct list_head *next)
282 {
283 struct list_head *first = list->next;
284 struct list_head *last = list->prev;
285
286 first->prev = prev;
287 prev->next = first;
288
289 last->next = next;
290 next->prev = last;
291 }
292
293 /**
294 * list_splice - join two lists, this is designed for stacks
295 * @list: the new list to add.
296 * @head: the place to add it in the first list.
297 */
298 static inline void list_splice(const struct list_head *list,
299 struct list_head *head)
300 {
301 if (!list_empty(list))
302 __list_splice(list, head, head->next);
303 }
304
305 /**
306 * list_splice_tail - join two lists, each list being a queue
307 * @list: the new list to add.
308 * @head: the place to add it in the first list.
309 */
310 static inline void list_splice_tail(struct list_head *list,
311 struct list_head *head)
312 {
313 if (!list_empty(list))
314 __list_splice(list, head->prev, head);
315 }
316
317 /**
318 * list_splice_init - join two lists and reinitialise the emptied list.
319 * @list: the new list to add.
320 * @head: the place to add it in the first list.
321 *
322 * The list at @list is reinitialised
323 */
324 static inline void list_splice_init(struct list_head *list,
325 struct list_head *head)
326 {
327 if (!list_empty(list)) {
328 __list_splice(list, head, head->next);
329 INIT_LIST_HEAD(list);
330 }
331 }
332
333 /**
334 * list_splice_tail_init - join two lists and reinitialise the emptied list
335 * @list: the new list to add.
336 * @head: the place to add it in the first list.
337 *
338 * Each of the lists is a queue.
339 * The list at @list is reinitialised
340 */
341 static inline void list_splice_tail_init(struct list_head *list,
342 struct list_head *head)
343 {
344 if (!list_empty(list)) {
345 __list_splice(list, head->prev, head);
346 INIT_LIST_HEAD(list);
347 }
348 }
349
350 /**
351 * list_entry - get the struct for this entry
352 * @ptr: the &struct list_head pointer.
353 * @type: the type of the struct this is embedded in.
354 * @member: the name of the list_struct within the struct.
355 */
356 #define list_entry(ptr, type, member) \
357 container_of(ptr, type, member)
358
359 /**
360 * list_first_entry - get the first element from a list
361 * @ptr: the list head to take the element from.
362 * @type: the type of the struct this is embedded in.
363 * @member: the name of the list_struct within the struct.
364 *
365 * Note, that list is expected to be not empty.
366 */
367 #define list_first_entry(ptr, type, member) \
368 list_entry((ptr)->next, type, member)
369
370 /**
371 * list_for_each - iterate over a list
372 * @pos: the &struct list_head to use as a loop cursor.
373 * @head: the head for your list.
374 */
375 #define list_for_each(pos, head) \
376 for (pos = (head)->next; pos != (head); pos = pos->next)
377
378 /**
379 * __list_for_each - iterate over a list
380 * @pos: the &struct list_head to use as a loop cursor.
381 * @head: the head for your list.
382 *
383 * This variant doesn't differ from list_for_each() any more.
384 * We don't do prefetching in either case.
385 */
386 #define __list_for_each(pos, head) \
387 for (pos = (head)->next; pos != (head); pos = pos->next)
388
389 /**
390 * list_for_each_prev - iterate over a list backwards
391 * @pos: the &struct list_head to use as a loop cursor.
392 * @head: the head for your list.
393 */
394 #define list_for_each_prev(pos, head) \
395 for (pos = (head)->prev; pos != (head); pos = pos->prev)
396
397 /**
398 * list_for_each_safe - iterate over a list safe against removal of list entry
399 * @pos: the &struct list_head to use as a loop cursor.
400 * @n: another &struct list_head to use as temporary storage
401 * @head: the head for your list.
402 */
403 #define list_for_each_safe(pos, n, head) \
404 for (pos = (head)->next, n = pos->next; pos != (head); \
405 pos = n, n = pos->next)
406
407 /**
408 * list_for_each_prev_safe - iterate over a list backwards safe against removal of list entry
409 * @pos: the &struct list_head to use as a loop cursor.
410 * @n: another &struct list_head to use as temporary storage
411 * @head: the head for your list.
412 */
413 #define list_for_each_prev_safe(pos, n, head) \
414 for (pos = (head)->prev, n = pos->prev; \
415 pos != (head); \
416 pos = n, n = pos->prev)
417
418 /**
419 * list_for_each_entry - iterate over list of given type
420 * @pos: the type * to use as a loop cursor.
421 * @head: the head for your list.
422 * @member: the name of the list_struct within the struct.
423 */
424 #define list_for_each_entry(pos, head, member) \
425 for (pos = list_entry((head)->next, typeof(*pos), member); \
426 &pos->member != (head); \
427 pos = list_entry(pos->member.next, typeof(*pos), member))
428
429 /**
430 * list_for_each_entry_reverse - iterate backwards over list of given type.
431 * @pos: the type * to use as a loop cursor.
432 * @head: the head for your list.
433 * @member: the name of the list_struct within the struct.
434 */
435 #define list_for_each_entry_reverse(pos, head, member) \
436 for (pos = list_entry((head)->prev, typeof(*pos), member); \
437 &pos->member != (head); \
438 pos = list_entry(pos->member.prev, typeof(*pos), member))
439
440 /**
441 * list_prepare_entry - prepare a pos entry for use in list_for_each_entry_continue()
442 * @pos: the type * to use as a start point
443 * @head: the head of the list
444 * @member: the name of the list_struct within the struct.
445 *
446 * Prepares a pos entry for use as a start point in list_for_each_entry_continue().
447 */
448 #define list_prepare_entry(pos, head, member) \
449 ((pos) ? : list_entry(head, typeof(*pos), member))
450
451 /**
452 * list_for_each_entry_continue - continue iteration over list of given type
453 * @pos: the type * to use as a loop cursor.
454 * @head: the head for your list.
455 * @member: the name of the list_struct within the struct.
456 *
457 * Continue to iterate over list of given type, continuing after
458 * the current position.
459 */
460 #define list_for_each_entry_continue(pos, head, member) \
461 for (pos = list_entry(pos->member.next, typeof(*pos), member); \
462 &pos->member != (head); \
463 pos = list_entry(pos->member.next, typeof(*pos), member))
464
465 /**
466 * list_for_each_entry_continue_reverse - iterate backwards from the given point
467 * @pos: the type * to use as a loop cursor.
468 * @head: the head for your list.
469 * @member: the name of the list_struct within the struct.
470 *
471 * Start to iterate over list of given type backwards, continuing after
472 * the current position.
473 */
474 #define list_for_each_entry_continue_reverse(pos, head, member) \
475 for (pos = list_entry(pos->member.prev, typeof(*pos), member); \
476 &pos->member != (head); \
477 pos = list_entry(pos->member.prev, typeof(*pos), member))
478
479 /**
480 * list_for_each_entry_from - iterate over list of given type from the current point
481 * @pos: the type * to use as a loop cursor.
482 * @head: the head for your list.
483 * @member: the name of the list_struct within the struct.
484 *
485 * Iterate over list of given type, continuing from current position.
486 */
487 #define list_for_each_entry_from(pos, head, member) \
488 for (; &pos->member != (head); \
489 pos = list_entry(pos->member.next, typeof(*pos), member))
490
491 /**
492 * list_for_each_entry_safe - iterate over list of given type safe against removal of list entry
493 * @pos: the type * to use as a loop cursor.
494 * @n: another type * to use as temporary storage
495 * @head: the head for your list.
496 * @member: the name of the list_struct within the struct.
497 */
498 #define list_for_each_entry_safe(pos, n, head, member) \
499 for (pos = list_entry((head)->next, typeof(*pos), member), \
500 n = list_entry(pos->member.next, typeof(*pos), member); \
501 &pos->member != (head); \
502 pos = n, n = list_entry(n->member.next, typeof(*n), member))
503
504 /**
505 * list_for_each_entry_safe_continue - continue list iteration safe against removal
506 * @pos: the type * to use as a loop cursor.
507 * @n: another type * to use as temporary storage
508 * @head: the head for your list.
509 * @member: the name of the list_struct within the struct.
510 *
511 * Iterate over list of given type, continuing after current point,
512 * safe against removal of list entry.
513 */
514 #define list_for_each_entry_safe_continue(pos, n, head, member) \
515 for (pos = list_entry(pos->member.next, typeof(*pos), member), \
516 n = list_entry(pos->member.next, typeof(*pos), member); \
517 &pos->member != (head); \
518 pos = n, n = list_entry(n->member.next, typeof(*n), member))
519
520 /**
521 * list_for_each_entry_safe_from - iterate over list from current point safe against removal
522 * @pos: the type * to use as a loop cursor.
523 * @n: another type * to use as temporary storage
524 * @head: the head for your list.
525 * @member: the name of the list_struct within the struct.
526 *
527 * Iterate over list of given type from current point, safe against
528 * removal of list entry.
529 */
530 #define list_for_each_entry_safe_from(pos, n, head, member) \
531 for (n = list_entry(pos->member.next, typeof(*pos), member); \
532 &pos->member != (head); \
533 pos = n, n = list_entry(n->member.next, typeof(*n), member))
534
535 /**
536 * list_for_each_entry_safe_reverse - iterate backwards over list safe against removal
537 * @pos: the type * to use as a loop cursor.
538 * @n: another type * to use as temporary storage
539 * @head: the head for your list.
540 * @member: the name of the list_struct within the struct.
541 *
542 * Iterate backwards over list of given type, safe against removal
543 * of list entry.
544 */
545 #define list_for_each_entry_safe_reverse(pos, n, head, member) \
546 for (pos = list_entry((head)->prev, typeof(*pos), member), \
547 n = list_entry(pos->member.prev, typeof(*pos), member); \
548 &pos->member != (head); \
549 pos = n, n = list_entry(n->member.prev, typeof(*n), member))
550
551 /**
552 * list_safe_reset_next - reset a stale list_for_each_entry_safe loop
553 * @pos: the loop cursor used in the list_for_each_entry_safe loop
554 * @n: temporary storage used in list_for_each_entry_safe
555 * @member: the name of the list_struct within the struct.
556 *
557 * list_safe_reset_next is not safe to use in general if the list may be
558 * modified concurrently (eg. the lock is dropped in the loop body). An
559 * exception to this is if the cursor element (pos) is pinned in the list,
560 * and list_safe_reset_next is called after re-taking the lock and before
561 * completing the current iteration of the loop body.
562 */
563 #define list_safe_reset_next(pos, n, member) \
564 n = list_entry(pos->member.next, typeof(*pos), member)
565
566 /*
567 * Double linked lists with a single pointer list head.
568 * Mostly useful for hash tables where the two pointer list head is
569 * too wasteful.
570 * You lose the ability to access the tail in O(1).
571 */
572 #endif
测试:
success !
转载于:.html
本文标签: 代码示例
版权声明:本文标题:代码示例 内容由网友自发贡献,该文观点仅代表作者本人, 转载请联系作者并注明出处:https://www.betaflare.com/biancheng/1701753925a474846.html, 本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌抄袭侵权/违法违规的内容,一经查实,本站将立刻删除。


发表评论