examples: unify iperf component

This commit is contained in:
SalimTerryLi 2021-07-07 15:18:20 +08:00
parent 32d9852625
commit 1abf5c861d
No known key found for this signature in database
GPG Key ID: F05CCEF2191AF770
18 changed files with 28 additions and 532 deletions

View File

@ -3,7 +3,8 @@
cmake_minimum_required(VERSION 3.5)
set(EXTRA_COMPONENT_DIRS $ENV{IDF_PATH}/examples/bluetooth/esp_ble_mesh/common_components/example_init
$ENV{IDF_PATH}/examples/bluetooth/esp_ble_mesh/common_components/fast_provisioning)
$ENV{IDF_PATH}/examples/bluetooth/esp_ble_mesh/common_components/fast_provisioning
$ENV{IDF_PATH}/examples/common_components/iperf)
include($ENV{IDF_PATH}/tools/cmake/project.cmake)
project(ble_mesh_wifi_coexist)

View File

@ -5,7 +5,8 @@
PROJECT_NAME := ble_mesh_wifi_coexist
EXTRA_COMPONENT_DIRS := $(IDF_PATH)/examples/bluetooth/esp_ble_mesh/common_components/example_init \
$(IDF_PATH)/examples/bluetooth/esp_ble_mesh/common_components/fast_provisioning
EXTRA_COMPONENT_DIRS := $(IDF_PATH)/examples/bluetooth/esp_ble_mesh/common_components/example_init
EXTRA_COMPONENT_DIRS += $(IDF_PATH)/examples/bluetooth/esp_ble_mesh/common_components/fast_provisioning
EXTRA_COMPONENT_DIRS += $(IDF_PATH)/examples/common_components/iperf
include $(IDF_PATH)/make/project.mk

View File

@ -1,3 +0,0 @@
idf_component_register(SRCS "cmd_wifi.c" "iperf.c"
INCLUDE_DIRS "."
REQUIRES lwip console)

View File

@ -1,11 +0,0 @@
#
# Component Makefile
#
# This Makefile should, at the very least, just include $(SDK_PATH)/Makefile. By default,
# this will take the sources in the src/ directory, compile them and link them into
# lib(subdirectory_name).a in the build directory. This behaviour is entirely configurable,
# please read the SDK documents if you need to do this.
#
#include $(IDF_PATH)/make/component_common.mk
COMPONENT_ADD_INCLUDEDIRS := .

View File

