stm32f107vc lwip tcp客户端服务器数据传输
建立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();函数里的内容看看客户端和服务器的区别。
空隙Client_init(无效)
{
结构tcp_pcb * PCB;
结构 ip_addr ipaddr;
IP4_ADDR(&ipaddr,192,168,0,22); 远程IP服务器写进变量
pcb = tcp_new(); 建立一个新的tcp
linktcp_bind(pcb, IP_ADDR_ANY, 23); cp_connect
(pcb, &ipaddr, PEER_PORT, tcp_Client_connected);绑定开发板IP和端口号接入端口号可以是0也就是随机,因为是客户端需要知道端口号。 主要是这里和服务器一样,服务器模式是监听监听,客户端事件主动去连接服务器我们看一下参数的接口
PCB就是我们前面建立的tcp端口
IPADDR要连接的远程服务器IP这里设置的是测试的电脑的IP
这里设置的是IP4_ADDR(&ipaddr,192,168,0,22);
PEER_PORT远程端口号我们要连接的端口号在这里我们设置的是5000
#define PEER_PORT 5000
看一下我们测试软件和电脑的设置截图主要是电脑的IP和上位机的本地端口号别没用
tcp_Client_connect连接到远程成功后一下这个函数连接成功后会调用函数,我们看这个函数的
进入函数:tcp_connect(pcb,&ipaddr, PEER_PORT, tcp_Client_connected);
下面第四个参数*连接就是函数在程序里找到这个
err_t
tcp_connect(struct tcp_pcb *pcb, struct ip_addr *ipaddr, u16_t 端口,
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 {
返回ERR_VAL;
}
多氯联苯》 REMOTE_PORT =端口; 端口号
if (pcb-》local_port == 0) {
pcb-》local_port = tcp_new_port(); 本地端口号
}
iss = tcp_next_iss();
印刷威廉希尔官方网站
板-》 rcv_nxt = 0;
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;
/* 作为初始发送 MSS,我们使用 TCP_MSS 但将其限制为 536。
当接收到 MSS 选项时,发送 MSS 会更新。*/
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-》 状态 = SYN_SENT;
#if LWIP_CALLBACK_API
pcb-》connected = connected;
如果调用的话是调用pcb-》连接是因为他的目的函数我们已经 连接成功给他了
这里如果对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;
}而(0)
所以我们搜索TCP_EVENT_CONNECTED就能找到连接远程服务器成功后程序到了哪里并且调用了我们的回调函数搜一下:
在tcp_process(结构tcp_pcb * PCB)函数中果然这个就是TCP进程的函数, ?我们看在哪里调用了
开关(多氯联苯》状态){
情况下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》unackdr》-》序列号)));
/* 收到带有预期序列号的 SYN ACK?*/
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 = 确认;
pcb-》snd_wnd = tcphdr-》wnd;
pcb-》snd_wl1 = seqno-1;/* 初始化为 seqno - 1 以强制窗口更新 */
pcb-》state = ESTABLISHED;
pcb-》mss = tcp_eff_send_mss(pcb-》mss, &(pcb-》remote_ip));
#endif /* TCP_CALCULATE_EFF_SEND_MSS */
/* 更改pcb-》mss后再次设置ssthresh(已经在tcp_connect
*中设置,但对于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-》未确认;
pcb-》unacked = rseg-》next;
/* 如果有‘
定时器,否则重置它重新开始 */
if(pcb-》unacked == NULL)
pcb-》rtime = -1;
否则 {
pcb-》rtime = 0;
印刷威廉希尔官方网站
板-》 nrtx = 0;
}
tcp_seg_free(RSEG);
/* 调用用户指定的函数在成功
连接时调用。*/
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);
}
返回RET;
}
}
OK 这样连接建立后就进入了连接函数tcp_Client_connected 进入函数后
err_t tcp_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“你好。连接已经建立!欢迎来到科星F107开发板网络学习!rn”
然后TCP_RECV指定了接受数据的通话功能此时我们直接调用了服务器模式的功能。
这样客户端模式的通信就完成了。
注:
因为客户端的连接,每次都有固定的流程和时间,可能上电开始服务器并没有准备好,是或者通信过程中因为某些原因连接断开了了,我们必须保证这种情况下也能连接,那么我们就得在没有连上的时候,一直去尝试连接,才能做到从连,所以我们实际应用中把客户端连接的函数 Client_init ();激活了主循环中。
测试流程
下载程序到开发板,打开上位机电脑和上位机的配置按照上面的截图
“开始监听”等待开发板客户端的连接连上后
点击“发送”开发板会返回相应的数据:就像
这样客户端模式数据通信就了。
结束语
在测试一下断线从,点击“停止监听”等一会开始监听看看能不能连上?做到更好?这也是开发产品应该完善的,
功能跟进TCP客户端模式控制开发板的LED状语从句:灯蜂鸣器
打开工程“科星F107开发板网络应用篇之TCP客户端-LED灯”
下载到开发板,打开上位机,参数配置如下图,
开始监听设备程序变红色就可以控制开发板的LED和蜂鸣器了自己的能力吧。
stm32f107vc lwip tcp客户端服务器数据传输
建立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();函数里的内容看看客户端和服务器的区别。
空隙Client_init(无效)
{
结构tcp_pcb * PCB;
结构 ip_addr ipaddr;
IP4_ADDR(&ipaddr,192,168,0,22); 远程IP服务器写进变量
pcb = tcp_new(); 建立一个新的tcp
linktcp_bind(pcb, IP_ADDR_ANY, 23); cp_connect
(pcb, &ipaddr, PEER_PORT, tcp_Client_connected);绑定开发板IP和端口号接入端口号可以是0也就是随机,因为是客户端需要知道端口号。 主要是这里和服务器一样,服务器模式是监听监听,客户端事件主动去连接服务器我们看一下参数的接口
PCB就是我们前面建立的tcp端口
IPADDR要连接的远程服务器IP这里设置的是测试的电脑的IP
这里设置的是IP4_ADDR(&ipaddr,192,168,0,22);
PEER_PORT远程端口号我们要连接的端口号在这里我们设置的是5000
#define PEER_PORT 5000
看一下我们测试软件和电脑的设置截图主要是电脑的IP和上位机的本地端口号别没用
tcp_Client_connect连接到远程成功后一下这个函数连接成功后会调用函数,我们看这个函数的
进入函数:tcp_connect(pcb,&ipaddr, PEER_PORT, tcp_Client_connected);
下面第四个参数*连接就是函数在程序里找到这个
err_t
tcp_connect(struct tcp_pcb *pcb, struct ip_addr *ipaddr, u16_t 端口,
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 {
返回ERR_VAL;
}
多氯联苯》 REMOTE_PORT =端口; 端口号
if (pcb-》local_port == 0) {
pcb-》local_port = tcp_new_port(); 本地端口号
}
iss = tcp_next_iss();
印刷威廉希尔官方网站
板-》 rcv_nxt = 0;
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;
/* 作为初始发送 MSS,我们使用 TCP_MSS 但将其限制为 536。
当接收到 MSS 选项时,发送 MSS 会更新。*/
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-》 状态 = SYN_SENT;
#if LWIP_CALLBACK_API
pcb-》connected = connected;
如果调用的话是调用pcb-》连接是因为他的目的函数我们已经 连接成功给他了
这里如果对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;
}而(0)
所以我们搜索TCP_EVENT_CONNECTED就能找到连接远程服务器成功后程序到了哪里并且调用了我们的回调函数搜一下:
在tcp_process(结构tcp_pcb * PCB)函数中果然这个就是TCP进程的函数, ?我们看在哪里调用了
开关(多氯联苯》状态){
情况下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》unackdr》-》序列号)));
/* 收到带有预期序列号的 SYN ACK?*/
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 = 确认;
pcb-》snd_wnd = tcphdr-》wnd;
pcb-》snd_wl1 = seqno-1;/* 初始化为 seqno - 1 以强制窗口更新 */
pcb-》state = ESTABLISHED;
pcb-》mss = tcp_eff_send_mss(pcb-》mss, &(pcb-》remote_ip));
#endif /* TCP_CALCULATE_EFF_SEND_MSS */
/* 更改pcb-》mss后再次设置ssthresh(已经在tcp_connect
*中设置,但对于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-》未确认;
pcb-》unacked = rseg-》next;
/* 如果有‘
定时器,否则重置它重新开始 */
if(pcb-》unacked == NULL)
pcb-》rtime = -1;
否则 {
pcb-》rtime = 0;
印刷威廉希尔官方网站
板-》 nrtx = 0;
}
tcp_seg_free(RSEG);
/* 调用用户指定的函数在成功
连接时调用。*/
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);
}
返回RET;
}
}
OK 这样连接建立后就进入了连接函数tcp_Client_connected 进入函数后
err_t tcp_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“你好。连接已经建立!欢迎来到科星F107开发板网络学习!rn”
然后TCP_RECV指定了接受数据的通话功能此时我们直接调用了服务器模式的功能。
这样客户端模式的通信就完成了。
注:
因为客户端的连接,每次都有固定的流程和时间,可能上电开始服务器并没有准备好,是或者通信过程中因为某些原因连接断开了了,我们必须保证这种情况下也能连接,那么我们就得在没有连上的时候,一直去尝试连接,才能做到从连,所以我们实际应用中把客户端连接的函数 Client_init ();激活了主循环中。
测试流程
下载程序到开发板,打开上位机电脑和上位机的配置按照上面的截图
“开始监听”等待开发板客户端的连接连上后
点击“发送”开发板会返回相应的数据:就像
这样客户端模式数据通信就了。
结束语
在测试一下断线从,点击“停止监听”等一会开始监听看看能不能连上?做到更好?这也是开发产品应该完善的,
功能跟进TCP客户端模式控制开发板的LED状语从句:灯蜂鸣器
打开工程“科星F107开发板网络应用篇之TCP客户端-LED灯”
下载到开发板,打开上位机,参数配置如下图,
开始监听设备程序变红色就可以控制开发板的LED和蜂鸣器了自己的能力吧。
举报