LWIP配置

服务器端实现

客户端实现

错误分析

一。LWIP配置(FREERTOS配置,ETH配置,LWIP配置)

1.FREERTOS配置

 为什么要修改定时源为Tim1?不用systick?

原因:HAL库与FREERTOS都需要使用systick,两者冲突,所以修改时钟源,让FREERTOS使用Tim1。

 2.ETH配置

 3.LWIP配置

不使用DHCP

 4.步骤: (1)freertos.c中会自己出现一个Lwip初始化

运行后结果:命令行中输入ping 192.168.1.10有回复

 二。服务器端

实验一:《stm32作为服务器端,COMMBOX串口作为客户端》

1.功能分析

小写转大写

 2.步骤:

(1)建立socket_tcp_server.h

#ifndef SOCKET_TCP_SERVER_H

#define SOCKET_TCP_SERVER_H

#define SERVER_IP "192.168.1.11"

#define SERVER_PORT 6666

#define BUFF_SIZE 1024

void vTcpServerTask(void);

#endif

 (2)建立socket_tcp_server.c,并添加到文件中

#include "socket_tcp_server.h"

#include "lwip/sockets.h"

#include "ctype.h"

char ReadBuff[BUFF_SIZE];

/**

* @brief TCP 服务器任务

* @param None

* @retval None

*/

void vTcpServerTask(void){

int sfd, cfd, n, i;

struct sockaddr_in server_addr, client_addr;

socklen_t client_addr_len;

//创建socket

sfd = socket(AF_INET, SOCK_STREAM, 0);

server_addr.sin_family = AF_INET;

server_addr.sin_port = htons(SERVER_PORT);

server_addr.sin_addr.s_addr = htonl(INADDR_ANY);

//绑定socket

bind(sfd, (struct sockaddr *)&server_addr, sizeof(server_addr));

//监听socket

listen(sfd, 5);

//等待客户端连接

client_addr_len = sizeof(client_addr);

cfd = accept(sfd, (struct sockaddr *)&client_addr, &client_addr_len);

printf("client is connect cfd = %d\r\n",cfd);

while(1){

//等待客户端发送数据

n = read(cfd, ReadBuff, BUFF_SIZE);

//进行大小写转换

for(i = 0; i < n; i++){

ReadBuff[i] = toupper(ReadBuff[i]);

}

//写回客户端

write(cfd, ReadBuff, n);

}

}

(3)freertos.c中网络任务中,添加服务器运行函数。

 不要忘记添加头文件

vTcpServerTask();

3.现象演示

(1)使用COMMBOX串口调试工程,添加socket网络客户端 ,目标ip为stm32的ip.端口在.h中

(2)stm32客户端发送的字母变大写

 三。客户端创建

实验二:《stm32作为客户端,COMMBOX串口作为服务器端》

1.创建socket_tcp_client.c与socket_tcp_client.h

(1)socket_tcp_client.c

#include "socket_tcp_server.h"

#include "socket_tcp_client.h"

#include "lwip/sockets.h"

#include "ctype.h"

static char ReadBuff[BUFF_SIZE];

void vTcpClientTask(void)

{

int cfd, n, i;

struct sockaddr_in server_addr;

//创建socket

cfd = socket(AF_INET, SOCK_STREAM, 0);

server_addr.sin_family = AF_INET;

server_addr.sin_port = htons(SERVER_PORT);

server_addr.sin_addr.s_addr = inet_addr(SERVER_IP);

//连接到服务器

connect(cfd, (struct sockaddr*)&server_addr, sizeof(server_addr));

printf("server is connect ok\r\n");

while(1){

//等待服务器发送数据

n = read(cfd, ReadBuff, BUFF_SIZE);

//进行大小写转换

for(i = 0; i < n; i++){

ReadBuff[i] = toupper(ReadBuff[i]);

}

//写回服务器

write(cfd, ReadBuff, n);

}

}

(2)socket_tcp_client.h

#ifndef _SOCKET_TCP_CLIENT_H