@ -1,449 +0,0 @@
/* Iperf Example - iperf implementation
This example code is in the Public Domain (or CC0 licensed, at your option.)
Unless required by applicable law or agreed to in writing, this
software is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
CONDITIONS OF ANY KIND, either express or implied.
*/
#include <sys/socket.h>
#include <stdio.h>
#include <string.h>
#include "esp_log.h"
#include "iperf.h"
typedef struct {
iperf_cfg_t cfg;
bool finish;
uint32_t total_len;
uint32_t buffer_len;
uint8_t *buffer;
uint32_t sockfd;
} iperf_ctrl_t;
typedef struct {
int32_t id;
uint32_t sec;
uint32_t usec;
} iperf_udp_pkt_t;
static bool s_iperf_is_running = false;
static iperf_ctrl_t s_iperf_ctrl;
static const char *TAG = "iperf";
inline static bool iperf_is_udp_client(void)
{
return ((s_iperf_ctrl.cfg.flag & IPERF_FLAG_CLIENT) && (s_iperf_ctrl.cfg.flag & IPERF_FLAG_UDP));
}
inline static bool iperf_is_udp_server(void)
{
return ((s_iperf_ctrl.cfg.flag & IPERF_FLAG_SERVER) && (s_iperf_ctrl.cfg.flag & IPERF_FLAG_UDP));
}
inline static bool iperf_is_tcp_client(void)
{
return ((s_iperf_ctrl.cfg.flag & IPERF_FLAG_CLIENT) && (s_iperf_ctrl.cfg.flag & IPERF_FLAG_TCP));
}
inline static bool iperf_is_tcp_server(void)
{
return ((s_iperf_ctrl.cfg.flag & IPERF_FLAG_SERVER) && (s_iperf_ctrl.cfg.flag & IPERF_FLAG_TCP));
}
int iperf_get_socket_error_code(int sockfd)
{
return errno;
}
int iperf_show_socket_error_reason(const char *str, int sockfd)
{
int err = errno;
if (err != 0) {
ESP_LOGW(TAG, "%s error, error code: %d, reason: %s", str, err, strerror(err));
}
return err;
}
void iperf_report_task(void *arg)
{
TickType_t delay_interval = (s_iperf_ctrl.cfg.interval * 1000) / portTICK_RATE_MS;
uint32_t interval = s_iperf_ctrl.cfg.interval;
uint32_t time = s_iperf_ctrl.cfg.time;
uint32_t last_len = 0;
uint32_t cur = 0;
printf("\n%16s %s\n", "Interval", "Bandwidth");
while (!s_iperf_ctrl.finish) {
vTaskDelay(delay_interval);
printf("%4d-%4d sec %.2f Mbits/sec\n", cur, cur + interval, (double)((s_iperf_ctrl.total_len - last_len) * 8) / interval / 1e6);
cur += interval;
last_len = s_iperf_ctrl.total_len;
if (cur == time) {
break;
}
}
if (cur != 0) {
printf("%4d-%4d sec %.2f Mbits/sec\n", 0, time, (double)(s_iperf_ctrl.total_len * 8) / cur / 1e6);
}
s_iperf_ctrl.finish = true;
vTaskDelete(NULL);
}
esp_err_t iperf_start_report(void)
{
int ret;
ret = xTaskCreatePinnedToCore(iperf_report_task, IPERF_REPORT_TASK_NAME, IPERF_REPORT_TASK_STACK, NULL, IPERF_REPORT_TASK_PRIORITY, NULL, portNUM_PROCESSORS - 1);
if (ret != pdPASS) {
ESP_LOGE(TAG, "create task %s failed", IPERF_REPORT_TASK_NAME);
return ESP_FAIL;
}
return ESP_OK;
}
esp_err_t iperf_run_tcp_server(void)
{
socklen_t addr_len = sizeof(struct sockaddr);
struct sockaddr_in remote_addr;
struct sockaddr_in addr;
int actual_recv = 0;
int want_recv = 0;
uint8_t *buffer;
int listen_socket;
struct timeval t;
int sockfd;
int opt;
listen_socket = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
if (listen_socket < 0) {
iperf_show_socket_error_reason("tcp server create", listen_socket);
return ESP_FAIL;
}
setsockopt(listen_socket, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof(opt));
addr.sin_family = AF_INET;
addr.sin_port = htons(s_iperf_ctrl.cfg.sport);
addr.sin_addr.s_addr = s_iperf_ctrl.cfg.sip;
if (bind(listen_socket, (struct sockaddr *)&addr, sizeof(addr)) != 0) {
iperf_show_socket_error_reason("tcp server bind", listen_socket);
close(listen_socket);
return ESP_FAIL;
}
if (listen(listen_socket, 5) < 0) {
iperf_show_socket_error_reason("tcp server listen", listen_socket);
close(listen_socket);
return ESP_FAIL;
}
iperf_start_report();
buffer = s_iperf_ctrl.buffer;
want_recv = s_iperf_ctrl.buffer_len;
while (!s_iperf_ctrl.finish) {
/*TODO need to change to non-block mode */
sockfd = accept(listen_socket, (struct sockaddr *)&remote_addr, &addr_len);
if (sockfd < 0) {
iperf_show_socket_error_reason("tcp server listen", listen_socket);
close(listen_socket);
return ESP_FAIL;
} else {
printf("accept: %s,%d\n", inet_ntoa(remote_addr.sin_addr), htons(remote_addr.sin_port));
t.tv_sec = IPERF_SOCKET_RX_TIMEOUT;
setsockopt(sockfd, SOL_SOCKET, SO_RCVTIMEO, &t, sizeof(t));
}
while (!s_iperf_ctrl.finish) {
actual_recv = recv(sockfd, buffer, want_recv, 0);
if (actual_recv < 0) {
iperf_show_socket_error_reason("tcp server recv", listen_socket);
break;
} else {
s_iperf_ctrl.total_len += actual_recv;
}
}
close(sockfd);
}
s_iperf_ctrl.finish = true;
close(listen_socket);
return ESP_OK;
}
esp_err_t IRAM_ATTR iperf_run_udp_server(void)
{
socklen_t addr_len = sizeof(struct sockaddr_in);
struct sockaddr_in addr;
int actual_recv = 0;
struct timeval t;
int want_recv = 0;
uint8_t *buffer;
int sockfd;
int opt;
sockfd = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);
if (sockfd < 0) {
iperf_show_socket_error_reason("udp server create", sockfd);
return ESP_FAIL;
}
setsockopt(sockfd, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof(opt));
addr.sin_family = AF_INET;
addr.sin_port = htons(s_iperf_ctrl.cfg.sport);
addr.sin_addr.s_addr = s_iperf_ctrl.cfg.sip;
if (bind(sockfd, (struct sockaddr *)&addr, sizeof(addr)) != 0) {
iperf_show_socket_error_reason("udp server bind", sockfd);
return ESP_FAIL;
}
addr.sin_family = AF_INET;
addr.sin_port = htons(s_iperf_ctrl.cfg.sport);
addr.sin_addr.s_addr = s_iperf_ctrl.cfg.sip;
iperf_start_report();
buffer = s_iperf_ctrl.buffer;
want_recv = s_iperf_ctrl.buffer_len;
ESP_LOGI(TAG, "want recv=%d", want_recv);
t.tv_sec = IPERF_SOCKET_RX_TIMEOUT;
setsockopt(sockfd, SOL_SOCKET, SO_RCVTIMEO, &t, sizeof(t));
while (!s_iperf_ctrl.finish) {
actual_recv = recvfrom(sockfd, buffer, want_recv, 0, (struct sockaddr *)&addr, &addr_len);
if (actual_recv < 0) {
iperf_show_socket_error_reason("udp server recv", sockfd);
} else {
s_iperf_ctrl.total_len += actual_recv;
}
}
s_iperf_ctrl.finish = true;
close(sockfd);
return ESP_OK;
}
esp_err_t iperf_run_udp_client(void)
{
struct sockaddr_in addr;
iperf_udp_pkt_t *udp;
int actual_send = 0;
bool retry = false;
uint32_t delay = 1;
int want_send = 0;
uint8_t *buffer;
int sockfd;
int opt;
int err;
int id;
sockfd = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);
if (sockfd < 0) {
iperf_show_socket_error_reason("udp server create", sockfd);
return ESP_FAIL;
}
setsockopt(sockfd, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof(opt));
addr.sin_family = AF_INET;
addr.sin_port = 0;
addr.sin_addr.s_addr = s_iperf_ctrl.cfg.sip;
if (bind(sockfd, (struct sockaddr *)&addr, sizeof(addr)) != 0) {
iperf_show_socket_error_reason("udp server bind", sockfd);
return ESP_FAIL;
}
addr.sin_family = AF_INET;
addr.sin_port = htons(s_iperf_ctrl.cfg.dport);
addr.sin_addr.s_addr = s_iperf_ctrl.cfg.dip;
iperf_start_report();
buffer = s_iperf_ctrl.buffer;
udp = (iperf_udp_pkt_t *)buffer;
want_send = s_iperf_ctrl.buffer_len;
id = 0;
while (!s_iperf_ctrl.finish) {
if (false == retry) {
id ++;
udp->id = htonl(id);
delay = 1;
}
retry = false;
actual_send = sendto(sockfd, buffer, want_send, 0, (struct sockaddr *)&addr, sizeof(addr));
if (actual_send != want_send) {
err = iperf_get_socket_error_code(sockfd);
if (err == ENOMEM) {
vTaskDelay(delay);
if (delay < IPERF_MAX_DELAY) {
delay <<= 1;
}
retry = true;
continue;
} else {
ESP_LOGE(TAG, "udp client send abort: err=%d", err);
break;
}
} else {
s_iperf_ctrl.total_len += actual_send;
}
}
s_iperf_ctrl.finish = true;
close(sockfd);
return ESP_OK;
}
esp_err_t iperf_run_tcp_client(void)
{
struct sockaddr_in local_addr;
struct sockaddr_in remote_addr;
int actual_send = 0;
int want_send = 0;
uint8_t *buffer;
int sockfd;
int opt;
sockfd = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
if (sockfd < 0) {
iperf_show_socket_error_reason("tcp client create", sockfd);
return ESP_FAIL;
}
setsockopt(sockfd, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof(opt));
memset(&local_addr, 0, sizeof(local_addr));
local_addr.sin_family = AF_INET;
local_addr.sin_port = 0;
local_addr.sin_addr.s_addr = s_iperf_ctrl.cfg.sip;
if (bind(sockfd, (struct sockaddr *)&local_addr, sizeof(local_addr)) != 0) {
iperf_show_socket_error_reason("tcp client bind", sockfd);
return ESP_FAIL;
}
memset(&remote_addr, 0, sizeof(remote_addr));
remote_addr.sin_family = AF_INET;
remote_addr.sin_port = htons(s_iperf_ctrl.cfg.dport);
remote_addr.sin_addr.s_addr = s_iperf_ctrl.cfg.dip;
if (connect(sockfd, (struct sockaddr *)&remote_addr, sizeof(remote_addr)) < 0) {
iperf_show_socket_error_reason("tcp client connect", sockfd);
return ESP_FAIL;
}
iperf_start_report();
buffer = s_iperf_ctrl.buffer;
want_send = s_iperf_ctrl.buffer_len;
while (!s_iperf_ctrl.finish) {
actual_send = send(sockfd, buffer, want_send, 0);
if (actual_send <= 0) {
iperf_show_socket_error_reason("tcp client send", sockfd);
break;
} else {
s_iperf_ctrl.total_len += actual_send;
}
}
close(sockfd);
return ESP_OK;
}
void iperf_task_traffic(void *arg)
{
if (iperf_is_udp_client()) {
iperf_run_udp_client();
} else if (iperf_is_udp_server()) {
iperf_run_udp_server();
} else if (iperf_is_tcp_client()) {
iperf_run_tcp_client();
} else {
iperf_run_tcp_server();
}
if (s_iperf_ctrl.buffer) {
free(s_iperf_ctrl.buffer);
s_iperf_ctrl.buffer = 0;
}
ESP_LOGI(TAG, "iperf exit");
s_iperf_is_running = false;
vTaskDelete(NULL);
}
uint32_t iperf_get_buffer_len(void)
{
if (iperf_is_udp_client()) {
return IPERF_UDP_TX_LEN;
} else if (iperf_is_udp_server()) {
return IPERF_UDP_RX_LEN;
} else if (iperf_is_tcp_client()) {
return IPERF_TCP_TX_LEN;
} else {
return IPERF_TCP_RX_LEN;
}
return 0;
}
esp_err_t iperf_start(iperf_cfg_t *cfg)
{
int ret;
if (!cfg) {
return ESP_FAIL;
}
if (s_iperf_is_running) {
ESP_LOGW(TAG, "iperf is running");
return ESP_FAIL;
}
memset(&s_iperf_ctrl, 0, sizeof(s_iperf_ctrl));
memcpy(&s_iperf_ctrl.cfg, cfg, sizeof(*cfg));
s_iperf_is_running = true;
s_iperf_ctrl.finish = false;
s_iperf_ctrl.buffer_len = iperf_get_buffer_len();
s_iperf_ctrl.buffer = (uint8_t *)malloc(s_iperf_ctrl.buffer_len);
if (!s_iperf_ctrl.buffer) {
ESP_LOGE(TAG, "create buffer: out of memory");
return ESP_FAIL;
}
memset(s_iperf_ctrl.buffer, 0, s_iperf_ctrl.buffer_len);
ret = xTaskCreatePinnedToCore(iperf_task_traffic, IPERF_TRAFFIC_TASK_NAME, IPERF_TRAFFIC_TASK_STACK, NULL, IPERF_TRAFFIC_TASK_PRIORITY, NULL, portNUM_PROCESSORS - 1);
if (ret != pdPASS) {
ESP_LOGE(TAG, "create task %s failed", IPERF_TRAFFIC_TASK_NAME);
free(s_iperf_ctrl.buffer);
s_iperf_ctrl.buffer = 0;
return ESP_FAIL;
}
return ESP_OK;
}
esp_err_t iperf_stop(void)
{
if (s_iperf_is_running) {
s_iperf_ctrl.finish = true;
}
while (s_iperf_is_running) {
ESP_LOGI(TAG, "wait current iperf to stop ...");
vTaskDelay(300 / portTICK_RATE_MS);
}
return ESP_OK;
}

