Trong bài này, mình sẽ hướng dẫn các bạn sử dụng TCP client và TCP server thông qua các api của lwip
Có 3 cách cơ bản để sử dụng lwip
- RAW / API gốc – một giao diện được sử dụng mà không cần hệ điều hành. Nó có một số nhược điểm, nhưng cũng có một số ưu điểm, chẳng hạn như cung cấp quyền tự do hành động, điều thường thiếu ở các api cao cấp hơn
- Netconn API là API nối tiếp cấp cao yêu cầu hệ điều hành thời gian thực (RTOS). Netconn API cho phép hoạt động đa luồng.
- BSD Socket API – Một API tương tự như Berkeley-Socket (được phát triển dựa trên API của Netconn)
Bây giờ chúng ta sẽ làm việc với giao diện RAW để gửi nhận tcp client nhé
Các bạn tạo 2 file tên là row_api_tcp_client.h và row_api_tcp_client.c Có thể lưu nó trong Inc và Src. Sau đó add source file vào cây thư mục của project
Copy các hàm mình đã viết vào thư viện row_api_tcp_client
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 |
#include "row_api_tcp_client.h" uint8_t is_tcp_connect,wait_connect; uint8_t ipaddr_dest[4]={192,168,1,7}; uint16_t port_dest=80; struct tcp_pcb *client_pcb; __IO uint32_t message_count=0; u8_t data[100]; struct client_struct *cs; static err_t tcp_client_connected(void *arg, struct tcp_pcb *tpcb, err_t err) { struct client_struct *es = NULL; wait_connect=0; if (err == ERR_OK) { es = (struct client_struct *)mem_malloc(sizeof(struct client_struct)); if (es != NULL) { es->state = ES_CONNECTED; es->pcb = tpcb; es->p_tx = pbuf_alloc(PBUF_TRANSPORT, strlen((char*)data) , PBUF_POOL); if (es->p_tx) { /* copy data to pbuf */ pbuf_take(es->p_tx, (char*)data, strlen((char*)data)); /* pass newly allocated es structure as argument to tpcb */ tcp_arg(tpcb, es); /* initialize LwIP tcp_recv callback function */ tcp_recv(tpcb, tcp_client_recv); /* initialize LwIP tcp_sent callback function */ tcp_sent(tpcb, tcp_client_sent); /* initialize LwIP tcp_poll callback function */ tcp_poll(tpcb, tcp_client_poll, 1); /* send data */ tcp_client_send(tpcb,es); is_tcp_connect=1; return ERR_OK; } } else { tcp_client_connection_close(tpcb, es); return ERR_MEM; } } else { tcp_client_connection_close(tpcb, es); } return err; } void net_cmd(char* buf_str) { } static void tcp_error(void * arg, err_t err) { wait_connect=0; } void tcp_client_connect(void) { ip_addr_t DestIPaddr; client_pcb = tcp_new(); if (client_pcb != NULL) { IP4_ADDR( &DestIPaddr, ipaddr_dest[0], ipaddr_dest[1], ipaddr_dest[2], ipaddr_dest[3]); tcp_connect(client_pcb,&DestIPaddr,port_dest,tcp_client_connected); tcp_err(client_pcb,tcp_error); } } void tcp_client_disConnect(void) { tcp_recv(client_pcb, NULL); tcp_sent(client_pcb, NULL); tcp_poll(client_pcb, NULL,0); tcp_close(client_pcb); } static err_t tcp_client_recv(void *arg, struct tcp_pcb *tpcb, struct pbuf *p, err_t err) { struct client_struct *es; err_t ret_err; es = (struct client_struct *)arg; if (p == NULL) { es->state = ES_CLOSING; if(es->p_tx == NULL) { tcp_client_connection_close(tpcb, es); } ret_err = ERR_OK ; } else if(err != ERR_OK) { if (p != NULL) { pbuf_free(p); } ret_err = err; } else if(es->state == ES_CONNECTED) { message_count++; tcp_recved(tpcb, p->tot_len); es->p_tx = p; char buff[50]; strncpy(buff,es->p_tx->payload,es->p_tx->len); buff[es->p_tx->len] = '\0'; UART_putString(buff); ret_err = ERR_OK; } else if (es->state == ES_RECEIVED) { ret_err = ERR_OK; } else { /* Acknowledge data reception */ tcp_recved(tpcb, p->tot_len); /* free pbuf and do nothing */ pbuf_free(p); ret_err = ERR_OK; } return ret_err; } //---------------------------------------------------------- static void tcp_client_send(struct tcp_pcb *tpcb, struct client_struct * es) { struct pbuf *ptr; err_t wr_err = ERR_OK; while ((wr_err == ERR_OK) && (es->p_tx != NULL) && (es->p_tx->len <= tcp_sndbuf(tpcb))) { ptr = es->p_tx; wr_err = tcp_write(tpcb, ptr->payload, ptr->len, 1); if (wr_err == ERR_OK) { es->p_tx = ptr->next; if(es->p_tx != NULL) { pbuf_ref(es->p_tx); } pbuf_free(ptr); } else if(wr_err == ERR_MEM) { es->p_tx = ptr; } else { /* other problem ?? */ } } } //---------------------------------------------------------- static err_t tcp_client_sent(void *arg, struct tcp_pcb *tpcb, u16_t len) { struct client_struct *es; LWIP_UNUSED_ARG(len); es = (struct client_struct *)arg; if(es->p_tx != NULL) { tcp_client_send(tpcb, es); } return ERR_OK; } //---------------------------------------------------------- static err_t tcp_client_poll(void *arg, struct tcp_pcb *tpcb) { err_t ret_err; struct client_struct *es; es = (struct client_struct*)arg; if (es != NULL) { if (es->p_tx != NULL) { } else { if(es->state == ES_CLOSING) { tcp_client_connection_close(tpcb, es); } } ret_err = ERR_OK; } else { tcp_abort(tpcb); ret_err = ERR_ABRT; } return ret_err; } static void tcp_client_connection_close(struct tcp_pcb *tpcb, struct client_struct * es) { /* remove callbacks */ tcp_recv(tpcb, NULL); tcp_sent(tpcb, NULL); tcp_poll(tpcb, NULL,0); if (es != NULL) { mem_free(es); } /* close tcp connection */ tcp_close(tpcb); is_tcp_connect=0; } void sendstring(char* buf_str) { tcp_sent(client_pcb, tcp_client_sent); tcp_write(client_pcb, (void*)buf_str, strlen(buf_str), 1); tcp_output(client_pcb); } |
Code cho row_api_tcp_client.c
Trong file này có khai báo ipaddr_dest = 192.168.1.7 là IP của máy tính, mình sẽ cho máy tính của mình làm TCP server để test. Các bạn hay sửa thành IP máy tính của các bạn nhé. Có thể gõ lệnh ipconfig vào cmd của windonw để lấy ip máy tính
Code cho row_api_tcp_client.h
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 |
#ifndef _ROW_API_TCP_CLIENT_H_ #define _ROW_API_TCP_CLIENT_H_ //----------------------------------------------- #include "main.h" #include <string.h> #include <stdlib.h> #include <stdint.h> #include "lwip_user.h" #include "lwip/tcp.h" //----------------------------------------------- #include "uart.h" //----------------------------------------------- enum client_states { ES_NOT_CONNECTED = 0, ES_CONNECTED, ES_RECEIVED, ES_CLOSING, }; struct client_struct { enum client_states state; /* connection status */ struct tcp_pcb *pcb; /* pointer on the current tcp_pcb */ struct pbuf *p_tx; /* pointer on pbuf to be transmitted */ }; void net_ini(void); void net_cmd(char* buf_str); void tcp_client_connect(void); void tcp_client_disConnect(void); static err_t tcp_client_connected(void *arg, struct tcp_pcb *tpcb, err_t err); static err_t tcp_client_recv(void *arg, struct tcp_pcb *tpcb, struct pbuf *p, err_t err); static void tcp_client_send(struct tcp_pcb *tpcb, struct client_struct * es); static err_t tcp_client_sent(void *arg, struct tcp_pcb *tpcb, u16_t len); static err_t tcp_client_poll(void *arg, struct tcp_pcb *tpcb); static err_t tcp_client_connected(void *arg, struct tcp_pcb *tpcb, err_t err); static void tcp_client_connection_close(struct tcp_pcb *tpcb, struct client_struct * es); static err_t tcp_client_recv(void *arg, struct tcp_pcb *tpcb, struct pbuf *p, err_t err); void sendstring(char* buf_str); #endif /* _ROW_API_TCP_CLIENT_H_ */ |
Trong file main.c mình sẽ khai báo 2 biến toàn cục
1 2 3 4 |
uint32_t tSent; char ETH_CONNECT_OK = 0; |
và thêm mã để in IP ra cổng uart và gửi data lên TCP Server sau mỗi 1s vào trong vòng lặp chính của main
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 |
lwip_periodic_handle(); if(ETH_IPOK() && (ETH_CONNECT_OK == 0)) { ETH_CONNECT_OK = 1; char buff[50]; sprintf(buff,"IP: %i.%i.%i.%i\r\n",localIP[0],localIP[1],localIP[2],localIP[3]); UART_putString(buff); sprintf(buff,"Gw: %i.%i.%i.%i\r\n",gatewayIP[0],gatewayIP[1],gatewayIP[2],gatewayIP[3]); UART_putString(buff); sprintf(buff,"Mask: %i.%i.%i.%i\r\n",subnetmask[0],subnetmask[1],subnetmask[2],subnetmask[3]); UART_putString(buff); tcp_client_connect(); } if(ETH_CONNECT_OK) //chay cac dich vu mang { if(HAL_GetTick() - tSent > 999) { tSent = HAL_GetTick(); sendstring("Xin chao, toi la STM32\r\n"); } } |
Biên dịch và nạp chương trình, bây giờ hãy sử dụng phần mềm Hercules để tạo 1 TCP server, bạn cũng mở thêm 1 màn Serial ở in dữ liệu debug lên nhé
Như vậy TCP Server đã nhận được dữ liệu gửi lên từ STM32
Viết chương trình TCP Server
Tương tự, chúng ta tạo 2 file row_api_tcp_server.h và row_api_tcp_server.c
Source file
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 |
#include "row_api_tcp_server.h" static struct tcp_pcb *server_pcb; struct server_struct *ss; //---------------------------------------------------------- void tcp_server_init(void) { server_pcb = tcp_new(); if (server_pcb != NULL) { err_t err; err = tcp_bind(server_pcb, IP_ADDR_ANY, 80); if (err == ERR_OK) { server_pcb = tcp_listen(server_pcb); tcp_accept(server_pcb, tcp_server_accept); } else { memp_free(MEMP_TCP_PCB, server_pcb); } } } static err_t tcp_server_accept(void *arg, struct tcp_pcb *newpcb, err_t err) { err_t ret_err; struct server_struct *es; LWIP_UNUSED_ARG(arg); LWIP_UNUSED_ARG(err); tcp_setprio(newpcb, TCP_PRIO_MIN); es = (struct server_struct *)mem_malloc(sizeof(struct server_struct)); ss = (struct server_struct *)mem_malloc(sizeof(struct server_struct)); if (es != NULL) { es->state = ES_ACCEPTED; es->pcb = newpcb; ss->pcb = newpcb; es->retries = 0; es->p = NULL; tcp_arg(newpcb, es); tcp_recv(newpcb, tcp_server_recv); tcp_err(newpcb, tcp_server_error); tcp_sent(newpcb, tcp_server_sent); tcp_poll(newpcb, tcp_server_poll, 0); ret_err = ERR_OK; } else { tcp_server_connection_close(newpcb, es); ret_err = ERR_MEM; } return ret_err; } static void tcp_server_connection_close(struct tcp_pcb *tpcb, struct server_struct *es) { // remove all callbacks tcp_arg(tpcb, NULL); tcp_sent(tpcb, NULL); tcp_recv(tpcb, NULL); tcp_err(tpcb, NULL); tcp_poll(tpcb, NULL, 0); if (es != NULL) { mem_free(es); } tcp_close(tpcb); } //---------------------------------------------------------- //---------------------------------------------------------- static void tcp_server_error(void *arg, err_t err) { } //----------------------------------------------- static err_t tcp_server_recv(void *arg, struct tcp_pcb *tpcb, struct pbuf *p, err_t err) { err_t ret_err; struct server_struct *es; LWIP_ASSERT("arg != NULL",arg != NULL); es = (struct server_struct *)arg; if (p == NULL) { es->state = ES_SR_CLOSING; if(es->p == NULL) { tcp_recved(tpcb, p->tot_len); } else { //acknowledge received packet tcp_sent(tpcb, tcp_server_sent); //send remaining data tcp_server_send(tpcb, es); } ret_err = ERR_OK; } else if(err != ERR_OK) { if (p != NULL) { es->p = NULL; pbuf_free(p); } ret_err = err; } else if(es->state == ES_ACCEPTED) { tcp_recved(tpcb, p->tot_len); char str1[100]; strncpy(str1,p->payload,p->len);str1[p->len]=0; UART_putString(str1); ret_err = ERR_OK; } else if (es->state == ES_SR_RECEIVED) { if(es->p == NULL) { ret_err = ERR_OK; } else { struct pbuf *ptr; ptr = es->p; pbuf_chain(ptr,p); } ret_err = ERR_OK; } else if(es->state == ES_SR_CLOSING) { tcp_recved(tpcb, p->tot_len); es->p = NULL; pbuf_free(p); ret_err = ERR_OK; } else { tcp_recved(tpcb, p->tot_len); es->p = NULL; pbuf_free(p); ret_err = ERR_OK; } return ret_err; } //----------------------------------------------- static void tcp_server_send(struct tcp_pcb *tpcb, struct server_struct *es) { struct pbuf *ptr; err_t wr_err = ERR_OK; while ((wr_err == ERR_OK) && (es->p != NULL) && (es->p->len <= tcp_sndbuf(tpcb))) { ptr = es->p; wr_err = tcp_write(tpcb, ptr->payload, ptr->len, 1); if (wr_err == ERR_OK) { u16_t plen; u8_t freed; plen = ptr->len; es->p = ptr->next; if(es->p != NULL) { pbuf_ref(es->p); } do { freed = pbuf_free(ptr); } while(freed == 0); tcp_recved(tpcb, plen); } else if(wr_err == ERR_MEM) { es->p = ptr; } else { //other problem } } } //---------------------------------------------------------- //----------------------------------------------- static err_t tcp_server_sent(void *arg, struct tcp_pcb *tpcb, u16_t len) { struct server_struct *es; LWIP_UNUSED_ARG(len); es = (struct server_struct *)arg; es->retries = 0; if(es->p != NULL) { tcp_server_send(tpcb, es); } else { if(es->state == ES_SR_CLOSING) tcp_server_connection_close(tpcb, es); } return ERR_OK; } //----------------------------------------------- static err_t tcp_server_poll(void *arg, struct tcp_pcb *tpcb) { err_t ret_err; struct server_struct *es; es = (struct server_struct *)arg; if (es != NULL) { if (es->p != NULL) { } else { if(es->state == ES_SR_CLOSING) { tcp_server_connection_close(tpcb, es); } } ret_err = ERR_OK; } else { tcp_abort(tpcb); ret_err = ERR_ABRT; } return ret_err; } void tcp_server_sendstring(char* buf_str) { tcp_sent(ss->pcb, tcp_server_sent); tcp_write(ss->pcb, (void*)buf_str, strlen(buf_str), 1); tcp_recved(ss->pcb, strlen(buf_str)); } //---------------------------------------------------------- |
Header File
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 |
#ifndef _ROW_API_TCP_SERVER_H_ #define _ROW_API_TCP_SERVER_H_ //----------------------------------------------- #include "main.h" #include <string.h> #include <stdlib.h> #include <stdint.h> #include "lwip_user.h" #include "lwip/tcp.h" #include "uart.h" //----------------------------------------------- enum server_states { ES_NONE = 0, ES_ACCEPTED, ES_SR_RECEIVED, ES_SR_CLOSING }; //----------------------------------------------- struct server_struct { u8_t state; /* current connection state */ u8_t retries; struct tcp_pcb *pcb; /* pointer on the current tcp_pcb */ struct pbuf *p; /* pointer on the received/to be transmitted pbuf */ }; //----------------------------------------------- void tcp_server_init(void); static err_t tcp_server_accept(void *arg, struct tcp_pcb *newpcb, err_t err); static void tcp_server_connection_close(struct tcp_pcb *tpcb, struct server_struct *es); static void tcp_server_error(void *arg, err_t err); static err_t tcp_server_recv(void *arg, struct tcp_pcb *tpcb, struct pbuf *p, err_t err); static err_t tcp_server_sent(void *arg, struct tcp_pcb *tpcb, u16_t len); static err_t tcp_server_poll(void *arg, struct tcp_pcb *tpcb); static void tcp_server_send(struct tcp_pcb *tpcb, struct server_struct *es); void tcp_server_sendstring(char* buf_str); //----------------------------------------------- #endif /* _ROW_API_TCP_SERVER_H_ */ |
Add source vào cây project
Include thư viện row_api_tcp_server.h vào main.c
Add tcp_server_init(); vào dưới hàm tcp_client_connect(); trong vòng lặp chính
Biên dịch và nạp chương trình, bây giờ mở Hercules lên và vào tab TCP CLient trên máy tính, các bạn gửi gì thì Chip stm32 sẽ nhận được và gửi lên màn Serial
Các bạn hãy vận dụng kiến thức đã được học ở các bài trước để tự tìm hiểu sorce code được viết trong thư viện raw tcp để có thể chính sửa theo ý muốn nhé. Chúc các bạn thành công !
Tải toàn bộ sorce cho bài này tại đây
Đón xem toàn bộ khóa học ethernet tại đây
void tcp_server_sendstring(char* buf_str)
{
tcp_sent(ss->pcb, tcp_server_sent);
tcp_write(ss->pcb, (void*)buf_str, strlen(buf_str), 1);
tcp_recved(ss->pcb, strlen(buf_str));
}
Anh giải thích rõ hơn phần này được ko