stm32f107vc lwip tcp客户端 服务器数据传输
第一篇 TCP客户端模式简单数据收发
----控制开发板LED灯
概要
建立LWIP客户端模式,科星F107开发板做为客户端去连接PC测试软件模拟的服务器,实现简单的数据收发,通过上位机控制板子的LED灯
一 打开工程《科星F107开发板网络应用篇之TCP客户端》
打开MAIN.C主文件
int main(void)
{
System_Setup();
LwIP_Init();
//Client_init();
while (1)
{
LwIP_Periodic_Handle(LocalTime);
}
}
上面和前面讲到的服务器模式,区别只在于Client_init();函数,别的和前面都一样,这里不再讲解,我们主要看一下Client_init();函数里的内容 看看客户端和服务器的区别。
void Client_init(void)
{
struct tcp_pcb *pcb;
struct ip_addr ipaddr;
IP4_ADDR(&ipaddr,192,168,0,22); 远程服务器IP 写进全局变量
pcb = tcp_new(); 建立一个新的tcp 链接
tcp_bind(pcb, IP_ADDR_ANY, 23); 绑定开发板IP和端口号 这里端口号可以是0 也就是随机,因为是客户端 不必知道端口号。
tcp_connect(pcb, &ipaddr, PEER_PORT, tcp_Client_connected); 主要是这里和服务器不太一样,服务器模式是监听listen,客户端的话 就要主动去连接服务器 我们看一下 参数的含义
PCB 就是我们前面建立的tcp端口
IPADDR 要连接的远程服务器IP 这里设置的是测试的电脑的IP
这里设置的是IP4_ADDR(&ipaddr,192,168,0,22);
PEER_PORT 远程端口号 我们要连接的端口号 这里我们设置的是5000
#define PEER_PORT 5000
看一下我们测试软件和电脑的设置截图 主要是电脑的IP和上位机的本地端口号 别的没用
tcp_Client_connected 连接到远程服务器成功后的回调函数 连接成功后会调用这个函数,我们看一下这个过程是怎么到这个函数的
进入函数: tcp_connect(pcb,&ipaddr, PEER_PORT, tcp_Client_connected);
下面第四个参数* connected就是回调函数 在程序里找到这个
err_t
tcp_connect(struct tcp_pcb *pcb, struct ip_addr *ipaddr, u16_t port,
err_t (* connected)(void *arg, struct tcp_pcb *tpcb, err_t err))
{
err_t ret;
u32_t iss;
LWIP_ERROR("tcp_connect: can only connected from state CLOSED", pcb->state == CLOSED, return ERR_ISCONN);
LWIP_DEBUGF(TCP_DEBUG, ("tcp_connect to port %"U16_F"\n", port));
if (ipaddr != NULL) {
pcb->remote_ip = *ipaddr; 远程IP
} else {
return ERR_VAL;
}
pcb->remote_port = port; 端口号
if (pcb->local_port == 0) {
pcb->local_port = tcp_new_port(); 本地端口号
}
iss = tcp_next_iss();
pcb->rcv_nxt = 0;
pcb->snd_nxt = iss;
pcb->lastack = iss - 1;
pcb->snd_lbb = iss - 1;
pcb->rcv_wnd = TCP_WND;
pcb->rcv_ann_wnd = TCP_WND;
pcb->rcv_ann_right_edge = pcb->rcv_nxt;
pcb->snd_wnd = TCP_WND;
/* As initial send MSS, we use TCP_MSS but limit it to 536.
The send MSS is updated when an MSS option is received. */
pcb->mss = (TCP_MSS > 536) ? 536 : TCP_MSS;
#if TCP_CALCULATE_EFF_SEND_MSS
pcb->mss = tcp_eff_send_mss(pcb->mss, ipaddr);
#endif /* TCP_CALCULATE_EFF_SEND_MSS */
pcb->cwnd = 1;
pcb->ssthresh = pcb->mss * 10;
pcb->state = SYN_SENT;
#if LWIP_CALLBACK_API
pcb->connected = connected; 回调函数传递给我们网络端口connected
既然是连接成功后回调这个函数 那么在哪里检测是不是连接成功呢?
如果调用的话肯定是调用pcb->connected 因为他就是回调函数 我们刚才已经传递给他了
这里如果对LWIP不熟的话 可能就搜不到了,我们直接告诉大家 在TCP.h中
#define TCP_EVENT_CONNECTED(pcb,err,ret) \
do { \
if((pcb)->connected != NULL) \
(ret) = (pcb)->connected((pcb)->callback_arg,(pcb),(err)); \
else (ret) = ERR_OK; \
} while (0)
所以我们搜索TCP_EVENT_CONNECTED就能找到连接远程服务器成功后 程序到了哪里 并且调用了我们的回调函数。搜一下:
在tcp_process(struct tcp_pcb *pcb)函数中 果然 这个就是tcp进程的函数,我们看在哪里调用了?
switch (pcb->state) {
case SYN_SENT:
LWIP_DEBUGF(TCP_INPUT_DEBUG, ("SYN-SENT: ackno %"U32_F" pcb->snd_nxt %"U32_F" unacked %"U32_F"\n", ackno,
pcb->snd_nxt, ntohl(pcb->unacked->tcphdr->seqno)));
/* received SYN ACK with expected sequence number? */
if ((flags & TCP_ACK) && (flags & TCP_SYN)
&& ackno == ntohl(pcb->unacked->tcphdr->seqno) + 1) {
pcb->snd_buf++;
pcb->rcv_nxt = seqno + 1;
pcb->rcv_ann_right_edge = pcb->rcv_nxt;
pcb->lastack = ackno;
pcb->snd_wnd = tcphdr->wnd;
pcb->snd_wl1 = seqno - 1; /* initialise to seqno - 1 to force window update */
pcb->state = ESTABLISHED;
#if TCP_CALCULATE_EFF_SEND_MSS
pcb->mss = tcp_eff_send_mss(pcb->mss, &(pcb->remote_ip));
#endif /* TCP_CALCULATE_EFF_SEND_MSS */
/* Set ssthresh again after changing pcb->mss (already set in tcp_connect
* but for the default value of pcb->mss) */
pcb->ssthresh = pcb->mss * 10;
pcb->cwnd = ((pcb->cwnd == 1) ? (pcb->mss * 2) : pcb->mss);
LWIP_ASSERT("pcb->snd_queuelen > 0", (pcb->snd_queuelen > 0));
--pcb->snd_queuelen;
LWIP_DEBUGF(TCP_QLEN_DEBUG, ("tcp_process: SYN-SENT --queuelen %"U16_F"\n", (u16_t)pcb->snd_queuelen));
rseg = pcb->unacked;
pcb->unacked = rseg->next;
/* If there's nothing left to acknowledge, stop the retransmit
timer, otherwise reset it to start again */
if(pcb->unacked == NULL)
pcb->rtime = -1;
else {
pcb->rtime = 0;
pcb->nrtx = 0;
}
tcp_seg_free(rseg);
/* Call the user specified function to call when sucessfully
* connected. */
TCP_EVENT_CONNECTED(pcb, ERR_OK, err);就是这里 接收到服务器的回应 具体是怎么到这里的 这个就得靠大家去看lwip函数讲解了 这里就不多说了 大致的流程已经和大家说了。
tcp_ack_now(pcb);
}
#endif /* LWIP_CALLBACK_API */
TCP_RMV(&tcp_bound_pcbs, pcb);
TCP_REG(&tcp_active_pcbs, pcb);
snmp_inc_tcpactiveopens();
ret = tcp_enqueue(pcb, NULL, 0, TCP_SYN, 0, TF_SEG_OPTS_MSS
#if LWIP_TCP_TIMESTAMPS
| TF_SEG_OPTS_TS
#endif
);
if (ret == ERR_OK) {
tcp_output(pcb);
}
return ret;
}
}
OK 这样连接建立后就进入了回调函数tcp_Client_connected 进入函数后
err_t tcp_Client_connected(void *arg, struct tcp_pcb *pcb, err_t err)
{
tcp_write(pcb, GREETING, strlen(GREETING), 0); //输出hello
tcp_recv(pcb, HelloWorld_recv);//指定接收到数据后的回调函数
return ERR_OK;
}
我们看到这里和服务器又一样了。连接成功后发送数据给上位机
#define GREETING "Hello.连接已经建立!欢迎来到科星F107开发板网络学习!\r\n"
然后TCP_RECV指定了接受数据的回调函数 这里我们直接调用了服务器模式时候的函数。
这样客户端模式的通信 就完成了。
注:
因为客户端的连接,每次都是有固定的流程和时间,也许上电开始服务器并没有准备好,或者是通信过程中由于某些原因连接断开了,那就需要我们能够保证这种情况下也能连接,那么我们就得在没有连上的时候,一直去尝试连接,才能做到从连,所以我们实际应用中把客户端连接的函数Client_init();放到了主循环中。
测试流程
下载程序到开发板,打开上位机 电脑和上位机的配置按照上面的截图
点击“开始监听” 等待开发板客户端的连接 连上后如图
点击“发送” 开发板会返回相应的数据:如图
这样 客户端模式数据通信就实现了。
结束语
在测试一下断线从连,点击“停止监听”等一会在开始监听看看能不能连上?怎样做才能做到更好?这也是开发产品应该完善的。
功能跟进 TCP客户端模式控制开发板的LED灯和蜂鸣器
打开工程《科星F107开发板网络应用篇之TCP客户端-LED灯》
下载程序到开发板,打开上位机,参数配置如下图,
开始监听 等到灯变红色 就可以控制开发板的LED和蜂鸣器了 自己试试吧。
|
|