View File

@ -1,61 +0,0 @@
/* Iperf Example - iperf declaration
This example code is in the Public Domain (or CC0 licensed, at your option.)
Unless required by applicable law or agreed to in writing, this
software is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
CONDITIONS OF ANY KIND, either express or implied.
*/
#ifndef __IPERF_H_
#define __IPERF_H_
#ifdef __cplusplus
extern "C" {
#endif
#define IPERF_FLAG_CLIENT (1)
#define IPERF_FLAG_SERVER (1<<1)
#define IPERF_FLAG_TCP (1<<2)
#define IPERF_FLAG_UDP (1<<3)
#define IPERF_DEFAULT_PORT 5001
#define IPERF_DEFAULT_INTERVAL 3
#define IPERF_DEFAULT_TIME 30
#define IPERF_TRAFFIC_TASK_NAME "iperf_traffic"
#define IPERF_TRAFFIC_TASK_PRIORITY 10
#define IPERF_TRAFFIC_TASK_STACK 4096
#define IPERF_REPORT_TASK_NAME "iperf_report"
#define IPERF_REPORT_TASK_PRIORITY 20
#define IPERF_REPORT_TASK_STACK 4096
#define IPERF_REPORT_TASK_NAME "iperf_report"
#define IPERF_UDP_TX_LEN (1472)
#define IPERF_UDP_RX_LEN (16<<10)
#define IPERF_TCP_TX_LEN (16<<10)
#define IPERF_TCP_RX_LEN (16<<10)
#define IPERF_MAX_DELAY 64
#define IPERF_SOCKET_RX_TIMEOUT 10
#define IPERF_SOCKET_ACCEPT_TIMEOUT 5
typedef struct {
uint32_t flag;
uint32_t dip;
uint32_t sip;
uint16_t dport;
uint16_t sport;
uint32_t interval;
uint32_t time;
} iperf_cfg_t;
esp_err_t iperf_start(iperf_cfg_t *cfg);
esp_err_t iperf_stop(void);
#ifdef __cplusplus
}
#endif
#endif