#define _SOCKET_TCP_CLIENT_H

void vTcpClientTask(void);

#endif

可能遇到的问题:

        由于打开了防火墙,所以无法连接

 

 补充:上述代码的问题

        1.代码封装性不好

        2.代码对边界错误提示太少

四。代码的优化

1.函数在封装,文件为socket_warp.c与socket_warp.h

(1)socket_warp.h

#ifndef _SOCKET_WRAP_H

#define _SOCKET_WRAP_H

#include "lwip/sockets.h"

int Socket(int domain, int type, int protocol);

int Bind(int sockfd, const struct sockaddr *addr, socklen_t addrlen);

int Listen(int sockfd, int backlog);

int Accept(int sockfd, struct sockaddr *addr, socklen_t *addrlen);

int Connect(int sockfd, const struct sockaddr *addr, socklen_t addrlen);

int Write(int fd,const void *buf,size_t nbytes);

int Read(int fd,void *buf,size_t nbyte);

int Sendto(int sockfd, const void *msg, int len, unsigned int flags, const struct sockaddr *to, int tolen);

int Recvfrom(int sockfd, void *buf, int len, unsigned int flags, struct sockaddr *from, socklen_t *fromlen);

#endif

(2)socket_warp.c

#include "socket_wrap.h"

#include "FreeRTOS.h"

#include "task.h"

int Socket(int domain, int type, int protocol){

int fd;

fd=socket(domain,type,protocol);

if(fd<0){

printf("create socket error\n");

//没有创建完成,那么这个任务也没有必要运行,直接切换上下文

//返回一个小于零的数

vTaskDelete(NULL);

}

return fd;

}

int Bind(int sockfd, const struct sockaddr *addr, socklen_t addrlen){

int ret;

ret=bind(sockfd,addr,addrlen);

if(ret < 0){

printf("bind socket error\r\n");

//当调用删除任务,就会切换上下文,CPU执行其他任务

vTaskDelete(NULL);

}

return ret;

}

int Listen(int sockfd, int backlog){

int ret;

ret=listen(sockfd,backlog);

if(ret<0){

printf("listen socket error\n");

vTaskDelete(NULL);

}

return ret;

}

int Accept(int sockfd, struct sockaddr *addr, socklen_t *addrlen){

int fd;

again:

fd=accept(sockfd,addr,addrlen);

if(fd<0){

printf("accept socket error\n");

goto again;

}

return fd;

}

int Connect(int sockfd, const struct sockaddr *addr, socklen_t addrlen){

int ret;

ret=connect(sockfd,addr,addrlen);

if(ret<0){

printf("connect socket error\n");

//先关闭当前的socket,其实内部是删除这个socket的内存块,不删除会导致下次无法生成

close(sockfd);

}

return ret;

}

int Write(int fd,const void *buf,size_t nbytes){

int ret;

ret=write(fd,buf,nbytes);

//没有书写完成就关闭socket

if(ret<0){

printf("write socket error\n");

close(fd);

}

return ret;

}

int Read(int fd,void *buf,size_t nbyte){

int ret;

ret=read(fd,buf,nbyte);

if(ret==0){

printf("read socket is close\n");

close(fd);

}else if(ret<0){

printf("read socket error\n");

close(fd);

}

}

int Sendto(int sockfd, const void *msg, int len, unsigned int flags, const struct sockaddr *to, int tolen){

int ret;

again:

ret=sendto(sockfd,msg,len,flags,to,tolen);

if(ret<0){

printf("sendto socket error\n");

goto again;

}

return ret;

}

int Recvfrom(int sockfd, void *buf, int len, unsigned int flags, struct sockaddr *from, socklen_t *fromlen){

int ret;

again:

ret=recvfrom(sockfd,buf,len,flags,from,fromlen);

if(ret<0){

printf("recvfrom socket error\n");

goto again;

}

return ret;

}

上述为封装函数,包括tcp_server,tcp_client已经后续的udp_server

