前面說了TCP客戶端通信,這一篇來講說單片機做爲服務器的通信方法api
tcp客戶端和服務器的連接作大的不一樣在於服務器是不須要主動連接誰的,他只須要綁定在本身得一個特定的端口之上,等別人來鏈接就行了,先建立任務數組
//建立TCP服務器線程 //返回值:0 TCP服務器建立成功 // 其餘 TCP服務器建立失敗 INT8U tcp_server_init(void) { INT8U res; OS_CPU_SR cpu_sr; OS_ENTER_CRITICAL(); //關中斷 res = OSTaskCreate(tcp_server_thread,(void*)0,(OS_STK*)&TCPSERVER_TASK_STK[TCPSERVER_STK_SIZE-1],TCPSERVER_PRIO); //建立TCP服務器線程 OS_EXIT_CRITICAL(); //開中斷 return res; }
任務循環以下服務器
//tcp服務器任務 static void tcp_server_thread(void *arg) { OS_CPU_SR cpu_sr; u32 data_len = 0; struct pbuf *q; err_t err,recv_err; u8 remot_addr[4]; struct netconn *conn, *newconn; static ip_addr_t ipaddr; static u16_t port; struct netbuf *recvbuf; while(dhcpstatus != 2)//等待dhcp成功 { OSTimeDly(10); //printf("wait dhcp\r\n"); } LWIP_UNUSED_ARG(arg); printf("建立一個TCP連接\r\n"); conn = netconn_new(NETCONN_TCP); //建立一個TCP連接 netconn_bind(conn,IP_ADDR_ANY,TCP_SERVER_PORT); //綁定端口 8088號端口 netconn_listen(conn); //進入監聽模式 printf("進入監聽模式"); //這個地方阻塞以後會形成客戶端連不上,報錯爲the socket is marked as non blocking,後面研究 // conn->recv_timeout = 10; //禁止阻塞線程 等待10ms while (1) { err = netconn_accept(conn,&newconn); //接收鏈接請求 newconn->recv_timeout = 10; if (err == ERR_OK) //處理新鏈接的數據 { printf("處理新鏈接的數據"); netconn_getaddr(newconn,&ipaddr,&port,0); //獲取遠端IP地址和端口號 remot_addr[3] = (uint8_t)(ipaddr.addr >> 24); remot_addr[2] = (uint8_t)(ipaddr.addr>> 16); remot_addr[1] = (uint8_t)(ipaddr.addr >> 8); remot_addr[0] = (uint8_t)(ipaddr.addr); printf("主機%d.%d.%d.%d鏈接上服務器,主機端口號爲:%d\r\n",\ remot_addr[0], remot_addr[1],remot_addr[2],remot_addr[3],port); while(1) { if(keyValue == KEY_UP) { keyValue = 0; tcp_server_flag = LWIP_SEND_DATA; } if((tcp_server_flag & LWIP_SEND_DATA) == LWIP_SEND_DATA) //有數據要發送 { err = netconn_write(newconn ,tcp_server_sendbuf,strlen((char*)tcp_server_sendbuf),NETCONN_COPY); //發送tcp_server_sendbuf中的數據 if(err != ERR_OK) { printf("發送失敗\r\n"); } tcp_server_flag &= ~LWIP_SEND_DATA; } if((recv_err = netconn_recv(newconn,&recvbuf)) == ERR_OK) //接收到數據 { OS_ENTER_CRITICAL(); //關中斷 memset(tcp_server_recvbuf,0,TCP_SERVER_RX_BUFSIZE); //數據接收緩衝區清零 for(q=recvbuf->p;q!=NULL;q=q->next) //遍歷完整個pbuf鏈表 { //判斷要拷貝到TCP_SERVER_RX_BUFSIZE中的數據是否大於TCP_SERVER_RX_BUFSIZE的剩餘空間,若是大於 //的話就只拷貝TCP_SERVER_RX_BUFSIZE中剩餘長度的數據,不然的話就拷貝全部的數據 if(q->len > (TCP_SERVER_RX_BUFSIZE-data_len)) memcpy(tcp_server_recvbuf+data_len,q->payload,(TCP_SERVER_RX_BUFSIZE-data_len));//拷貝數據 else memcpy(tcp_server_recvbuf+data_len,q->payload,q->len); data_len += q->len; if(data_len > TCP_SERVER_RX_BUFSIZE) break; //超出TCP客戶端接收數組,跳出 } OS_EXIT_CRITICAL(); //開中斷 data_len=0; //複製完成後data_len要清零。 printf("%s\r\n",tcp_server_recvbuf); //經過串口發送接收到的數據 netbuf_delete(recvbuf); }else if(recv_err == ERR_CLSD) //關閉鏈接 { netconn_close(newconn); netconn_delete(newconn); printf("主機:%d.%d.%d.%d斷開與服務器的鏈接\r\n",remot_addr[0], remot_addr[1],remot_addr[2],remot_addr[3]); break; } } OSTimeDly(10); } OSTimeDly(10); } }
這裏面用了兩個服務器特有的api,以下socket
netconn_bind(conn,IP_ADDR_ANY,TCP_SERVER_PORT); //綁定端口 8088號端口 netconn_listen(conn); //進入監聽模式
其實嚴格來講,監聽以後應該來一個任務生成一個新的任務處理相應的連接,這個demo裏面沒這個作,按部就班嘛tcp
代碼位置以下ui
http://download.csdn.net/detail/dengrengong/8599075