View File

@ -1,5 +1,6 @@
set(srcs "main.c"
"board.c")
"board.c"
"cmd_wifi.c")
idf_component_register(SRCS "${srcs}"
INCLUDE_DIRS ".")

View File

@ -1,3 +1,3 @@
idf_component_register(SRCS "iperf.c"
INCLUDE_DIRS .
INCLUDE_DIRS "include"
REQUIRES lwip)

View File

@ -0,0 +1,14 @@
# iperf Component
This directory contains an implementation for iperf network performance tester
Note that it's incompatible with `iperf3`
This component is used as part of the following ESP-IDF examples:
- [esp_wifi](../../wifi/iperf).
- [ble_mesh_wifi_coexist](../../bluetooth/esp_ble_mesh/ble_mesh_wifi_coexist).
- [ethernet](../../ethernet/iperf)
To learn more about how to use this component, please check API Documentation from header file [iperf.h](./include/iperf.h).
Please note that this component is not considered to be a part of ESP-IDF stable API, and only for example use for now.

View File

@ -8,4 +8,4 @@
#
#include $(IDF_PATH)/make/component_common.mk
COMPONENT_ADD_INCLUDEDIRS := .
COMPONENT_ADD_INCLUDEDIRS := include

View File

@ -3,7 +3,7 @@
cmake_minimum_required(VERSION 3.5)
set(EXTRA_COMPONENT_DIRS $ENV{IDF_PATH}/examples/system/console/advanced/components
$ENV{IDF_PATH}/examples/wifi/iperf/components)
$ENV{IDF_PATH}/examples/common_components/iperf)
include($ENV{IDF_PATH}/tools/cmake/project.cmake)
project(ethernet_iperf)

