以STM32H7为例
初始化:
首先要初始化网卡,并添加网口。
tcpip_init(NULL, NULL);
//其中两参数:第一个为函数指针,第二个为函数的参数
//此为初始化整个模块的函数,在完成初始化后将会调用此函数
//此处未使用
struct netif gnetif; //定义网络接口
ip_addr_t ipaddr;//IP地址
ip_addr_t netmask;//掩码
ip_addr_t gw;//网关
//实例化
IP_ADDR4(&ipaddr,192,168,1,100);
IP_ADDR4(&netmask,255,255,255,0);
IP_ADDR4(&gw,192,168,1,1);
//网卡初始化,并想网卡列表添加网口
//ethernetif_init:硬件相关的初始化,以及发送数据相关处理
//tcpip_input:接收数据相关处理
netif_add(&gnetif, &ipaddr, &netmask, &gw, NULL, ðernetif_init, &tcpip_input);
netif_set_default(&gnetif);//注册默认网口
//eth_link_callback:连接状态改变时的回调函数
netif_set_link_callback(&gnetif, eth_link_callback); //设置连接状态改变时回调函数
eg:
void eth_link_callback(struct netif *netif)
{
if(netif_is_link_up(netif))
{
//连上
}
else
{
//断开
}
}
服务端:
struct pbuf *q; //接收的数据包
static ip_addr_t ipaddr;//地址
static uint16_t port; //端口
conn = netconn_new(NETCONN_TCP);/* 创建TCP连接 */
netconn_bind(conn,NULL,8080); /* 绑定本地地址和监听的端口号 */
netconn_listen(conn); /* 进入监听状态 */
conn->recv_timeout = 100; /*设置阻塞线程时间100ms*/
while(1)
{
err = netconn_accept(conn,&newconn); //阻塞时间内未连接上来,则报超时错误
if(err == ERR_OK)
{
struct netbuf *recvbuf; //接收链表
newconn->recv_timeout = 100; //超时设置
//ipaddr.addr为连上来的IP
//port端口
netconn_getaddr(newconn,&ipaddr,&port,0); //获取连接上来的IP与端口号
while(1)
{
//接收到数据
if((TCP_err = netconn_recv(newconn,&recvbuf)) == ERR_OK)
{
//遍历pbuf链表直到遇空
for(q = recvbuf->p; q != NULL; q = q->next)
{
//q->payload数据
//q->len长度
}
netbuf_delete(recvbuf); //收完数据删除数据包
}
else if(TCP_err != ERR_OK && TCP_err != ERR_TIMEOUT) //关闭连接
{
netconn_close(newconn);//关闭此连接
netconn_delete(newconn);//删除此连接
break;//跳出一次while 等待下一个连接
}
else
{
vTaskDelay(100);
}
}
}
}
//发送函数
void Tcp_ServerSendBuff(void)
{
//DataBuff:要发送的buff
//length:要发送的数据长度
err = netconn_write( newconn, DataBuff, length, NETCONN_COPY);
if(err != ERR_OK)
{
//发送失败
}
}
客户端:
//定义想要连接的服务端IP以及端口
#define TCP_SERVER_IP "192.168.1.30"
#define TCP_SERVER_PORT 8080
static struct netconn *tcp_client;//定义连接实例
struct netbuf *buf;
void *data;
u16_t len;
err_t err;
ip_addr_t server_ip;
u16_t server_port = TCP_SERVER_PORT;// 服务器端口号初始化
ip4addr_aton( TCP_SERVER_IP, &server_ip );// 服务器IP地址初始化
while(1)
{
tcp_client = netconn_new( NETCONN_TCP );//连接实例
if( tcp_client != NULL )
{
tcp_client->pcb.tcp->so_options |= SOF_KEEPALIVE;//打开TCP保活机制
err = netconn_connect( tcp_client, &server_ip, server_port );//连接指定的IP
if(err == ERR_OK)
{
for( ;; )
{
if((err = netconn_recv(tcp_client, &buf)) == ERR_OK)
{
//获取一个指向netbuf 结构中的数据的指针
if((err = netbuf_data(buf, &data, &len)) == ERR_OK)
{
//在此获取数据以及数据长度
received_server_data_process( data, len );//此处采用的接收到就马上发出去
netbuf_delete(buf);
}
else
{
printf("err:netbuf_data(buf, &data, &len):%d.",err);
}
}
else
{
printf("err:netconn_recv(conn, &buf):%d.",err);
netbuf_delete(buf);
break; //连接发生错误,退出死等数据的循环,重新建立链接
}
}
}
}
}
//接收数据
int received_server_data_process( uint8_t *data, uint16_t len )
{
return send_server_data( data, len );
}
//发送数据
int send_server_data( uint8_t *data, uint16_t len )
{
if( tcp_client_server_conn )
{
return netconn_write( tcp_client, data, len, NETCONN_COPY);
}
else
return ERR_CONN;
}
还可以采用socket的API,即将LWIP_SOCKET置1。,如下为服务端
struct sockaddr_in server_addr; //服务器地址
struct sockaddr_in conn_addr; //连接地址
int sock_fd; //服务器的 socked
int sock_conn; // 请求的 socked
socklen_t addr_len; // 地址长度
int err;
sock_fd = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP); //建立一个新的socket连接
memset(&server_addr, 0, sizeof(server_addr)); //将服务器地址清空
server_addr.sin_family = AF_INET; //地址家族
server_addr.sin_addr.s_addr =htonl(INADDR_ANY); //注意转化为网络字节序
server_addr.sin_port = htons(SERVER_PORT); //使用指定端口号
err = bind(sock_fd, (struct sockaddr *)&server_addr, sizeof(server_addr)); //建立绑定
if (err < 0) //如果绑定失败则关闭套接字
{
closesocket(sock_fd); //关闭套接字
}
while(1)
{
err = listen(sock_fd, 1); //监听连接请求 1:ACCEPT队列的大小
if (err < 0) //如果监听失败则关闭套接字
{
closesocket(sock_fd); //关闭套接字
}
addr_len = sizeof(struct sockaddr_in); //将链接地址赋值给addr_len
sock_conn = accept(sock_fd, (struct sockaddr *)&conn_addr, &addr_len); //等待连接,状态赋值给sock_conn
if(sock_conn < 0) //状态小于0代表连接故障,此时关闭套接字
{
closesocket(sock_fd);
}
while (1)
{
memset(data_buffer, 0, sizeof(data_buffer)); //清空接收Buff
int length = recv(sock_conn, (unsigned int *)data_buffer, 100, 0); //将收到的数据放到接收Buff 100为可以接收的数据长度
if(length > 0)
{
send(sock_conn, data_buffer,length, 1); //回复客户端
}
else
{
closesocket(sock_conn);
break;
}
}
}
如下为客户端
#define PORT 8080
conn_fd = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
serv_addr.sin_family = AF_INET;
serv_addr.sin_port = htons(PORT);
serv_addr.sin_addr.s_addr = inet_addr("192.168.1.90");
if( connect(conn_fd, (struct sockaddr *)&serv_addr, sizeof(struct sockaddr_in)) == -1)
{
printf("connect error\n");
}
其类似于Linux下的使用TCP的API
服务端分为如下步骤:
-
创建一个socket:sock_fd = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
-
绑定socket:
memset(&server_addr, 0, sizeof(server_addr)); //将服务器地址清空
server_addr.sin_family = AF_INET; //地址家族
server_addr.sin_addr.s_addr =htonl(INADDR_ANY); //注意转化为网络字节序
server_addr.sin_port = htons(SERVER_PORT); //使用指定端口号
err = bind(sock_fd, (struct sockaddr *)&server_addr, sizeof(server_addr)); //建立绑定 -
监听连接请求:err = listen(sock_fd, 1); //1:ACCEPT队列的大小
-
等待连接:sock_conn = accept(sock_fd, (struct sockaddr *)&conn_addr, &addr_len);
客户端分为如下步骤:
- 创建一个socket:sock_fd = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
- 配置连接IP以及端口,然后直接连接:connect(conn_fd, (struct sockaddr *)&serv_addr, sizeof(struct sockaddr_in));