#include <FreeRTOS.h>
#include <lwip/netdb.h>
#include <lwip/sockets.h>
#include <task.h>
#include "common.h"
/*
** 此文件用到了errno这个变量, 默认情况下不是线程安全的
** 为了使errno变量变为线程安全, 在common.c中已通过重写Keil MDK的__aeabi_errno_addr函数
** 重新指定errno变量的存放位置, 与FreeRTOS系统的FreeRTOS_errno绑定
**
** 输出不以\0结尾的字符串的方法:
** char str[5] = {'H', 'e', 'l', 'l', 'o'};
** printf("str=%.5s\n", str);
** 或
** printf("str=%.*s\n", sizeof(str), str);
**
**
** 双引号字符串自动包含\0
** char *str = "Hello"; // 只读字符串
** char str[] = "Hello"; // 可读可写的字符串
** 可直接用%s输出
** printf("str=%s\n", str);
**
** 通常, 网络收到的数据p->payload是不含\0的
** 所以不能用%s输出, 应该使用%.*s输出
** printf("Data: %.*s\n", p->len, p->payload);
*/
/*** [DNS TEST] ***/
#if LWIP_DNS
static void dns_test_task(void *arg)
{
char buffer[1024];
char *name = "savannah.nongnu.org";
char *request = "GET /news/?group=lwip HTTP/1.1\r\nHost: savannah.nongnu.org\r\n\r\n";
int count = 0;
int ipver = (int)arg;
int ret;
int retry = 6;
int sockfd = -1;
struct addrinfo hints = {0};
struct addrinfo *result = NULL;
switch (ipver)
{
case 4:
hints.ai_family = AF_INET;
break;
case 6:
hints.ai_family = AF_INET6;
break;
default:
hints.ai_family = AF_UNSPEC;
break;
}
hints.ai_socktype = SOCK_STREAM;
hints.ai_protocol = IPPROTO_TCP;
hints.ai_flags = AI_ADDRCONFIG | AI_NUMERICSERV | AI_V4MAPPED;
retry:
ret = getaddrinfo(name, "80", &hints, &result);
if (ret == EAI_MEMORY)
{
printf_s("%s(%d): getaddrinfo() failed due to out of memory!\n", __FUNCTION__, ipver);
if (retry == 0)
goto end;
else
{
printf_s("%s(%d): try again 10 seconds later.\n", __FUNCTION__, ipver);
vTaskDelay(pdMS_TO_TICKS(10000));
retry--;
goto retry;
}
}
else if (ret != 0 || result == NULL)
{
printf_s("%s(%d): getaddrinfo() failed! ret=%d, errno=%d\n", __FUNCTION__, ipver, ret, errno);
goto end;
}
switch (result->ai_family)
{
case AF_INET:
inet_ntop(AF_INET, &((struct sockaddr_in *)result->ai_addr)->sin_addr, buffer, sizeof(buffer));
break;
case AF_INET6:
inet_ntop(AF_INET6, &((struct sockaddr_in6 *)result->ai_addr)->sin6_addr, buffer, sizeof(buffer));
break;
default:
printf_s("%s(%d): invalid result->ai_family=%d\n", __FUNCTION__, ipver, result->ai_family);
goto end;
}
printf_s("DNS Found IP of %s: %s\n", name, buffer);
// 访问网页http://savannah.nongnu.org/news/?group=lwip
// 注意: 现在绝大多数网站用的都是加密版本的https, 而下面的代码只能连接不加密的http网站
// 所以下面的代码无法访问百度
sockfd = socket(result->ai_family, result->ai_socktype, result->ai_protocol);
if (sockfd == -1)
{
printf_s("%s(%d): socket() failed! errno=%d\n", __FUNCTION__, ipver, errno);
goto end;
}
printf_s("TCP socket is connecting to %s...\n", buffer);
ret = connect(sockfd, result->ai_addr, result->ai_addrlen);
if (ret == -1)
{
printf_s("TCP socket connection failed! errno=%d\n", errno);
goto end;
}
printf_s("TCP socket is connected!\n");
send(sockfd, request, strlen(request), 0); // 发送HTTP请求头
while (1)
{
ret = recv(sockfd, buffer, sizeof(buffer), 0);
if (ret > 0)
{
printf_s("%s(%d): %d bytes received!\n", __FUNCTION__, ipver, ret);
count += ret;
}
else if (ret == 0)
{
printf_s("TCP socket is closed! count=%d\n", count);
break;
}
else
{
printf_s("TCP socket error! ret=%d, errno=%d\n", ret, errno);
break;
}
}
end:
if (result != NULL)
freeaddrinfo(result);
if (sockfd != -1)
close(sockfd);
vTaskDelete(NULL);
}
void dns_test(int ipver)
{
BaseType_t ret;
ret = xTaskCreate(dns_test_task, "dns_test", 768, (void *)ipver, tskIDLE_PRIORITY, NULL);
if (ret != pdPASS)
printf_s("%s: failed to create a task! ret=%d\n", __FUNCTION__, ret);
}
#endif
/*** [TCP SPEED TEST] ***/
static uint8_t tcp_tester_buffer[15000];
static void tcp_tester_client_task(void *arg)
{
char ip[INET6_ADDRSTRLEN];
char rcvbuf[10];
fd_set readset, writeset;
int clientfd = (int)arg;
int port, ret;
socklen_t addrlen;
struct sockaddr_in6 addr;
struct timeval tv = {0};
addrlen = sizeof(addr);
ret = getpeername(clientfd, (struct sockaddr *)&addr, &addrlen);
if (ret == -1)
{
printf_s("%s: getpeername() failed! errno=%d\n", __FUNCTION__, errno);
goto err;
}
inet_ntop(AF_INET6, &addr.sin6_addr, ip, sizeof(ip));
port = ntohs(addr.sin6_port);
printf_s("TCP tester accepted [%s]:%d!\n", ip, port);
FD_ZERO(&readset);
FD_ZERO(&writeset);
while (1)
{
FD_SET(clientfd, &readset);
FD_SET(clientfd, &writeset);
ret = select(clientfd + 1, &readset, &writeset, NULL, &tv);
if (ret == -1)
{
printf_s("%s: select() failed! errno=%d\n", __FUNCTION__, errno);
break;
}
else if (FD_ISSET(clientfd, &readset))
{
// 有数据可接收
ret = recv(clientfd, rcvbuf, sizeof(rcvbuf), 0);
printf_s("TCP tester received %d bytes from [%s]:%d!\n", ret, ip, port);
printf_s("TCP tester stopped sending data!\n");
break;
}
else if (FD_ISSET(clientfd, &writeset))
{
// 发送数据
ret = send(clientfd, tcp_tester_buffer, sizeof(tcp_tester_buffer), 0);
if (ret == -1)
{
printf_s("%s: send() failed! errno=%d\n", __FUNCTION__, errno);
break;
}
}
}
printf_s("TCP tester client [%s]:%d closed!\n", ip, port);
err:
close(clientfd);
vTaskDelete(NULL);
}
static void tcp_tester_task(void *arg)
{
int clientfd, serverfd = -1;
int ret;
socklen_t addrlen;
struct sockaddr_in6 addr = {0};
serverfd = socket(AF_INET6, SOCK_STREAM, IPPROTO_TCP);
if (serverfd == -1)
{
printf_s("%s: socket() failed! errno=%d\n", __FUNCTION__, errno);
goto err;
}
addr.sin6_family = AF_INET6;
addr.sin6_port = htons(24001);
ret = bind(serverfd, (struct sockaddr *)&addr, sizeof(addr));
if (ret == -1)
{
printf_s("%s: bind() failed! errno=%d\n", __FUNCTION__, errno);
goto err;
}
ret = listen(serverfd, 5);
if (ret == -1)
{
printf_s("%s: listen() failed! errno=%d\n", __FUNCTION__, errno);
goto err;
}
while (1)
{
addrlen = sizeof(addr);
clientfd = accept(serverfd, (struct sockaddr *)&addr, &addrlen);
if (clientfd != -1)
{
ret = xTaskCreate(tcp_tester_client_task, "tcp_tester_client", 384, (void *)clientfd, tskIDLE_PRIORITY, NULL);
if (ret != pdPASS)
{
printf_s("%s: could not accept a new client!\n", __FUNCTION__);
close(clientfd);
}
}
else
printf_s("%s: accept() failed! errno=%d\n", __FUNCTION__, errno);
}
err:
if (serverfd != -1)
close(serverfd);
vTaskDelete(NULL);
}
void tcp_tester_init(void)
{
BaseType_t ret;
ret = xTaskCreate(tcp_tester_task, "tcp_tester", 256, NULL, tskIDLE_PRIORITY, NULL);
if (ret != pdPASS)
printf_s("%s: failed to create a task! ret=%d\n", __FUNCTION__, ret);
}
/*** [UDP TEST] ***/
struct test
{
uint32_t id;
uint32_t count;
};
static void udp_tester_task(void *arg)
{
char ip[INET6_ADDRSTRLEN];
int ret;
int serverfd = -1;
socklen_t addrlen;
struct sockaddr_in6 addr = {0};
struct test *t = (struct test *)tcp_tester_buffer;
serverfd = socket(AF_INET6, SOCK_DGRAM, IPPROTO_UDP);
if (serverfd == -1)
{
printf_s("%s: socket() failed! errno=%d\n", __FUNCTION__, errno);
goto err;
}
addr.sin6_family = AF_INET6;
addr.sin6_port = htons(24002);
ret = bind(serverfd, (struct sockaddr *)&addr, sizeof(addr));
if (ret == -1)
{
printf_s("%s: bind() failed! errno=%d\n", __FUNCTION__, errno);
goto err;
}
LWIP_ASSERT("sizeof(struct test) <= sizeof(tcp_tester_buffer)", sizeof(struct test) <= sizeof(tcp_tester_buffer));
while (1)
{
addrlen = sizeof(addr);
ret = recvfrom(serverfd, tcp_tester_buffer, sizeof(tcp_tester_buffer), 0, (struct sockaddr *)&addr, &addrlen);
if (ret != -1)
{
// 每从客户端收到一个数据包, 都发送一批数据包作为回应
inet_ntop(AF_INET6, &addr.sin6_addr, ip, sizeof(ip));
printf_s("Sending UDP packets to [%s]:%d...\n", ip, ntohs(addr.sin6_port));
t->count = 1024;
for (t->id = 0; t->id < t->count; t->id++)
{
ret = sendto(serverfd, tcp_tester_buffer, 1300, 0, (struct sockaddr *)&addr, sizeof(addr));
if (ret == -1)
{
printf_s("%s: sendto() failed! errno=%d, t->id=%d\n", __FUNCTION__, errno, t->id);
break;
}
}
}
else
printf_s("%s: recvfrom() failed! errno=%d\n", __FUNCTION__, errno);
}
err:
if (serverfd != -1)
close(serverfd);
vTaskDelete(NULL);
}
void udp_tester_init(void)
{
BaseType_t ret;
ret = xTaskCreate(udp_tester_task, "udp_tester", 384, NULL, tskIDLE_PRIORITY, NULL);
if (ret != pdPASS)
printf_s("%s: failed to create a task! ret=%d\n", __FUNCTION__, ret);
}