View File

@ -7,6 +7,7 @@ PROJECT_NAME := ethernet_iperf
EXTRA_COMPONENT_DIRS = $(IDF_PATH)/examples/system/console/advanced/components
EXTRA_COMPONENT_DIRS += $(IDF_PATH)/examples/wifi/iperf/components
EXTRA_COMPONENT_DIRS += $(IDF_PATH)/examples/common_components/iperf
include $(IDF_PATH)/make/project.mk

View File

@ -2,7 +2,8 @@
# in this exact order for cmake to work correctly
cmake_minimum_required(VERSION 3.5)
set(EXTRA_COMPONENT_DIRS $ENV{IDF_PATH}/examples/system/console/advanced/components)
set(EXTRA_COMPONENT_DIRS $ENV{IDF_PATH}/examples/system/console/advanced/components
$ENV{IDF_PATH}/examples/common_components/iperf)
include($ENV{IDF_PATH}/tools/cmake/project.cmake)
project(iperf)

View File

@ -6,5 +6,6 @@
PROJECT_NAME := iperf
EXTRA_COMPONENT_DIRS := $(IDF_PATH)/examples/system/console/advanced/components
EXTRA_COMPONENT_DIRS += $(IDF_PATH)/examples/common_components/iperf
include $(IDF_PATH)/make/project.mk