TCP RAW API
1.1 新建TCP控制塊
函數(shù)原型:
struct tcp_pcb * tcp_new(void)
1.2 綁定控制塊 tcp_bind()
用于服務(wù)端程序
將本地的 IP 地址、端口號與一個(gè)控制塊進(jìn)行綁定
函數(shù)原型:
err_t tcp_bind(struct tcp_pcb *pcb, const p_addr_t *ipaddr, u16_t port)
1.3 設(shè)置控制塊處于監(jiān)聽狀態(tài) tcp_listen()
用于服務(wù)端程序,在接收連接前必須讓TCP處于監(jiān)聽狀態(tài)
#define tcp_listen(pcb) tcp_listen_with_backlog(pcb, TCP_DEFAULT_LISTEN_BACKLOG)
struct tcp_pcb * tcp_listen_with_backlog_and_err(struct tcp_pcb *pcb, u8_t backlog, err_t *err)
1.4 處理連接 tcp_accept()
用于服務(wù)端,處理客戶端連接
函數(shù)原型:
void tcp_accept(struct tcp_pcb *pcb, tcp_accept_fn accept)
tcp_accept_fn回調(diào)函數(shù)類型定義:
typedef err_t (*tcp_accept_fn)(void *arg,
struct tcp_pcb *newpcb,
err_t err);
1.5 建立連接 tcp_connect()
TCP客戶端使用該函數(shù)主動發(fā)起連接
函數(shù)原型:
err_t tcp_connect(struct tcp_pcb *pcb,
const ip_addr_t *ipaddr,
u16_t port,
tcp_connected_fn connected)
當(dāng)TCP連接成功connected回調(diào)函數(shù)就會被調(diào)用
tcp_connected_fn回調(diào)函數(shù)類型定義:
typedef err_t (*tcp_connected_fn)(void *arg,
struct tcp_pcb *tpcb,
err_t err);
1.6 終止連接 tcp_close()
客戶端應(yīng)用程序主動調(diào)用tcp_close()終止一個(gè)TCP連接
err_t tcp_close(struct tcp_pcb *pcb)
1.7 接收數(shù)據(jù) tcp_recv()
函數(shù)原型:
void tcp_recv(struct tcp_pcb *pcb, tcp_recv_fn recv) //注冊recv回調(diào)函數(shù)字段
回調(diào)函數(shù)原型:
typedef err_t (*tcp_recv_fn)(void *arg,
struct tcp_pcb *tpcb,
struct pbuf *p,
err_t err);
該回調(diào)函數(shù)被調(diào)用的契機(jī):
- 內(nèi)核接收到數(shù)據(jù),該函數(shù)被調(diào)用并將數(shù)據(jù)遞交給應(yīng)用層,也就是將數(shù)據(jù)傳入回調(diào)函數(shù)
- 內(nèi)核檢測到對方主動終止TCP連接,也會觸發(fā)回調(diào)函數(shù)
所以數(shù)據(jù)的處理和應(yīng)用程序編寫就在該回調(diào)函數(shù)中實(shí)現(xiàn)
1.8 發(fā)送數(shù)據(jù) tcp_sent()
void tcp_sent(struct tcp_pcb *pcb, tcp_sent_fn sent) //將TCP控制塊sent回調(diào)函數(shù)字段注冊一個(gè)tcp_sent_fn的函數(shù),數(shù)據(jù)發(fā)送成功將調(diào)用sent回調(diào)函數(shù)通知應(yīng)用數(shù)據(jù)已經(jīng)被對方接收
tcp_sent_fn回調(diào)函數(shù)類型:
typedef err_t (*tcp_sent_fn)(void *arg,
struct tcp_pcb *tpcb,
u16_t len);
服務(wù)端編程實(shí)現(xiàn)
1、服務(wù)器的初始化
struct ip4_addr_fmt {
uint32_t addr1 : 8;
uint32_t addr2 : 8;
uint32_t addr3 : 8;
uint32_t addr4 : 8;
};
void tcp_user_server_init(void)
{
err_t err;
tcp_server = tcp_new(); /* 創(chuàng)建TCP控制塊 */
if (tcp_server == NULL)
{
LOG_LINE("failed to new tcp pcb");
return;
}
err = tcp_bind(tcp_server, IP_ADDR_ANY, 9005); /* 綁定IP端口 */
if (err != ERR_OK)
{
LOG_LINE("failed to bind tcp");
tcp_close(tcp_server);
return;
}
tcp_server = tcp_listen(tcp_server); /* 啟用接聽 */
tcp_accept(tcp_server, tcp_server_accept_cb); /* 設(shè)置收到客戶端連接請求的回調(diào)函數(shù) */
LOG_LINE("TCP Server OK!!!");
return;
}
接收請求連接的回調(diào):
err_t tcp_server_accept_cb(void *arg, struct tcp_pcb *newpcb, err_t err)
{
struct ip4_addr_fmt *ip = (struct ip4_addr_fmt *)&newpcb->remote_ip;
LOG_LINE("client %d.%d.%d.%d/%d connected", ip->addr1, ip->addr2, ip->addr3, ip->addr4, newpcb->remote_port);
tcp_recv(newpcb, tcp_server_recv_cb);
tcp_err(newpcb, tcp_err_cb);
return ERR_OK; /* 記得return ERR_OK,很重要/(ㄒoㄒ)/~~ */
}
-
tcp_recv(newpcb, tcp_server_recv_cb);
在接收請求回調(diào)中設(shè)置客戶端的數(shù)據(jù)接收函數(shù)回調(diào)函數(shù) -
tcp_err(newpcb, tcp_err_cb);
設(shè)置錯(cuò)誤的處理的回調(diào)函數(shù)
在測試過程中出現(xiàn)客戶端連接上后又?jǐn)嚅_,一直在反復(fù)重連,調(diào)試很久一段時(shí)間發(fā)現(xiàn)是因?yàn)?code>tcp_server_accept_cb函數(shù)的最后沒有return ERR_OK
客戶端數(shù)據(jù)的接收:
err_t tcp_server_recv_cb(void *arg, struct tcp_pcb *tpcb,
struct pbuf *p, err_t err)
{
if (p != NULL)
{
struct pbuf *q;
int recv_count = 0;
tcp_recved(tpcb, p->tot_len); /* 更新接收窗口 */
for (q = p; q != NULL; q = q->next)
{
if (q->len > sizeof(recv_test_buf))
{
memcpy(recv_test_buf, q->payload, sizeof(recv_test_buf));
break;
}
else
{
if (recv_count >= sizeof(recv_test_buf))
break;
memcpy(&recv_test_buf[recv_count], q->payload, q->len);
recv_count += q->len;
}
}
pbuf_free(p);
}
else if (err == ERR_OK) /* 接收成功但數(shù)據(jù)包是空的說明客戶端斷開連接 */
{
LOG_LINE("客戶端斷開連接");
return tcp_close(tpcb);
}
struct ip4_addr_fmt *ip = (struct ip4_addr_fmt *)&tpcb->remote_ip;
LOG_LINE("recv from %d.%d.%d.%d/%d, msg %s",
ip->addr1, ip->addr2, ip->addr3, ip->addr4, tpcb->remote_port, recv_test_buf);
memset(recv_test_buf, 0, sizeof(recv_test_buf));
return ERR_OK; /* 記得return ERR_OK,很重要/(ㄒoㄒ)/~~ */
}
- 接收數(shù)據(jù)前要調(diào)用
tcp_recved(tpcb, p->tot_len);
更新接收窗口
客戶端編程實(shí)現(xiàn)
客戶端編程就比較簡單了
void tcp_user_client_init(void)
{
tcp_client = tcp_new();
if (tcp_client == NULL)
{
LOG_LINE("failed to new tcp client");
return;
}
ip4_addr_t server_ip;
IP4_ADDR(&server_ip, 192, 168, 57, 112); /* 服務(wù)器IP地址 */
err_t err;
tcp_arg(tcp_client, tcp_client);
tcp_err(tcp_client, tcp_client_err_cb);
err = tcp_connect(tcp_client, &server_ip, 52000, tcp_client_connected_cb);
}
在成功連接的回調(diào)函數(shù)中設(shè)置客戶端數(shù)據(jù)接收回調(diào):
err_t tcp_client_connected_cb(void *arg, struct tcp_pcb *tpcb, err_t err)
{
tcp_recv(tpcb, tcp_client_recv_cb); /* 設(shè)置數(shù)據(jù)接收回調(diào) */
tcp_poll(tpcb, tcp_poll_cb, 10); /* 設(shè)置輪詢回調(diào) */
LOG_LINE("連接服務(wù)器成功");
return ERR_OK; /* 記得return ERR_OK,很重要/(ㄒoㄒ)/~~ */
}
數(shù)據(jù)接收:
err_t tcp_client_recv_cb(void *arg, struct tcp_pcb *tpcb,
struct pbuf *p, err_t err)
{
if (p != NULL)
{
struct pbuf *q;
int recv_count = 0;
tcp_recved(tpcb, p->tot_len); /* 更新接收窗口 */
for (q = p; q != NULL; q = q->next)
{
if (q->len > sizeof(recv_test_buf))
{
memcpy(recv_test_buf, q->payload, sizeof(recv_test_buf));
break;
}
else
{
if (recv_count >= sizeof(recv_test_buf))
break;
memcpy(&recv_test_buf[recv_count], q->payload, q->len);
recv_count += q->len;
}
}
pbuf_free(p);
}
else if (err == ERR_OK) /* 接收成功但數(shù)據(jù)包是空的說明客戶端斷開連接 */
{
LOG_LINE("服務(wù)端斷開連接");
tcp_close(tpcb);
tcp_client = NULL;
tcp_user_client_init(); /* 重連服務(wù)器 */
return err;
}
struct ip4_addr_fmt *ip = (struct ip4_addr_fmt *)&tpcb->remote_ip;
LOG_LINE("recv from %d.%d.%d.%d/%d, msg %s",
ip->addr1, ip->addr2, ip->addr3, ip->addr4, tpcb->remote_port, recv_test_buf);
memset(recv_test_buf, 0, sizeof(recv_test_buf));
return ERR_OK;
}
在定時(shí)輪詢回調(diào)中向服務(wù)器發(fā)送數(shù)據(jù):文章來源:http://www.zghlxwxcb.cn/news/detail-763807.html
err_t tcp_poll_cb(void *arg, struct tcp_pcb *tpcb)
{
static uint32_t test_count = 0;
char buf[100] = { 0 };
snprintf(buf, sizeof(buf), "tcp client test %d\r\n", test_count++);
return tcp_write(tpcb, buf, sizeof(buf), 1); /* 記得return,很重要/(ㄒoㄒ)/~~ */
}
總結(jié)
在測試過程中出現(xiàn)客戶端連接上后又?jǐn)嚅_,一直在反復(fù)重連,調(diào)試很久一段時(shí)間發(fā)現(xiàn)是因?yàn)?code>tcp_server_accept_cb函數(shù)的最后沒有return ERR_OK
。
代碼還是要去寫,不想看著接口很簡單,想當(dāng)然一點(diǎn)問題也沒有,看一眼就會了,結(jié)果當(dāng)自己親自去寫的時(shí)候出現(xiàn)很多愚蠢的問題。文章來源地址http://www.zghlxwxcb.cn/news/detail-763807.html
到了這里,關(guān)于LwIP RAW API TCP服務(wù)端客戶端編程及問題的文章就介紹完了。如果您還想了解更多內(nèi)容,請?jiān)谟疑辖撬阉鱐OY模板網(wǎng)以前的文章或繼續(xù)瀏覽下面的相關(guān)文章,希望大家以后多多支持TOY模板網(wǎng)!