上文介绍了用TCP发送“Hello World”的实例,工作在client模式下。本文实现同样的功能,但让TCP工作在server模式下。把开发板当作服务器,远程主机为客服端访问服务器,实现被动连接。TCP client和TCP server在lwIP中的连接流程和区别可参考本系列前面与lwIP相关的文章。
SDK程序设计
大部分代码和上一个实例相同。main函数的while循环中仍然是根据连接标志来启动数据发送函数,每秒发送一个“Hello World”。
while(1) {
/* call tcp timer every 250ms */
if(TcpTmrFlag)
{
tcp_tmr();
TcpTmrFlag = 0;
}
xemacif_input(netif); //将MAC队列中的packets传输到lwIP栈中
if (tcp_client_connected) { //连接成功则发送数据
sleep(1);
send_data();
}
}
与上一个实例的主要区别在user_tcp.c文件,代码如下:
#include "user_tcp.h"
#define SEND_SIZE 12
#define local_port 8080
static struct tcp_pcb *connected_pcb = NULL;
volatile unsigned tcp_client_connected = 0;
char sendBuffer[12]="Hello World!";
//--------------------------------------------------
// TCP连接成功的回调函数
//--------------------------------------------------
err_t connect_accept_callback(void *arg, struct tcp_pcb *tpcb, err_t err)
{
xil_printf("tcp_server: Connection Accepted\r\n");
connected_pcb = tpcb; //存储连接的TCP状态
tcp_client_connected = 1;
tcp_nagle_disable(connected_pcb);
return ERR_OK;
}
//--------------------------------------------------
// TCP PCB初始化函数
//--------------------------------------------------
int tcp_send_init()
{
struct tcp_pcb *pcb;
err_t err;
/* 创建新的TCP PCB */
pcb = tcp_new();
if (!pcb) {
xil_printf("txperf: Error creating PCB. Out of Memory\r\n");
return -1;
}
/* 绑定本地端口 */
err = tcp_bind(pcb, IP_ADDR_ANY, local_port);
if (err != ERR_OK) {
xil_printf("tcp_server: Unable to bind to port %d: err = %d\r\n", local_port, err);
return -2;
}
/* 监听连接 */
tcp_arg(pcb, NULL);
pcb = tcp_listen(pcb);
if (!pcb) {
xil_printf("tcp_server: Out of memory while tcp_listen\r\n");
return -3;
}
/* 设置accept回调函数 */
tcp_accept(pcb, connect_accept_callback);
return 0;
}
//--------------------------------------------------
// TCP数据发送函数
//--------------------------------------------------
void send_data(void)
{
err_t err;
struct tcp_pcb *tpcb = connected_pcb;
if (!connected_pcb)
return;
err = tcp_write(tpcb, sendBuffer, SEND_SIZE, 3);
if (err != ERR_OK) {
xil_printf("txperf: Error on tcp_write: %d\r\n", err);
connected_pcb = NULL;
return;
}
err = tcp_output(tpcb);
if (err != ERR_OK) {
xil_printf("txperf: Error on tcp_output: %d\r\n",err);
return;
}
}
本设计的TCP工作在服务器模式,将开发板当作服务器,被动等待客户端请求连接。
TCP客户端
我们回顾一下本系列第11篇中介绍的TCP被动连接方法:
1. 调用pcb_new创建一个pcb。
2.(可选)调用tcp_arg将应用程序中特定的值于PCB关联在一起。
3. 调用tcp_bind函数指定本地IP地址和端口。
4. 调用tcp_listen或tcp_listen_with_backlog,这些函数将释放作为参数的PCB,并返回一个更小的监听PCB,如“tcp_new = tcp_listen(tpcb);”。
5. 调用tcp_accept指定新连接到来时要调用的函数。
tcp_send_init中的初始化流程与此完全一致。tcp_accept函数设置connect_accept_callback回调函数。当已经监听的pcb与另一个主机建立连接关系后,该函数被调用。
回调函数中通过“xil_printf(“tcp_server: Connection Accepted\r\n”);”确认连接已建立,同时将tcp_client_connected置1,开始发送hello world。本程序中无需通过发送回调函数实现什么功能,因此没有注册该回调函数。
下载完程序后,打开网络调试助手选择TCP Client(否则检测不到开发板的IP地址),设置程序中绑定的IP地址和端口。连接后串口打印连接信息,开始发送hello world。
补充
有网友发现了这个问题,我就再补充解释一下,这位网友很棒。
---------------------