FreeRTOS下LWIP的TCP简单使用

11 篇文章 0 订阅

以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, &ethernetif_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
服务端分为如下步骤:

  1. 创建一个socket:sock_fd = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);

  2. 绑定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)); //建立绑定

  3. 监听连接请求:err = listen(sock_fd, 1); //1:ACCEPT队列的大小

  4. 等待连接:sock_conn = accept(sock_fd, (struct sockaddr *)&conn_addr, &addr_len);

客户端分为如下步骤:

  1. 创建一个socket:sock_fd = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
  2. 配置连接IP以及端口,然后直接连接:connect(conn_fd, (struct sockaddr *)&serv_addr, sizeof(struct sockaddr_in));
  • 6
    点赞
  • 19
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值