本测试基于lwip2.1.2。参考了CSDN博主「@则强」的文章
原文链接:https://blog.csdn.net/baidu_39191253/article/details/127630186
部分地方做了修改,增添了接收处理函数。方便调试。
此例子时采用lwip 非阻塞模式接收数据。然后通过select函数设置时间。间隔此时间查询接收数据然后再处理。接收到数据后直接转发出去。
平台采用GD32F470ZGT6的基于freertos的socket例程。(移植的lwip应该都可以用。包括正点原子,野火的例程。(tcp服务器例程) 编译时采用C99模式。
仅贴出tcp_ip任务的内容供参考。lwip_select函数这里主要用来做时间延迟。此函数会一直等待延迟时间,跟vTaskDelay函数类似。原因可跟踪内部函数,调用了二值信号量,应该是在等这个的时间。调试时间仓促。个别有理解错误的地方可以留言。函数测试通过。可以正常收发。
函数任务:
xTaskCreate(TCP_Server_task, "TCP_Server", DEFAULT_THREAD_STACKSIZE, NULL, TCP_SERVER_TASK_PRIO, NULL);
/*!
\brief TCP_Server task
\param[in] arg: user supplied argument
\param[out] none
\retval none
*/
static void TCP_Server_task(void *arg)
{
int ret;
int sockfd = -1, newfd = -1;
uint32_t len;
int tcp_port = 8000;
int recvnum;
struct sockaddr_in svr_addr, clt_addr;
char buf[50];
/* bind to port 8000 at any interface */
svr_addr.sin_family = AF_INET;
svr_addr.sin_port = htons(tcp_port);
svr_addr.sin_addr.s_addr = htons(INADDR_ANY);
name_recv.length = 0;
name_recv.done = 0;
while(1) {
/* create a TCP socket */
sockfd = socket(AF_INET, SOCK_STREAM, 0);
if(sockfd < 0) {
continue;
}
ret = bind(sockfd, (struct sockaddr *)&svr_addr, sizeof(svr_addr));
if(ret < 0) {
lwip_close(sockfd);
sockfd = -1;
continue;
}
unsigned long mode = 1;
if(ioctlsocket(sockfd, FIONBIO, &mode) != 0) {
printf("SOCKET: failed to set socket non-blocking!\n");
}
/* listen for incoming connections (TCP listen backlog = 1) */
ret = listen(sockfd, 1);
if(ret < 0) {
lwip_close(sockfd);
continue;
}
struct sockaddr_in sin; /* bind socket address */
socklen_t sinlen; /* length of address */
fd_set readfds;
fd_set errofds;
int ret, sock;
struct timeval timeout;
int timeoutMs = 10;
while (1)
{
/*
FD_ZERO(fd_set *fdset); // 将set清零使集合中不含任何fd
FD_SET(int fd, fd_set *fdset); // 将fd加入set集合
FD_CLR(int fd, fd_set *fdset); // 将fd从set集合中清除
FD_ISSET(int fd, fd_set *fdset); // 检测fd是否在set集合中,不在则返回0
调用FD_ZERO将一个 fd_set 变量的所有位设置为0。要开启描述符集中的一位,可以调用FD_SET。调用FD_CLR可以清除一位。最后,可以调用FD_ISSET测试描述符集中的一个指定位是否已打开。
*/
FD_ZERO(&readfds);
FD_SET(sockfd, &readfds);
FD_ZERO(&errofds);
FD_SET(sockfd, &errofds);
timeout.tv_sec = 1;
timeout.tv_usec = 1000 * 0;//设置超时时间
ret = select(sockfd + 1, &readfds, (fd_set *)0, &errofds, &timeout );
if (ret == 0){//返回值等于0表示超时
printf("wait connect\r\n");//等待连接,select函数跟vTaskDelay()函数类似,一直在此等待超时时间
continue;
}
else if (ret < 0){//返回值小于于0表示出错
printf("\n >>>>>> ret = %d\n",ret);
break;
}
if (FD_ISSET(sockfd, &errofds))
{
printf("\n >>>>>> exit ok!\n");
break;
}
sinlen = sizeof(sin);
if (FD_ISSET(sockfd, &readfds))
{
printf("\n >>>>>> read ok!\n");
sock = accept(sockfd, (struct sockaddr *)&sin, &sinlen);
if(sock < 0){
break;
}
else
{
while(1)//这里是接收函数,一直在这里循环
{
//连接之后,开始接收数据
FD_ZERO(&readfds);
FD_SET(sock, &readfds);
FD_ZERO(&errofds);
FD_SET(sock, &errofds);
int timeoutMs = 10;
timeout.tv_sec = timeoutMs / 1000;
timeout.tv_usec = (timeoutMs % 1000) * 1000;
ret = select(sock + 1, &readfds, (fd_set *)0, &errofds, &timeout );
//printf("delay and ret = %d\r\n",ret);//测试函数到此位置时间 select函数跟vTaskDelay()函数类似,一直在此等待超时时间
if (ret>0)
{//返回值等于0表示超时
recvnum = recv(sock, buf, MAX_NAME_SIZE, 0);
}
if(recvnum>0)
{
memcpy(name_recv.bytes, buf, recvnum);
name_recv.length = recvnum;
send(sock, name_recv.bytes, name_recv.length, 0);
recvnum = 0;
name_recv.length = 0;
}
}
}
}
}