首页 > 代码库 > lwip socket探秘之accept
lwip socket探秘之accept
一个基本的socket建立顺序是
Server端:
- socket()
- bind()
- listen()
- accept()
- recv()
Client端:
- socket()
- connect()
- send()
本文着重介绍Server端的accept()过程。
上一篇我们已经分析了listen()过程,listen()过程新建了pcb并把它放到了tcp_listen_pcbs这个链表里。
接下来,Client端通过Server绑定的地址和端口号(通过bind绑定),给Server发包。Server收到了Client过来的TCP包后,如何记住这个Client,并且接下来会做什么呢?这些就是这篇小文分析的内容。
首先Server端TCP层接到了Client来的TCP segment。我们从lwip中TCP层rx的入口开始讲起。
tcp_input()是TCP层rx数据的入口,如下面代码段红色注释部分所示。
注意传进来的参数p,虽然这是TCP层入口,但它内部的p->payload是没有剥离ip头的,原因就是,TCP层处理socket时,是需要remote端的ip的。这一点从tcp_input()函数内部就能看出来。
代码段如下:
1 /** 2 * The initial input processing of TCP. It verifies the TCP header, demultiplexes 3 * the segment between the PCBs and passes it on to tcp_process(), which implements 4 * the TCP finite state machine. This function is called by the IP layer (in 5 * ip_input()). 6 * 7 * @param p received TCP segment to process (p->payload pointing to the IP header) 8 * @param inp network interface on which this segment was received 9 */ 10 void 11 tcp_input(struct pbuf *p, struct netif *inp) 12 { 13 struct tcp_pcb *pcb, *prev; 14 struct tcp_pcb_listen *lpcb; 15 u8_t hdrlen; 16 err_t err; 17 18 PERF_START; 19 20 TCP_STATS_INC(tcp.recv); 21 snmp_inc_tcpinsegs(); 22 23 iphdr = p->payload; // 得到了IP header 24 tcphdr = (struct tcp_hdr *)((u8_t *)p->payload + IPH_HL(iphdr) * 4); // 得到了TCP header 25 26 ............ 27 28 /* Convert fields in TCP header to host byte order. */ 29 tcphdr->src = http://www.mamicode.com/ntohs(tcphdr->src); // 把tcp header的一些内容从网络字节序转成主机字节序。可以猜测ip header已经在ip层被转过了。 30 tcphdr->dest = ntohs(tcphdr->dest); 31 seqno = tcphdr->seqno = ntohl(tcphdr->seqno); 32 ackno = tcphdr->ackno = ntohl(tcphdr->ackno); 33 tcphdr->wnd = ntohs(tcphdr->wnd); 34 35 .................. 36 37 //下面出现了3个pcb链表,分别是tcp_active_pcbs、tcp_tw_pcbs和tcp_listen_pcbs。 38 for(pcb = tcp_active_pcbs; pcb != NULL; pcb = pcb->next) { 39 LWIP_ASSERT("tcp_input: active pcb->state != CLOSED", pcb->state != CLOSED); 40 LWIP_ASSERT("tcp_input: active pcb->state != TIME-WAIT", pcb->state != TIME_WAIT); 41 LWIP_ASSERT("tcp_input: active pcb->state != LISTEN", pcb->state != LISTEN); 42 if (pcb->remote_port == tcphdr->src && 43 pcb->local_port == tcphdr->dest && 44 ip_addr_cmp(&(pcb->remote_ip), &(iphdr->src)) && 45 ip_addr_cmp(&(pcb->local_ip), &(iphdr->dest))) { 46 47 /* Move this PCB to the front of the list so that subsequent 48 lookups will be faster (we exploit locality in TCP segment 49 arrivals). */ 50 LWIP_ASSERT("tcp_input: pcb->next != pcb (before cache)", pcb->next != pcb); 51 if (prev != NULL) { 52 prev->next = pcb->next; 53 pcb->next = tcp_active_pcbs; 54 tcp_active_pcbs = pcb; 55 } 56 LWIP_ASSERT("tcp_input: pcb->next != pcb (after cache)", pcb->next != pcb); 57 break; 58 } 59 prev = pcb; 60 } 61 62 if (pcb == NULL) { 63 /* If it did not go to an active connection, we check the connections 64 in the TIME-WAIT state. */ 65 for(pcb = tcp_tw_pcbs; pcb != NULL; pcb = pcb->next) { 66 LWIP_ASSERT("tcp_input: TIME-WAIT pcb->state == TIME-WAIT", pcb->state == TIME_WAIT); 67 if (pcb->remote_port == tcphdr->src && 68 pcb->local_port == tcphdr->dest && 69 ip_addr_cmp(&(pcb->remote_ip), &(iphdr->src)) && 70 ip_addr_cmp(&(pcb->local_ip), &(iphdr->dest))) { 71 /* We don‘t really care enough to move this PCB to the front 72 of the list since we are not very likely to receive that 73 many segments for connections in TIME-WAIT. */ 74 LWIP_DEBUGF(TCP_INPUT_DEBUG, ("tcp_input: packed for TIME_WAITing connection.\n")); 75 tcp_timewait_input(pcb); 76 pbuf_free(p); 77 return; 78 } 79 } 80 81 //我们暂时只关心tcp_listen_pcbs这个链表。这个链表包含了所有处在listen状态的pcb。一个listen状态的pcb是什么时候把自己注册进tcp_listen_pcbs这个链表的?这个待会讲到。 82 /* Finally, if we still did not get a match, we check all PCBs that 83 are LISTENing for incoming connections. */ 84 prev = NULL; 85 for(lpcb = tcp_listen_pcbs.listen_pcbs; lpcb != NULL; lpcb = lpcb->next) { 86 if ((ip_addr_isany(&(lpcb->local_ip)) || // 如果pcb->local_ip是0或者NULL,意味着接收任何ip的连接请求 87 ip_addr_cmp(&(lpcb->local_ip), &(iphdr->dest))) && // 或者pcb->local_ip和接收到的tcp segment的ip地址一致,并且pcb->local_port和接收到的tcp segment的port号也一致,认为找到了之前注册的、并且是remote client发过来的这个tcp segment的目标的pcb。 88 lpcb->local_port == tcphdr->dest) { 89 /* Move this PCB to the front of the list so that subsequent 90 lookups will be faster (we exploit locality in TCP segment 91 arrivals). */ 92 if (prev != NULL) { 93 ((struct tcp_pcb_listen *)prev)->next = lpcb->next; 94 /* our successor is the remainder of the listening list */ 95 lpcb->next = tcp_listen_pcbs.listen_pcbs; 96 /* put this listening pcb at the head of the listening list */ 97 tcp_listen_pcbs.listen_pcbs = lpcb; 98 } 99 100 LWIP_DEBUGF(TCP_INPUT_DEBUG, ("tcp_input: packed for LISTENing connection.\n"));101 tcp_listen_input(lpcb); // 得到server正在listen状态的pcb后,用刚收到的remote tcp segment更新pcb,马上会分析这个函数102 pbuf_free(p);103 return;104 }105 prev = (struct tcp_pcb *)lpcb;106 }107 }
tcp_listen_input()如下:
1 /** 2 * Called by tcp_input() when a segment arrives for a listening 3 * connection (from tcp_input()). 4 * 5 * @param pcb the tcp_pcb_listen for which a segment arrived 6 * @return ERR_OK if the segment was processed 7 * another err_t on error 8 * 9 * @note the return value is not (yet?) used in tcp_input()10 * @note the segment which arrived is saved in global variables, therefore only the pcb11 * involved is passed as a parameter to this function12 */13 static err_t14 tcp_listen_input(struct tcp_pcb_listen *pcb)15 {16 struct tcp_pcb *npcb;17 err_t rc;18 19 /* In the LISTEN state, we check for incoming SYN segments,20 creates a new PCB, and responds with a SYN|ACK. */21 if (flags & TCP_ACK) {22 /* For incoming segments with the ACK flag set, respond with a23 RST. */24 LWIP_DEBUGF(TCP_RST_DEBUG, ("tcp_listen_input: ACK in LISTEN, sending reset\n"));25 tcp_rst(ackno + 1, seqno + tcplen,26 &(iphdr->dest), &(iphdr->src),27 tcphdr->dest, tcphdr->src);28 } else if (flags & TCP_SYN) {29 LWIP_DEBUGF(TCP_DEBUG, ("TCP connection request %"U16_F" -> %"U16_F".\n", tcphdr->src, tcphdr->dest));30 #if TCP_LISTEN_BACKLOG31 if (pcb->accepts_pending >= pcb->backlog) {32 LWIP_DEBUGF(TCP_DEBUG, ("tcp_listen_input: listen backlog exceeded for port %"U16_F"\n", tcphdr->dest));33 return ERR_ABRT;34 }35 #endif /* TCP_LISTEN_BACKLOG */36 npcb = tcp_alloc(pcb->prio); // allocate一个新的pcb,待会会放入active pcb链表37 /* If a new PCB could not be created (probably due to lack of memory),38 we don‘t do anything, but rely on the sender will retransmit the39 SYN at a time when we have more memory available. */40 if (npcb == NULL) {41 LWIP_DEBUGF(TCP_DEBUG, ("tcp_listen_input: could not allocate PCB\n"));42 TCP_STATS_INC(tcp.memerr);43 return ERR_MEM;44 }45 #if TCP_LISTEN_BACKLOG46 pcb->accepts_pending++;47 #endif /* TCP_LISTEN_BACKLOG */48 /* Set up the new PCB. */49 ip_addr_set(&(npcb->local_ip), &(iphdr->dest)); // pcb里存好server端自己的ip50 npcb->local_port = pcb->local_port; // pcb里存好server端自己的port号51 ip_addr_set(&(npcb->remote_ip), &(iphdr->src)); // pcb里指定client端的ip52 npcb->remote_port = tcphdr->src; // pcb里指定client端的port号53 npcb->state = SYN_RCVD; // pcb的状态变成了SYN_RCVD54 npcb->rcv_nxt = seqno + 1;55 npcb->rcv_ann_right_edge = npcb->rcv_nxt;56 npcb->snd_wnd = tcphdr->wnd;57 npcb->ssthresh = npcb->snd_wnd;58 npcb->snd_wl1 = seqno - 1;/* initialise to seqno-1 to force window update */59 npcb->callback_arg = pcb->callback_arg;60 #if LWIP_CALLBACK_API61 npcb->accept = pcb->accept;62 #endif /* LWIP_CALLBACK_API */63 /* inherit socket options */64 npcb->so_options = pcb->so_options & (SOF_DEBUG|SOF_DONTROUTE|SOF_KEEPALIVE|SOF_OOBINLINE|SOF_LINGER);65 /* Register the new PCB so that we can begin receiving segments66 for it. */67 TCP_REG(&tcp_active_pcbs, npcb); // 把新pcb加入active pcbs链表,以后这个pcb专门为server端与remote ip对应的这个client端之间的通信服务68 69 /* Parse any options in the SYN. */70 tcp_parseopt(npcb);71 #if TCP_CALCULATE_EFF_SEND_MSS72 npcb->mss = tcp_eff_send_mss(npcb->mss, &(npcb->remote_ip));73 #endif /* TCP_CALCULATE_EFF_SEND_MSS */74 75 snmp_inc_tcppassiveopens();76 77 /* Send a SYN|ACK together with the MSS option. */78 rc = tcp_enqueue(npcb, NULL, 0, TCP_SYN | TCP_ACK, 0, TF_SEG_OPTS_MSS // tx一个SYN|ACK79 #if LWIP_TCP_TIMESTAMPS80 /* and maybe include the TIMESTAMP option */81 | (npcb->flags & TF_TIMESTAMP ? TF_SEG_OPTS_TS : 0)82 #endif83 );84 if (rc != ERR_OK) {85 tcp_abandon(npcb, 0);86 return rc;87 }88 return tcp_output(npcb);89 }90 return ERR_OK;91 }
至此,产生了一个新pcb,这个pcb处在SYN_RCVD状态。这个pcb被加入了active pcbs链表。
如果刚才的client又发送了tcp segment过来,那么接收的流程又跟上面讲的有所不同。还是从tcp_input开始分析:
1 void 2 tcp_input(struct pbuf *p, struct netif *inp) 3 { 4 struct tcp_pcb *pcb, *prev; 5 struct tcp_pcb_listen *lpcb; 6 u8_t hdrlen; 7 err_t err; 8 9 PERF_START;10 11 TCP_STATS_INC(tcp.recv);12 snmp_inc_tcpinsegs();13 14 iphdr = p->payload; // 得到了IP header15 tcphdr = (struct tcp_hdr *)((u8_t *)p->payload + IPH_HL(iphdr) * 4); // 得到了TCP header16 17 ............18 19 /* Convert fields in TCP header to host byte order. */20 tcphdr->src = http://www.mamicode.com/ntohs(tcphdr->src); // 把tcp header的一些内容从网络字节序转成主机字节序。可以猜测ip header已经在ip层被转过了。21 tcphdr->dest = ntohs(tcphdr->dest);22 seqno = tcphdr->seqno = ntohl(tcphdr->seqno);23 ackno = tcphdr->ackno = ntohl(tcphdr->ackno);24 tcphdr->wnd = ntohs(tcphdr->wnd);25 26 ..................27 28 //下面出现了3个pcb链表,分别是tcp_active_pcbs、tcp_tw_pcbs和tcp_listen_pcbs。29 // 这一次,tcp segment对应的client-server专属pcb能够在 tcp_active_pcbs链表里找到。30 for(pcb = tcp_active_pcbs; pcb != NULL; pcb = pcb->next) {31 LWIP_ASSERT("tcp_input: active pcb->state != CLOSED", pcb->state != CLOSED);32 LWIP_ASSERT("tcp_input: active pcb->state != TIME-WAIT", pcb->state != TIME_WAIT);33 LWIP_ASSERT("tcp_input: active pcb->state != LISTEN", pcb->state != LISTEN);34 if (pcb->remote_port == tcphdr->src && // 这个tcp segment对应的client和server信息都能和某个pcb一致,则找到了这个pcb35 pcb->local_port == tcphdr->dest &&36 ip_addr_cmp(&(pcb->remote_ip), &(iphdr->src)) &&37 ip_addr_cmp(&(pcb->local_ip), &(iphdr->dest))) {38 39 /* Move this PCB to the front of the list so that subsequent40 lookups will be faster (we exploit locality in TCP segment41 arrivals). */42 LWIP_ASSERT("tcp_input: pcb->next != pcb (before cache)", pcb->next != pcb);43 if (prev != NULL) {44 prev->next = pcb->next;45 pcb->next = tcp_active_pcbs;46 tcp_active_pcbs = pcb;47 }48 LWIP_ASSERT("tcp_input: pcb->next != pcb (after cache)", pcb->next != pcb);49 break;50 }51 prev = pcb;52 }53 54 .............55 56 if (pcb != NULL) {57 err = tcp_process(pcb);58 }
tcp_process():
1 static err_t 2 tcp_process(struct tcp_pcb *pcb) 3 { 4 .............. 5 case SYN_RCVD: 6 if (flags & TCP_ACK) { 7 /* expected ACK number? */ 8 if (TCP_SEQ_BETWEEN(ackno, pcb->lastack+1, pcb->snd_nxt)) { 9 u16_t old_cwnd;10 pcb->state = ESTABLISHED;11 LWIP_DEBUGF(TCP_DEBUG, ("TCP connection established %"U16_F" -> %"U16_F".\n", inseg.tcphdr->src, inseg.tcphdr->dest));12 #if LWIP_CALLBACK_API13 LWIP_ASSERT("pcb->accept != NULL", pcb->accept != NULL);14 #endif15 /* Call the accept function. */16 TCP_EVENT_ACCEPT(pcb, ERR_OK, err); // 这里会调到accept_function函数17 if (err != ERR_OK) {18 /* If the accept function returns with an error, we abort19 * the connection. */20 tcp_abort(pcb);21 return ERR_ABRT;22 }23 old_cwnd = pcb->cwnd;24 /* If there was any data contained within this ACK,25 * we‘d better pass it on to the application as well. */26 tcp_receive(pcb);27 28 /* Prevent ACK for SYN to generate a sent event */29 if (pcb->acked != 0) {30 pcb->acked--;31 }32 33 pcb->cwnd = ((old_cwnd == 1) ? (pcb->mss * 2) : pcb->mss);34 35 if (recv_flags & TF_GOT_FIN) {36 tcp_ack_now(pcb);37 pcb->state = CLOSE_WAIT;38 }39 }40 /* incorrect ACK number */41 else {42 /* send RST */43 tcp_rst(ackno, seqno + tcplen, &(iphdr->dest), &(iphdr->src),44 tcphdr->dest, tcphdr->src);45 }46 } else if ((flags & TCP_SYN) && (seqno == pcb->rcv_nxt - 1)) {47 /* Looks like another copy of the SYN - retransmit our SYN-ACK */48 tcp_rexmit(pcb);49 }50 break;51 ....................52 }
1 /** 2 * Accept callback function for TCP netconns. 3 * Allocates a new netconn and posts that to conn->acceptmbox. 4 * 5 * @see tcp.h (struct tcp_pcb_listen.accept) for parameters and return value 6 */ 7 // 注意accept_function的参数是 8 // arg:server端负责listen的那个sock的sock->conn, 9 // newpcb:listen到tcp segment后为特定的client-server创建的pcb10 // err:err11 static err_t12 accept_function(void *arg, struct tcp_pcb *newpcb, err_t err)13 {14 struct netconn *newconn;15 struct netconn *conn;16 17 #if API_MSG_DEBUG18 #if TCP_DEBUG19 tcp_debug_print_state(newpcb->state);20 #endif /* TCP_DEBUG */21 #endif /* API_MSG_DEBUG */22 conn = (struct netconn *)arg;23 24 LWIP_ERROR("accept_function: invalid conn->acceptmbox",25 conn->acceptmbox != SYS_MBOX_NULL, return ERR_VAL;);26 27 /* We have to set the callback here even though28 * the new socket is unknown. conn->socket is marked as -1. */29 newconn = netconn_alloc(conn->type, conn->callback); // 新建一个netconn30 if (newconn == NULL) {31 return ERR_MEM;32 }33 newconn->pcb.tcp = newpcb;34 setup_tcp(newconn); // 这个函数很重要,我们要记住它把newconn和newpcb绑定起来了35 newconn->err = err;36 37 if (sys_mbox_trypost(conn->acceptmbox, newconn) != ERR_OK) { // 把newconn丢到了旧conn(即负责listen的pcb的conn)的acceptmbox这个mailbox上38 /* When returning != ERR_OK, the connection is aborted in tcp_process(),39 so do nothing here! */40 newconn->pcb.tcp = NULL;41 netconn_free(newconn);42 return ERR_MEM;43 } else {44 /* Register event with callback */45 API_EVENT(conn, NETCONN_EVT_RCVPLUS, 0);46 }47 48 return ERR_OK;49 }
现在我们已知一个新的newconn产生了,并且它放在负责listen的pcb的conn的acceptmbox里。那么谁来取走这个newconn呢?
就是lwip_accept()函数。
lwip_accept()函数就是用户使用socket过程中调用的accept()。
1 /* Below this, the well-known socket functions are implemented. 2 * Use google.com or opengroup.org to get a good description :-) 3 * 4 * Exceptions are documented! 5 */ 6 7 int 8 lwip_accept(int s, struct sockaddr *addr, socklen_t *addrlen) // 这里的s是负责listen的socket 9 {10 struct lwip_socket *sock, *nsock;11 struct netconn *newconn;12 struct ip_addr naddr;13 u16_t port;14 int newsock;15 struct sockaddr_in sin;16 err_t err;17 18 LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_accept(%d)...\n", s));19 sock = get_socket(s); // 从用户可见的int型socket得到协议栈自己维护的socket descriptor20 if (!sock)21 return -1;22 23 if ((sock->flags & O_NONBLOCK) && (sock->rcvevent <= 0)) {24 LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_accept(%d): returning EWOULDBLOCK\n", s));25 sock_set_errno(sock, EWOULDBLOCK);26 return -1;27 }28 29 newconn = netconn_accept(sock->conn); // 传入的是listen socket指向的conn30 ..................31 }
我们先来看一下netconn_accept函数:
1 /** 2 * Accept a new connection on a TCP listening netconn. 3 * 4 * @param conn the TCP listen netconn 5 * @return the newly accepted netconn or NULL on timeout 6 */ 7 struct netconn * 8 netconn_accept(struct netconn *conn) 9 {10 struct netconn *newconn;11 12 .............13 14 #if LWIP_SO_RCVTIMEO15 if (sys_arch_mbox_fetch(conn->acceptmbox, (void *)&newconn, conn->recv_timeout) == SYS_ARCH_TIMEOUT) {16 newconn = NULL;17 } else18 #else19 sys_arch_mbox_fetch(conn->acceptmbox, (void *)&newconn, 0); // 这里从listen socket->conn的acceptmbox取出1个conn,当然就是socket用户在调用listen()后,listen()通过accept_function()函数放入的新conn,这是和listen socket->conn不同的新conn,这里名字是newconn20 #endif /* LWIP_SO_RCVTIMEO*/21 ..................22 23 return newconn;24 }
所以,netconn_accept取出了client端连接server后server为这一对peer生成的newconn。
我们接着回到lwip_accept()函数:
1 /* Below this, the well-known socket functions are implemented. 2 * Use google.com or opengroup.org to get a good description :-) 3 * 4 * Exceptions are documented! 5 */ 6 7 int 8 lwip_accept(int s, struct sockaddr *addr, socklen_t *addrlen) // 这里的s是负责listen的socket 9 {10 .................11 newconn = netconn_accept(sock->conn); // 传入的是listen socket指向的conn12 if (!newconn) {13 LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_accept(%d) failed, err=%d\n", s, sock->conn->err));14 sock_set_errno(sock, err_to_errno(sock->conn->err));15 return -1;16 }17 18 /* get the IP address and port of the remote host */19 err = netconn_peer(newconn, &naddr, &port); // 从newconn里得到client端ip addr和port号,实际是从newconn绑定的pcb里获得20 if (err != ERR_OK) {21 netconn_delete(newconn);22 sock_set_errno(sock, err_to_errno(err));23 return -1;24 }25 26 /* Note that POSIX only requires us to check addr is non-NULL. addrlen must27 * not be NULL if addr is valid.28 */29 if (NULL != addr) { // 如果实参addr地址不是NULL,就把client端的ip地址和port写入addr指向的地址,作为这个函数的返回值之一。但是很多时候这个addr参数用户都会设置为NULL,不需要这个信息。30 LWIP_ASSERT("addr valid but addrlen NULL", addrlen != NULL);31 memset(&sin, 0, sizeof(sin));32 sin.sin_len = sizeof(sin);33 sin.sin_family = AF_INET;34 sin.sin_port = htons(port);35 sin.sin_addr.s_addr = naddr.addr;36 37 if (*addrlen > sizeof(sin))38 *addrlen = sizeof(sin);39 40 MEMCPY(addr, &sin, *addrlen);41 }42 43 newsock = alloc_socket(newconn); // 对于一个client和server的peer,现在有了新的pcb、新的conn,还需要一个新的socket给用户用!并且这个函数还把newconn和socket绑定起来了,从新socket能得到newconn。44 if (newsock == -1) {45 netconn_delete(newconn);46 sock_set_errno(sock, ENFILE);47 return -1;48 }49 LWIP_ASSERT("invalid socket index", (newsock >= 0) && (newsock < NUM_SOCKETS));50 newconn->callback = event_callback;51 nsock = &sockets[newsock];52 LWIP_ASSERT("invalid socket pointer", nsock != NULL);53 54 sys_sem_wait(socksem);55 /* See event_callback: If data comes in right away after an accept, even56 * though the server task might not have created a new socket yet.57 * In that case, newconn->socket is counted down (newconn->socket--),58 * so nsock->rcvevent is >= 1 here!59 */60 nsock->rcvevent += -1 - newconn->socket;61 newconn->socket = newsock; // 从newconn能找到new socket62 sys_sem_signal(socksem);63 64 LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_accept(%d) returning new sock=%d addr=", s, newsock));65 ip_addr_debug_print(SOCKETS_DEBUG, &naddr);66 LWIP_DEBUGF(SOCKETS_DEBUG, (" port=%"U16_F"\n", port));67 68 sock_set_errno(sock, 0);69 return newsock;70 }
lwip_accept()函数返回了一个new socket,这个socket从listen socket而来,是server专为listen到的client准备的一个socket,可以认为是为这一对通路单独服务的server端socket。我们先把它叫做专属socket。
accept()过程结束,返回的socket用于接下来的recv()。
lwip socket探秘之accept
声明:以上内容来自用户投稿及互联网公开渠道收集整理发布,本网站不拥有所有权,未作人工编辑处理,也不承担相关法律责任,若内容有误或涉及侵权可进行投诉: 投诉/举报 工作人员会在5个工作日内联系你,一经查实,本站将立刻删除涉嫌侵权内容。