1 TCP控制块
tcp控制块定义在tcp.h文件中
LWIP中将TCP控制块组合成链表的形式,如图:
2 TCP回调函数
函数 | 描述 |
tcp_arg() | 注册回调函数使用的参数 |
tcp_recv() | 注册接收的回调函数 |
tcp_err() | 注册出错处理的回调函数 |
tcp_sent() | 注册发送成功的回调函数 |
tcp_poll() | 注册轮询函数 |
tcp_accept() | 当侦听到有客户端连接的话就会调用注册函数 |
3 TCP实现原理
4 TCP函数
函数 | API | 描述 |
TCP建立连接 | tcp_new | 创建一个TCP的PCB控制块 |
tcp_bind | 绑定一个本地IP和PORT | |
tcp_listen | 监听TCP | |
tcp_accept | 注册监听回调函数 | |
tcp_connect | 连接远程主机 | |
发送TCP数据 | tcp_write | 将数据发送至发送缓冲区队列当中 |
tcp_sent | 注册发送完成回调函数 | |
tcp_output | 将发送缓冲队列中的数据发送出去 | |
接收TCP数据 | tcp_recv | 注册接收完成回调函数 |
tcp_recved | 通知内核更新接收窗口 | |
关闭连接 | tcp_close | 关闭TCP连接 |
tcp_abort | 终止TCP连接 |
5 RAW接口的TCPClient实验
(1)tcp_new创建一个TCP控制块; //描述当前TCP端口、IP等信息
(2)tcp_connect设置目标IP地址和插入TCP_PCB链表;//把控制块插入TCP PCB链表
(3)tcp_recv注册接收回调函数; //接受回调函数自行编写
(4)tcp_write发送数据; //网络搭建完成后,发送数据
6 TCPClient代码
#include "lwip/netif.h"
#include "lwip/ip.h"
#include "lwip/tcp.h"
#include "lwip/init.h"
#include "netif/etharp.h"
#include "lwip/udp.h"
#include "lwip/pbuf.h"
#include <stdio.h>
#include <string.h>
#include "main.h"
#include "tcpClient.h"
static void client_err(void *arg, err_t err) //出现错误时调用这个函数,打印错误信息,并尝试重新连接
{
printf("连接错误!!\n");
printf("尝试重连!!\n");
/* 连接失败的时候释放TCP控制块的内存 */
printf("关闭连接,释放TCP控制块内存\n");
/* 重新连接 */
printf("重新初始化客户端------------\r\n");
TCP_Client_Init();
}
static err_t client_send(void *arg, struct tcp_pcb *tpcb) //发送函数,调用了tcp_write函数
{
uint8_t send_buf[]= "tcp客户端测试-----------\r\n";
//发送数据到服务器
tcp_write(tpcb, send_buf, strlen(send_buf), 1);
return ERR_OK;
}
static err_t client_recv(void *arg, struct tcp_pcb *tpcb, struct pbuf *p, err_t err)
{
if (p != NULL)
{
/* 接收数据*/
tcp_recved(tpcb, p->tot_len);
// /* 返回接收到的数据*/
// tcp_write(tpcb, p->payload, p->tot_len, 1);
int i = 0;
for(i = 0; i < p->len;i++)
{
printf("%c",*((char *)p->payload+i));
}
memset(p->payload, 0 , p->tot_len);
pbuf_free(p);
}
else if (err == ERR_OK)
{
//服务器断开连接
printf("服务器断开连接---------\r\n");
tcp_close(tpcb);
//重新连接
TCP_Client_Init();
}
return ERR_OK;
}
static err_t client_connected(void *arg, struct tcp_pcb *pcb, err_t err)
{
printf("connected ok-------------\r\n");
if(err == ERR_OK)
{
tcp_err(pcb, client_err); //注册异常处理
printf("已经注册异常处理函数\r\n");
tcp_poll(pcb,client_send,10);//注册一个周期性回调函数
printf("已经注册了周期回调函数\r\n");
tcp_recv(pcb,client_recv); //注册一个接收函数
printf("已经注册了接收回调函数\r\b");
}
return ERR_OK;
}
void TCP_Client_Init(void)
{
struct tcp_pcb *client_pcb = NULL;
ip4_addr_t server_ip; //因为客户端要主动去连接服务器,所以要知道服务器的IP地址
/* 创建一个TCP控制块 */
client_pcb = tcp_new();
IP4_ADDR(&server_ip, 192,168,2,87);//合并IP地址
printf("客户端开始连接-------------\r\n");
//开始连接
tcp_connect(client_pcb, &server_ip, TCP_CLIENT_PORT, client_connected);
ip_set_option(client_pcb, SOF_KEEPALIVE);
printf("已经调用了tcp_connect函数\n");
}
[点击并拖拽以移动]
7 TCPServer代码
#include "tcpserver.h"
#include "lwip/netif.h"
#include "lwip/ip.h"
#include "lwip/tcp.h"
#include "lwip/init.h"
#include "netif/etharp.h"
#include "lwip/udp.h"
#include "lwip/pbuf.h"
#include <stdio.h>
#include <string.h>
static err_t tcp_server_recv(void *arg, struct tcp_pcb *tpcb, struct pbuf *p, err_t err)
{ //对应接收数据连接的控制块 接收到的数据
if (p != NULL)
{
int i = 0;
/* 更新窗口*/
tcp_recved(tpcb, p->tot_len); //读取数据的控制块 得到所有数据的长度
for(i = 0; i < p->len;i++)
{
printf("%c",*((char *)p->payload+i));
}
/* 返回接收到的数据*/
//tcp_write(tpcb, p->payload, p->tot_len, 1);
const char reback[] = "已经收到了数据";
tcp_write(tpcb, reback, strlen(reback), 1);
memset(p->payload, 0 , p->tot_len);
pbuf_free(p);
}
else if (err == ERR_OK) //检测到对方主动关闭连接时,也会调用recv函数,此时p为空
{
printf("客户端连接断开");
return tcp_close(tpcb);
}
return ERR_OK;
}
static err_t tcp_server_accept(void *arg, struct tcp_pcb *newpcb, err_t err) //形参的数量和类型必须一致
{
tcp_recv(newpcb, tcp_server_recv); //当收到数据时,回调用户自己写的tcpecho_recv
return ERR_OK;
}
void TCP_Server_Init(void)
{
struct tcp_pcb *server_pcb = NULL;
/* 创建一个TCP控制块 */
server_pcb = tcp_new();
printf("创建了一个控制块\n");
/* 绑定TCP控制块 */
tcp_bind(server_pcb, IP_ADDR_ANY, TCP_SERVER_PORT);
printf("已经绑定一个控制块\n");
/* 进入监听状态 */
server_pcb = tcp_listen(server_pcb);
printf("进入监听状态\n");
/* 处理连接 注册函数,侦听到连接时被注册的函数被回调 */
tcp_accept(server_pcb, tcp_server_accept); //侦听到连接后,回调用户编写的tcp_server_accept
//这个函数是*tcp_accept_fn类型的
}