2.socket_tcp_server.c与socket_tcp_client.c

1.socket_tcp_server.c

#include "socket_udp_server.h"

#include "socket_tcp_server.h"

#include "socket_wrap.h"

#include "ctype.h"

static char ReadBuff[BUFF_SIZE];

void vUdpServerTask(){

int sfd, n, i;

struct sockaddr_in server_addr, client_addr;

socklen_t client_addr_len;

int optval=1;

//创建socket

sfd=Socket(AF_INET, SOCK_DGRAM, 0);

setsockopt(sfd,SOL_SOCKET ,SO_BROADCAST,&optval,sizeof(optval));

//绑定socket

server_addr.sin_family = AF_INET;

server_addr.sin_port = htons(SERVER_PORT);

server_addr.sin_addr.s_addr = htonl(INADDR_ANY);

Bind(sfd, (struct sockaddr *)&server_addr, sizeof(server_addr));

//处理

client_addr_len=sizeof(client_addr);

while(1){

//等待客户端发送数据

n = Recvfrom(sfd, ReadBuff, BUFF_SIZE, 0, (struct sockaddr *)&client_addr, &client_addr_len);

ReadBuff[n] = '\0';

printf("recv data:%s\r\n",ReadBuff);

//进行大小写转换

for(i = 0; i < n; i++){

ReadBuff[i] = toupper(ReadBuff[i]);

}

//写回客户端

Sendto(sfd, ReadBuff, n, 0, (struct sockaddr *)&client_addr, client_addr_len);

}

}

2.socket_tcp_client.c

#include "socket_tcp_server.h"

#include "socket_tcp_client.h"

#include "socket_wrap.h"

#include "ctype.h"

#include "FreeRTOS.h"

#include "task.h"

#include "string.h"

static char ReadBuff[BUFF_SIZE];

void vTcpClientTask(void)

{

int cfd, n, i, ret;

struct sockaddr_in server_addr;

// int so_reuseaddr_val = 1;

again:

//创建socket

cfd = Socket(AF_UNSPEC, SOCK_STREAM, 0);

//使能socket层 心跳检测

// setsockopt(cfd, SOL_SOCKET, SO_REUSEADDR, &so_reuseaddr_val, sizeof(int));

server_addr.sin_family = AF_INET;

server_addr.sin_port = htons(SERVER_PORT);

server_addr.sin_addr.s_addr = inet_addr(SERVER_IP);

//连接到服务器

//connect 其实是一个阻塞接口,内部要完成TCP的三次握手,当然有超时机制,所以我们需要等一段时间,才能重新连接到服务器

ret = connect(cfd, (struct sockaddr*)&server_addr, sizeof(server_addr));

if(ret < 0){

//100ms去连接一次服务器

vTaskDelay(1000);

printf("connect fail\r\n");

goto again;

}

printf("server is connect ok\r\n");

while(1){

//等待服务器发送数据

n = Read(cfd, ReadBuff, BUFF_SIZE);

if(n <= 0){

goto again;

}

//进行大小写转换

for(i = 0; i < n; i++){

ReadBuff[i] = toupper(ReadBuff[i]);

}

//写回服务器

n = Write(cfd, ReadBuff, n);

if(n <= 0){

goto again;

}

}

}

结果:pc端的COMMBOX创建或者关闭服务器或者客户端时,串口都会打印出内容,提示打开或者关闭。

补充:

1.当stm32作为服务器端时:

2.当stm32作为客户端时:

 所以我们本机配置也应该为:192.168.1.11

(1) stm32开始运行后,wifi中会出现一个未识别网络,点击他。

(2)点击更改适配器选项

 (3)配置:右键--》属性

(4)配置IPv4

 

(5)电脑配置:自己的IP地址为192.168.1.11(当然127.0.0.1也可以,表示自己)

综上:

(1)stm32的ip在cubemx上创建

(2)电脑的IP在网络的适配器上设置

文章来源

评论可见,请评论后查看内容,谢谢!!!评论后请刷新页面。