816 lines
31 KiB
C
816 lines
31 KiB
C
/*!
|
|
\file tcp_test.c
|
|
\brief TCP test for GD32W51x WiFi SDK
|
|
|
|
\version 2021-10-30, V1.0.0, firmware for GD32W51x
|
|
*/
|
|
|
|
/*
|
|
Copyright (c) 2021, GigaDevice Semiconductor Inc.
|
|
|
|
Redistribution and use in source and binary forms, with or without modification,
|
|
are permitted provided that the following conditions are met:
|
|
|
|
1. Redistributions of source code must retain the above copyright notice, this
|
|
list of conditions and the following disclaimer.
|
|
2. Redistributions in binary form must reproduce the above copyright notice,
|
|
this list of conditions and the following disclaimer in the documentation
|
|
and/or other materials provided with the distribution.
|
|
3. Neither the name of the copyright holder nor the names of its contributors
|
|
may be used to endorse or promote products derived from this software without
|
|
specific prior written permission.
|
|
|
|
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
|
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
|
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
|
|
IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
|
|
INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
|
|
NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
|
|
PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
|
|
WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
|
ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY
|
|
OF SUCH DAMAGE.
|
|
*/
|
|
|
|
#include <string.h>
|
|
#include <stdlib.h>
|
|
#include "app_cfg.h"
|
|
#include "app_type.h"
|
|
#include "wrapper_os.h"
|
|
#include "malloc.h"
|
|
#include "debug_print.h"
|
|
#include <lwip/sockets.h>
|
|
|
|
// #define DEBUG_DUAL_MODE
|
|
#define HEADER_VERSION1 0x80000000
|
|
#define DUAL_MODE_RUN_NOW 0x00000001
|
|
|
|
#define ERROR_ALREADY_RUNNING 1
|
|
#define ERROR_NO_MEMORY -1
|
|
|
|
struct tcp_data_t {
|
|
void *task_hdl;
|
|
int32_t srv_fd;
|
|
int32_t cli_fd;
|
|
uint8_t is_server;
|
|
uint16_t srv_port; /* server port to listen on/connect to (default 5001) */
|
|
char srv_ip[16]; /* server ip to listen on/connect to */
|
|
uint8_t dualmode; /* Set dual mode, do a bidirectional test simultaneously */
|
|
uint8_t nodelay; /* set TCP no delay, disabling Nagle's Algorithm */
|
|
uint16_t buf_len; /* length of buffer to read or write (default 1460 Bytes) */
|
|
uint8_t tos; /* type of service byte */
|
|
uint32_t rpt_intvl; /* ticks between periodic bandwidth reports (default 1000 ticks) */
|
|
uint32_t time_limit; /* time in ticks to transmit for (default 10000 ticks) */
|
|
uint64_t size_limit; /* number of buffer to transmit for (default use time) */
|
|
};
|
|
|
|
struct client_hdr {
|
|
int32_t flags;
|
|
int32_t numThreads;
|
|
int32_t mPort;
|
|
int32_t bufferlen;
|
|
int32_t mWindowSize;
|
|
int32_t mAmount;
|
|
int32_t mRate;
|
|
int32_t mUDPRateUnits;
|
|
};
|
|
|
|
static const long kKilo_to_Unit = 1024;
|
|
static const long kMega_to_Unit = 1024 * 1024;
|
|
static const long kGiga_to_Unit = 1024 * 1024 * 1024;
|
|
|
|
static const long kkilo_to_Unit = 1000;
|
|
static const long kmega_to_Unit = 1000 * 1000;
|
|
static const long kgiga_to_Unit = 1000 * 1000 * 1000;
|
|
|
|
static struct tcp_data_t *tcp_srv_data = NULL;
|
|
static struct tcp_data_t *tcp_cli_data = NULL;
|
|
static int32_t tcp_terminate = 0;
|
|
static void tcp_task_func(void *param);
|
|
|
|
static uint64_t byte_atoi(const char *inString)
|
|
{
|
|
double theNum;
|
|
char suffix = '\0';
|
|
|
|
/* scan the number and any suffices */
|
|
sscanf(inString, "%lf%c", &theNum, &suffix);
|
|
|
|
/* convert according to [Gg Mm Kk] */
|
|
switch ( suffix ) {
|
|
case 'G': theNum *= kGiga_to_Unit; break;
|
|
case 'M': theNum *= kMega_to_Unit; break;
|
|
case 'K': theNum *= kKilo_to_Unit; break;
|
|
case 'g': theNum *= kgiga_to_Unit; break;
|
|
case 'm': theNum *= kmega_to_Unit; break;
|
|
case 'k': theNum *= kkilo_to_Unit; break;
|
|
default: break;
|
|
}
|
|
return (uint64_t)theNum;
|
|
}
|
|
|
|
#ifdef DEBUG_DUAL_MODE
|
|
/*!
|
|
\brief dump client header
|
|
\param[in] h: pointer to the input client header structure
|
|
\param[in] flags: a bitmap for different options
|
|
\arg HEADER_VERSION1 0x80000000
|
|
\arg RUN_NOW 0x00000001
|
|
\arg UNITS_PPS 0x00000002
|
|
\param[in] numThreads: number of the client threads
|
|
\param[in] mPort: port number of the client
|
|
\param[in] bufferlen: buffer length of the client
|
|
\param[in] mWindowSize: TCP window size of the client
|
|
\param[in] mAmount: number of bytes to send
|
|
\param[in] mRate: UDP data rate
|
|
\param[in] mUDPRateUnits: UDP rate uinit (either bw or pps)
|
|
\param[out] none
|
|
\retval none
|
|
*/
|
|
static void client_header_dump(struct client_hdr *h)
|
|
{
|
|
DEBUGPRINT("\r\n========== dump client header ============\r\n");
|
|
DEBUGPRINT("flags = 0x%x, ntohl(flags) = 0x%x\r\n", h->flags, ntohl(h->flags));
|
|
DEBUGPRINT("numThreads = %d\r\n", ntohl(h->numThreads));
|
|
DEBUGPRINT("mPort = %d\r\n", ntohl(h->mPort));
|
|
DEBUGPRINT("bufferlen = %d\r\n", ntohl(h->bufferlen));
|
|
DEBUGPRINT("mWindowSize = %d\r\n", ntohl(h->mWindowSize));
|
|
DEBUGPRINT("mAmount = 0x%x (%d)\r\n", ntohl(h->mAmount), ntohl(h->mAmount));
|
|
DEBUGPRINT("mRate = %d\r\n", ntohl(h->mRate));
|
|
DEBUGPRINT("mUDPRateUnits = %d\r\n", ntohl(h->mUDPRateUnits));
|
|
DEBUGPRINT("========== dump client header end ============\r\n");
|
|
}
|
|
#endif
|
|
|
|
/*!
|
|
\brief send client header
|
|
\param[in] tcp_data: pointer to the send client header structure
|
|
task_hdl: task handle
|
|
srv_fd: server socket file descriptor
|
|
cli_fd: client socket file descriptor
|
|
is_server: is server falg
|
|
srv_port: server port to listen on/connect to (default 5001)
|
|
srv_ip: server ip to listen on/connect to
|
|
dualmode: Set dual mode, do a bidirectional test simultaneously
|
|
nodelay: set TCP no delay, disabling Nagle's Algorithm
|
|
buf_len: length of buffer to read or write (default 1460 Bytes)
|
|
tos: type of service byte
|
|
rpt_intvl: ticks between periodic bandwidth reports (default 1000 ticks)
|
|
time_limit: time in ticks to transmit for (default 10000 ticks)
|
|
size_limit: number of buffer to transmit for (default use time)
|
|
\param[out] none
|
|
\retval none
|
|
*/
|
|
static void client_header_send(struct tcp_data_t *tcp_data)
|
|
{
|
|
struct client_hdr header;
|
|
|
|
if (tcp_data->dualmode == 1) {
|
|
header.flags = htonl(HEADER_VERSION1 | DUAL_MODE_RUN_NOW); // 0x01000080
|
|
} else {
|
|
header.flags = 0;
|
|
}
|
|
header.numThreads = htonl(0x01);
|
|
header.mPort = htonl(tcp_data->srv_port);
|
|
header.bufferlen = htonl(tcp_data->buf_len);
|
|
header.mWindowSize = htonl(TCP_WND);
|
|
if (tcp_data->size_limit> 0) {
|
|
header.mAmount = tcp_data->size_limit & 0x7FFFFFFF;
|
|
header.mAmount = htonl(header.mAmount);
|
|
} else {
|
|
header.mAmount = (int32_t)(tcp_data->time_limit * 100 / OS_TICK_RATE_HZ);
|
|
header.mAmount = htonl(-header.mAmount);
|
|
}
|
|
header.mRate = 0; // UDP
|
|
header.mUDPRateUnits = 0; // UDP
|
|
|
|
#ifdef DEBUG_DUAL_MODE
|
|
buffer_dump("Client header:", &header, sizeof(header));
|
|
client_header_dump(&header);
|
|
#endif
|
|
if (send(tcp_data->cli_fd, (char*)&header, sizeof(header), 0) <= 0) {
|
|
DEBUGPRINT("TCP ERROR: TCP client send client header error.\r\n");
|
|
}
|
|
}
|
|
|
|
/*!
|
|
\brief initialize client data
|
|
\param[in] none
|
|
\param[out] none
|
|
\retval function run status
|
|
\arg ERROR_ALREADY_RUNNING: tcp client is already running
|
|
\arg ERROR_NO_MEMORY: No memory for tcp data
|
|
\arg 0: run success
|
|
*/
|
|
static int client_data_init(void)
|
|
{
|
|
if (tcp_cli_data) {
|
|
if (tcp_cli_data->task_hdl != NULL) {
|
|
DEBUGPRINT("TCP: Tcp client is already running.\r\n");
|
|
return ERROR_ALREADY_RUNNING;
|
|
}
|
|
} else {
|
|
tcp_cli_data = sys_malloc(sizeof(struct tcp_data_t));
|
|
if (tcp_cli_data == NULL) {
|
|
DEBUGPRINT("TCP: No memory for tcp_data.\r\n");
|
|
return ERROR_NO_MEMORY;
|
|
}
|
|
}
|
|
|
|
sys_memset(tcp_cli_data, 0, sizeof(struct tcp_data_t));
|
|
tcp_cli_data->cli_fd = -1;
|
|
tcp_cli_data->buf_len = 1460;
|
|
tcp_cli_data->srv_port = 5001;
|
|
tcp_cli_data->tos = 0;
|
|
tcp_cli_data->rpt_intvl = 1 * OS_TICK_RATE_HZ; // default 1s
|
|
tcp_cli_data->time_limit = 10 * OS_TICK_RATE_HZ + 100; // default 10s
|
|
|
|
return 0;
|
|
}
|
|
|
|
/*!
|
|
\brief initialize server data
|
|
\param[in] none
|
|
\param[out] none
|
|
\retval function run status
|
|
\arg ERROR_ALREADY_RUNNING: tcp server is already running
|
|
\arg ERROR_NO_MEMORY: No memory for tcp data
|
|
\arg 0: run success
|
|
*/
|
|
static int server_data_init(void)
|
|
{
|
|
if (tcp_srv_data) {
|
|
if (tcp_srv_data->task_hdl != NULL) {
|
|
DEBUGPRINT("TCP WARNING: Tcp server is already running.\r\n");
|
|
return ERROR_ALREADY_RUNNING;
|
|
}
|
|
} else {
|
|
tcp_srv_data = sys_malloc(sizeof(struct tcp_data_t));
|
|
if (tcp_srv_data == NULL) {
|
|
DEBUGPRINT("TCP ERROR: No memory for tcp_data.\r\n");
|
|
return ERROR_NO_MEMORY;
|
|
}
|
|
}
|
|
|
|
sys_memset(tcp_srv_data, 0, sizeof(struct tcp_data_t));
|
|
tcp_srv_data->is_server = 1;
|
|
tcp_srv_data->srv_fd = -1;
|
|
tcp_srv_data->cli_fd = -1;
|
|
tcp_srv_data->buf_len = 1460;
|
|
tcp_srv_data->srv_port = 5001;
|
|
tcp_srv_data->rpt_intvl = 1 * OS_TICK_RATE_HZ; // default 1s
|
|
|
|
return 0;
|
|
}
|
|
|
|
/*!
|
|
\brief tcp client function
|
|
\param[in] tcp_data: pointer to the send client header structure
|
|
task_hdl: task handle
|
|
srv_fd: server socket file descriptor
|
|
cli_fd: client socket file descriptor
|
|
is_server: is server falg
|
|
srv_port: server port to listen on/connect to (default 5001)
|
|
srv_ip: server ip to listen on/connect to
|
|
dualmode: Set dual mode, do a bidirectional test simultaneously
|
|
nodelay: set TCP no delay, disabling Nagle's Algorithm
|
|
buf_len: length of buffer to read or write (default 1460 Bytes)
|
|
tos: type of service byte
|
|
rpt_intvl: ticks between periodic bandwidth reports (default 1000 ticks)
|
|
time_limit: time in ticks to transmit for (default 10000 ticks)
|
|
size_limit: number of buffer to transmit for (default use time)
|
|
\param[out] none
|
|
\retval none
|
|
\retval function run status
|
|
\arg -1: allocate client buffer failed
|
|
\arg 0: run success
|
|
*/
|
|
static int tcp_client(struct tcp_data_t *tcp_data)
|
|
{
|
|
struct sockaddr_in sAddr;
|
|
int iAddrSize = sizeof(struct sockaddr_in);
|
|
char *cBsdBuf = NULL;
|
|
int iStatus, iCounter, n;
|
|
uint32_t stime, etime, rpt_stime; // ticks
|
|
uint32_t m;
|
|
uint64_t rpt_sz = 0, tot_sz = 0;
|
|
uint32_t time = 0, tcnt = 0; // seconds
|
|
uint32_t intvl_sec = tcp_data->rpt_intvl / OS_TICK_RATE_HZ;
|
|
|
|
cBsdBuf = sys_malloc(tcp_data->buf_len);
|
|
if(NULL == cBsdBuf){
|
|
DEBUGPRINT("TCP ERROR: allocate client buffer failed (len = %d).\r\n", tcp_data->buf_len);
|
|
return -1;
|
|
}
|
|
|
|
for (iCounter = 0; iCounter < tcp_data->buf_len; iCounter++) {
|
|
cBsdBuf[iCounter] = (char)(iCounter % 10);
|
|
}
|
|
|
|
FD_ZERO(&sAddr);
|
|
sAddr.sin_family = AF_INET;
|
|
sAddr.sin_port = htons(tcp_data->srv_port);
|
|
sAddr.sin_addr.s_addr = inet_addr(tcp_data->srv_ip);
|
|
|
|
/* creating a TCP socket */
|
|
tcp_data->cli_fd = socket(AF_INET, SOCK_STREAM, 0);
|
|
if (tcp_data->cli_fd < 0) {
|
|
DEBUGPRINT("TCP ERROR: create tcp client socket fd error!\r\n");
|
|
goto Exit2;
|
|
}
|
|
|
|
/* Set IP TOS */
|
|
if (tcp_data->tos) {
|
|
m = tcp_data->tos;
|
|
if (setsockopt(tcp_data->cli_fd, IPPROTO_IP, IP_TOS, &m, sizeof(m)) < 0) {
|
|
DEBUGPRINT("TCP ERROR:set socket tos %d error, errno = %d!\r\n", m, errno);
|
|
goto Exit2;
|
|
}
|
|
}
|
|
|
|
n = tcp_data->nodelay;
|
|
setsockopt(tcp_data->cli_fd, IPPROTO_TCP, TCP_NODELAY,
|
|
(const char *) &n, sizeof( n ) );
|
|
|
|
DEBUGPRINT("TCP: server IP=%s port=%d.\r\n", tcp_data->srv_ip, tcp_data->srv_port);
|
|
DEBUGPRINT("TCP: create socket %d.\r\n", tcp_data->cli_fd);
|
|
|
|
/* connecting to TCP server */
|
|
iStatus = connect(tcp_data->cli_fd, (struct sockaddr *)&sAddr, iAddrSize);
|
|
if (iStatus < 0) {
|
|
DEBUGPRINT("TCP ERROR: tcp client connect server error!\r\n");
|
|
goto Exit1;
|
|
}
|
|
|
|
client_header_send(tcp_data);
|
|
|
|
rpt_stime = etime = stime = sys_current_time_get();
|
|
if (tcp_data->size_limit != 0) {
|
|
while (!tcp_terminate && (tot_sz < tcp_data->size_limit)) {
|
|
if (send(tcp_data->cli_fd, cBsdBuf, tcp_data->buf_len, 0) <= 0) {
|
|
DEBUGPRINT("TCP ERROR: TCP client send data error.\r\n");
|
|
goto Exit1;
|
|
}
|
|
tot_sz += tcp_data->buf_len;
|
|
rpt_sz += tcp_data->buf_len;
|
|
etime = sys_current_time_get();
|
|
time = (etime - rpt_stime) * OS_MS_PER_TICK;
|
|
|
|
if ((etime - rpt_stime) >= tcp_data->rpt_intvl) {
|
|
if (tcnt == 0)
|
|
DEBUGPRINT("\t\tInterval\t\tTransfer\t\tBandwidth\r\n");
|
|
DEBUGPRINT("Send\t%4d - %4d sec\t\t%4d KBytes\t\t%5d Kbps\r\n", tcnt, (tcnt + intvl_sec), (uint32_t)(rpt_sz >> 10), (uint32_t)((rpt_sz << 3) / time));
|
|
tcnt += intvl_sec;
|
|
rpt_stime = etime;
|
|
rpt_sz = 0;
|
|
}
|
|
}
|
|
} else {
|
|
while (!tcp_terminate && ((etime - stime) <= tcp_data->time_limit)) {
|
|
if (send(tcp_data->cli_fd, cBsdBuf, tcp_data->buf_len, 0) <= 0) {
|
|
DEBUGPRINT("TCP ERROR: TCP client send data error.\r\n");
|
|
goto Exit1;
|
|
}
|
|
tot_sz += tcp_data->buf_len;
|
|
rpt_sz += tcp_data->buf_len;
|
|
etime = sys_current_time_get();
|
|
time = (etime - rpt_stime) * OS_MS_PER_TICK;
|
|
|
|
if ((etime - rpt_stime) >= tcp_data->rpt_intvl) {
|
|
// DEBUGPRINT("etime=%d stime=%d limit=%d\r\n", etime, stime, tcp_data->time_limit);
|
|
if (tcnt == 0)
|
|
DEBUGPRINT("\t\tInterval\t\tTransfer\t\tBandwidth\r\n");
|
|
DEBUGPRINT("Send\t%4d - %4d sec\t\t%4d KBytes\t\t%5d Kbps\r\n", tcnt, (tcnt + intvl_sec), (uint32_t)(rpt_sz >> 10), (uint32_t)((rpt_sz << 3) / time));
|
|
tcnt += intvl_sec;
|
|
rpt_stime = etime;
|
|
rpt_sz = 0;
|
|
}
|
|
}
|
|
}
|
|
|
|
time = (etime - stime) * OS_MS_PER_TICK;
|
|
DEBUGPRINT("Total\t%4d - %4d sec\t\t%4d KBytes\t\t%5d Kbps\r\n", 0, tcnt, (uint32_t)(tot_sz >> 10), (uint32_t)((tot_sz << 3) / time));
|
|
|
|
Exit1:
|
|
close(tcp_data->cli_fd);
|
|
|
|
Exit2:
|
|
sys_mfree(cBsdBuf);
|
|
return 0;
|
|
}
|
|
|
|
/*!
|
|
\brief tcp server function
|
|
\param[in] tcp_data: pointer to the send client header structure
|
|
task_hdl: task handle
|
|
srv_fd: server socket file descriptor
|
|
cli_fd: client socket file descriptor
|
|
is_server: is server falg
|
|
srv_port: server port to listen on/connect to (default 5001)
|
|
srv_ip: server ip to listen on/connect to
|
|
dualmode: Set dual mode, do a bidirectional test simultaneously
|
|
nodelay: set TCP no delay, disabling Nagle's Algorithm
|
|
buf_len: length of buffer to read or write (default 1460 Bytes)
|
|
tos: type of service byte
|
|
rpt_intvl: ticks between periodic bandwidth reports (default 1000 ticks)
|
|
time_limit: time in ticks to transmit for (default 10000 ticks)
|
|
size_limit: number of buffer to transmit for (default use time)
|
|
\param[out] none
|
|
\retval none
|
|
\retval function run status
|
|
\arg -1: allocate server buffer failed
|
|
\arg 0: run success
|
|
*/
|
|
static int tcp_server(struct tcp_data_t *tcp_data)
|
|
{
|
|
struct sockaddr_in sLocalAddr;
|
|
struct sockaddr_in sCliAddr;
|
|
int iAddrSize = sizeof(sLocalAddr);
|
|
int iCounter, iStatus, n, recv_sz;
|
|
char *cBsdBuf = NULL;
|
|
uint32_t stime, etime, rpt_stime; // ticks
|
|
uint64_t rpt_sz = 0, tot_sz = 0;
|
|
uint32_t time, tcnt = 0; // ms
|
|
uint32_t intvl_sec = tcp_data->rpt_intvl / OS_TICK_RATE_HZ;
|
|
struct client_hdr header;
|
|
|
|
cBsdBuf = sys_malloc(tcp_data->buf_len);
|
|
if(NULL == cBsdBuf){
|
|
DEBUGPRINT("TCP ERROR: allocate server buffer failed (len = %d).\r\n", tcp_data->buf_len);
|
|
return -1;
|
|
}
|
|
|
|
for (iCounter = 0; iCounter < tcp_data->buf_len; iCounter++) {
|
|
cBsdBuf[iCounter] = (char)(iCounter % 10);
|
|
}
|
|
|
|
tcp_data->srv_fd = socket(AF_INET, SOCK_STREAM, 0);
|
|
if (tcp_data->srv_fd < 0) {
|
|
goto Exit3;
|
|
}
|
|
|
|
DEBUGPRINT("TCP: create server socket %d.\r\n", tcp_data->srv_fd);
|
|
n = 1;
|
|
setsockopt(tcp_data->srv_fd, SOL_SOCKET, SO_REUSEADDR,
|
|
(const char *) &n, sizeof( n ) );
|
|
|
|
sys_memset((char *)&sLocalAddr, 0, sizeof(sLocalAddr));
|
|
sLocalAddr.sin_family = AF_INET;
|
|
sLocalAddr.sin_len = sizeof(sLocalAddr);
|
|
sLocalAddr.sin_port = htons(tcp_data->srv_port);
|
|
sLocalAddr.sin_addr.s_addr = htonl(INADDR_ANY);
|
|
|
|
/* binding the TCP socket to the TCP server address */
|
|
iStatus = bind(tcp_data->srv_fd, (struct sockaddr *)&sLocalAddr, iAddrSize);
|
|
if( iStatus < 0 ) {
|
|
DEBUGPRINT("TCP ERROR: bind tcp server socket fd error!\r\n");
|
|
goto Exit2;
|
|
}
|
|
DEBUGPRINT("TCP: bind successfully.\r\n");
|
|
|
|
/* putting the socket for listening to the incoming TCP connection */
|
|
/* Make it listen to socket with max 20 connections */
|
|
iStatus = listen(tcp_data->srv_fd, 20);
|
|
if( iStatus != 0 ) {
|
|
DEBUGPRINT("TCP ERROR: listen tcp server socket fd error!\r\n");
|
|
goto Exit2;
|
|
}
|
|
DEBUGPRINT("TCP: listen port %d\r\n", tcp_data->srv_port);
|
|
|
|
/* waiting for an incoming TCP connection */
|
|
/* accepts a connection form a TCP client, if there is any. otherwise returns SL_EAGAIN */
|
|
tcp_data->cli_fd = accept(tcp_data->srv_fd,
|
|
(struct sockaddr *)&sCliAddr,
|
|
(socklen_t*)&iAddrSize);
|
|
if (tcp_data->cli_fd < 0) {
|
|
DEBUGPRINT("TCP ERROR: accept tcp client socket fd error!\r\n");
|
|
goto Exit2;
|
|
}
|
|
DEBUGPRINT("TCP: accept socket %d successfully.\r\n", tcp_data->cli_fd);
|
|
n = tcp_data->nodelay;
|
|
setsockopt(tcp_data->srv_fd, IPPROTO_TCP, TCP_NODELAY,
|
|
(const char *) &n, sizeof( n ) );
|
|
|
|
/* Process client header */
|
|
recv_sz = recv(tcp_data->cli_fd, cBsdBuf, tcp_data->buf_len, 0);
|
|
if (tcp_data->dualmode == 0) {
|
|
DEBUGPRINT("TCP: Check client header.\r\n");
|
|
/* Run as tcp server. The server is created by user. */
|
|
sys_memcpy(&header, cBsdBuf, sizeof(header));
|
|
#ifdef DEBUG_DUAL_MODE
|
|
buffer_dump("Client header:", &header, sizeof(header));
|
|
client_header_dump(&header);
|
|
#endif
|
|
if (ntohl(header.flags) & DUAL_MODE_RUN_NOW) {
|
|
/* Dual mode, do a bidirectional test simultaneously */
|
|
/* Create tcp client data to send packets. */
|
|
int ret = client_data_init();
|
|
if (ret != 0) {
|
|
goto Exit1;
|
|
}
|
|
DEBUGPRINT("TCP: dual mode, start client task.\r\n");
|
|
tcp_cli_data->buf_len = ntohl(header.bufferlen);
|
|
if (tcp_cli_data->buf_len == 0)
|
|
tcp_cli_data->buf_len = 1460;
|
|
if (header.mAmount != 0) {
|
|
header.mAmount = ntohl(header.mAmount);
|
|
if (((uint32_t)header.mAmount) > 0x7fffffff) {
|
|
header.mAmount = (-header.mAmount) / 100;
|
|
tcp_cli_data->time_limit = header.mAmount * OS_TICK_RATE_HZ + 100;
|
|
} else {
|
|
tcp_cli_data->size_limit = header.mAmount;
|
|
}
|
|
}
|
|
tcp_cli_data->srv_port = ntohl(header.mPort);
|
|
snprintf(tcp_cli_data->srv_ip, sizeof(tcp_cli_data->srv_ip), "%s", inet_ntoa(sCliAddr.sin_addr));
|
|
tcp_cli_data->task_hdl = sys_task_create(NULL, (const uint8_t *)"tcp_client", NULL,
|
|
TCP_TEST_STACK_SIZE, 0, TCP_TEST_CLIENT_PRIO + TASK_PRIO_HIGHER(1),
|
|
(task_func_t)tcp_task_func, tcp_cli_data);
|
|
if (tcp_cli_data->task_hdl == NULL) {
|
|
DEBUGPRINT("TCP ERROR: create tcp client task failed.\r\n");
|
|
goto Exit1;
|
|
}
|
|
}
|
|
} else {
|
|
/* Run as tcp client. The server is auto created since the client is dual mode. */
|
|
/* Not to parse client header. */
|
|
}
|
|
|
|
/* waits packets from the connected TCP client */
|
|
rpt_stime = stime = sys_current_time_get();
|
|
while (!tcp_terminate) {
|
|
recv_sz = recv(tcp_data->cli_fd, cBsdBuf, tcp_data->buf_len, 0);
|
|
if (recv_sz < 0) {
|
|
DEBUGPRINT("TCP ERROR: server recv data error. recv_sz is %d.\r\n", recv_sz);
|
|
goto Exit1;
|
|
} else if (recv_sz == 0) {
|
|
break;
|
|
}
|
|
etime = sys_current_time_get();
|
|
tot_sz += recv_sz;
|
|
rpt_sz += recv_sz;
|
|
time = (etime - rpt_stime) * OS_MS_PER_TICK;
|
|
if ((etime - rpt_stime) >= tcp_data->rpt_intvl) {
|
|
if (tcnt == 0)
|
|
DEBUGPRINT("\t\tInterval\t\tTransfer\t\tBandwidth\r\n");
|
|
DEBUGPRINT("Recv\t%4d - %4d sec\t\t%4d KBytes\t\t%5d Kbps\r\n", tcnt, (tcnt + intvl_sec), (uint32_t)(rpt_sz >> 10), (uint32_t)((rpt_sz << 3) / time));
|
|
tcnt += intvl_sec;
|
|
rpt_stime = etime;
|
|
rpt_sz = 0;
|
|
}
|
|
}
|
|
time = (etime - stime) * OS_MS_PER_TICK;
|
|
DEBUGPRINT("Total\t%4d - %4d sec\t\t%4d KBytes\t\t%5d Kbps\r\n", 0, tcnt, (uint32_t)(tot_sz >> 10), (uint32_t)((tot_sz << 3) / time));
|
|
|
|
Exit1:
|
|
/* close the connected socket after receiving from connected TCP client */
|
|
close(tcp_data->cli_fd);
|
|
|
|
Exit2:
|
|
/* close the listening socket */
|
|
close(tcp_data->srv_fd);
|
|
|
|
Exit3:
|
|
/* free buffer */
|
|
sys_mfree(cBsdBuf);
|
|
|
|
return 0;
|
|
}
|
|
|
|
/*!
|
|
\brief tcp task function
|
|
\param[in] param: pointer to the input argument
|
|
\param[out] none
|
|
\retval none
|
|
*/
|
|
static void tcp_task_func(void *param)
|
|
{
|
|
struct tcp_data_t *tcp_data = (struct tcp_data_t *)param;
|
|
|
|
if (tcp_data->is_server == 1) {
|
|
DEBUGPRINT("TCP: start tcp Server!\r\n");
|
|
|
|
tcp_server(tcp_data);
|
|
|
|
DEBUGPRINT("tcp server task: used stack = %d, free stack = %d\r\n",
|
|
(TCP_TEST_STACK_SIZE - sys_stack_free_get(NULL)), sys_stack_free_get(NULL));
|
|
|
|
DEBUGPRINT("TCP: tcp server stopped!\r\n");
|
|
} else {
|
|
DEBUGPRINT("TCP: start tcp client!\r\n");
|
|
|
|
tcp_client(tcp_data);
|
|
|
|
DEBUGPRINT("tcp client task: used stack = %d, free stack = %d\r\n",
|
|
(TCP_TEST_STACK_SIZE - sys_stack_free_get(NULL)), sys_stack_free_get(NULL));
|
|
|
|
DEBUGPRINT("TCP: tcp client stopped!\r\n");
|
|
}
|
|
|
|
tcp_data->task_hdl = NULL;
|
|
|
|
sys_task_delete(NULL);
|
|
}
|
|
|
|
/*!
|
|
\brief command of tcp
|
|
\param[in] argc: counter of input argument
|
|
\param[out] argv: pointer to the input argument
|
|
\retval none
|
|
*/
|
|
void cmd_tcp(int argc, char **argv)
|
|
{
|
|
int arg_cnt = 1, is_server = 0;
|
|
char *endptr = NULL;
|
|
tcp_terminate = 0;
|
|
|
|
/* Parse main option */
|
|
if (arg_cnt == 1) {
|
|
if (strcmp(argv[1], "-s") == 0) {
|
|
DEBUGPRINT("\r\nIperf2: start iperf2 server!\r\n");
|
|
int ret = server_data_init();
|
|
if (ret == ERROR_ALREADY_RUNNING || ret == ERROR_NO_MEMORY)
|
|
return;
|
|
|
|
arg_cnt += 1;
|
|
is_server = 1;
|
|
} else if (strcmp(argv[1], "-c") == 0) {
|
|
DEBUGPRINT("\r\nIperf2: start iperf2 client!\r\n");
|
|
int ret = client_data_init();
|
|
if (ret == ERROR_ALREADY_RUNNING || ret == ERROR_NO_MEMORY)
|
|
return;
|
|
|
|
if (argc >= 3)
|
|
snprintf(tcp_cli_data->srv_ip, sizeof(tcp_cli_data->srv_ip), "%s", argv[2]);
|
|
else
|
|
goto Exit;
|
|
arg_cnt += 2;
|
|
} else if (strcmp(argv[1], "exit") == 0) {
|
|
int cnt = 0;
|
|
tcp_terminate = 1;
|
|
if (tcp_srv_data) {
|
|
while ((tcp_srv_data->task_hdl != NULL) && (cnt++ < 10))
|
|
sys_ms_sleep(100);
|
|
sys_mfree(tcp_srv_data);
|
|
tcp_srv_data = NULL;
|
|
}
|
|
if (tcp_cli_data) {
|
|
cnt = 0;
|
|
while ((tcp_cli_data->task_hdl != NULL) && (cnt++ < 10))
|
|
sys_ms_sleep(100);
|
|
sys_mfree(tcp_cli_data);
|
|
tcp_cli_data = NULL;
|
|
}
|
|
return;
|
|
} else {
|
|
goto Exit;
|
|
}
|
|
}
|
|
|
|
if (is_server)
|
|
DEBUG_ASSERT(tcp_srv_data != NULL);
|
|
else
|
|
DEBUG_ASSERT(tcp_cli_data != NULL);
|
|
|
|
/* Parse other options */
|
|
while (arg_cnt < argc) {
|
|
/* Client/Server */
|
|
if (strcmp(argv[arg_cnt], "-i") == 0) {
|
|
uint32_t intvl = 0;
|
|
if (argc <= (arg_cnt + 1))
|
|
goto Exit;
|
|
intvl = (uint32_t)atoi(argv[arg_cnt + 1]) * OS_TICK_RATE_HZ;
|
|
if (intvl > 3600 * OS_TICK_RATE_HZ) {
|
|
DEBUGPRINT("TCP WARNNING: Report interval is larger than 3600 seconds. Use 3600 seconds instead.\r\n");
|
|
intvl = 3600 * OS_TICK_RATE_HZ;
|
|
}
|
|
if (intvl > 0) {
|
|
if (is_server) {
|
|
tcp_srv_data->rpt_intvl = intvl;
|
|
} else {
|
|
tcp_cli_data->rpt_intvl = intvl;
|
|
}
|
|
}
|
|
arg_cnt += 2;
|
|
} else if (strcmp(argv[arg_cnt], "-l") == 0) {
|
|
uint32_t len = 0;
|
|
if (argc <= (arg_cnt + 1))
|
|
goto Exit;
|
|
len = (uint32_t)atoi(argv[arg_cnt + 1]);
|
|
if (len > 4380) {
|
|
DEBUGPRINT("TCP WARNNING: To save memory, the buffer size is preferably less than 4380. Use 4380 instead.\r\n");
|
|
len = 4380;
|
|
}
|
|
if (len > 0) {
|
|
if (is_server) {
|
|
tcp_srv_data->buf_len = len;
|
|
} else {
|
|
tcp_cli_data->buf_len = len;
|
|
}
|
|
}
|
|
arg_cnt += 2;
|
|
} else if (strcmp(argv[arg_cnt], "-p") == 0) {
|
|
if (argc <= (arg_cnt + 1))
|
|
goto Exit;
|
|
if (is_server) {
|
|
tcp_srv_data->srv_port = (uint16_t)atoi(argv[arg_cnt + 1]);
|
|
} else {
|
|
tcp_cli_data->srv_port = (uint16_t)atoi(argv[arg_cnt + 1]);
|
|
}
|
|
arg_cnt += 2;
|
|
} else if (strcmp(argv[arg_cnt], "-N") == 0) {
|
|
if (is_server) {
|
|
tcp_srv_data->nodelay = 1;
|
|
} else {
|
|
tcp_cli_data->nodelay = 1;
|
|
}
|
|
arg_cnt += 2;
|
|
/* Client Only */
|
|
} else if (strcmp(argv[arg_cnt], "-n") == 0) {
|
|
if (argc <= (arg_cnt + 1))
|
|
goto Exit;
|
|
if (is_server) {
|
|
goto Exit;
|
|
} else {
|
|
tcp_cli_data->size_limit = byte_atoi(argv[arg_cnt + 1]);
|
|
}
|
|
arg_cnt += 2;
|
|
} else if (strcmp(argv[arg_cnt], "-t") == 0) {
|
|
if (argc <= (arg_cnt + 1))
|
|
goto Exit;
|
|
if (is_server) {
|
|
goto Exit;
|
|
} else {
|
|
tcp_cli_data->time_limit = (uint32_t)atoi(argv[arg_cnt + 1]) * OS_TICK_RATE_HZ + 100;
|
|
}
|
|
arg_cnt += 2;
|
|
} else if (strcmp(argv[arg_cnt], "-d") == 0) {
|
|
if (is_server) {
|
|
goto Exit;
|
|
} else {
|
|
tcp_cli_data->dualmode = 1;
|
|
}
|
|
arg_cnt += 1;
|
|
} else if (strcmp(argv[arg_cnt], "-S") == 0) {
|
|
if (argc <= (arg_cnt + 1))
|
|
goto Exit;
|
|
if (is_server) {
|
|
goto Exit;
|
|
} else {
|
|
tcp_cli_data->tos = (uint8_t)strtoul(argv[arg_cnt + 1], &endptr, 0);
|
|
if (*endptr != '\0') {
|
|
DEBUGPRINT("iperf: invalid tos\r\n");
|
|
goto Exit;
|
|
}
|
|
}
|
|
arg_cnt += 2;
|
|
} else {
|
|
goto Exit;
|
|
}
|
|
}
|
|
|
|
/* Create task */
|
|
if (is_server) {
|
|
tcp_srv_data->task_hdl = sys_task_create(NULL, (const uint8_t *)"tcp_server", NULL,
|
|
TCP_TEST_STACK_SIZE, 0, TCP_TEST_SERVER_PRIO,
|
|
(task_func_t)tcp_task_func, tcp_srv_data);
|
|
if (tcp_srv_data->task_hdl == NULL) {
|
|
DEBUGPRINT("TCP ERROR: create tcp server task failed.\r\n");
|
|
return;
|
|
}
|
|
} else {
|
|
/* Check report interval */
|
|
if ((tcp_cli_data->size_limit == 0) && (tcp_cli_data->rpt_intvl > tcp_cli_data->time_limit)) {
|
|
tcp_cli_data->rpt_intvl = tcp_cli_data->time_limit;
|
|
}
|
|
tcp_cli_data->task_hdl = sys_task_create(NULL, (const uint8_t *)"tcp_client", NULL,
|
|
TCP_TEST_STACK_SIZE, 0, TCP_TEST_CLIENT_PRIO,
|
|
(task_func_t)tcp_task_func, tcp_cli_data);
|
|
if (tcp_cli_data->task_hdl == NULL) {
|
|
DEBUGPRINT("TCP ERROR: create tcp client task failed.\r\n");
|
|
return;
|
|
}
|
|
|
|
if (tcp_cli_data->dualmode == 1) {
|
|
int ret = server_data_init();
|
|
if (ret == ERROR_ALREADY_RUNNING || ret == ERROR_NO_MEMORY)
|
|
return;
|
|
tcp_srv_data->srv_port = tcp_cli_data->srv_port;
|
|
tcp_srv_data->dualmode = 1;
|
|
tcp_srv_data->task_hdl = sys_task_create(NULL, (const uint8_t *)"tcp_server", NULL,
|
|
TCP_TEST_STACK_SIZE, 0, TCP_TEST_SERVER_PRIO + TASK_PRIO_LOWER(1),
|
|
(task_func_t)tcp_task_func, tcp_srv_data);
|
|
if (tcp_srv_data->task_hdl == NULL) {
|
|
DEBUGPRINT("TCP ERROR: create tcp server task failed.\r\n");
|
|
return;
|
|
}
|
|
}
|
|
}
|
|
|
|
return;
|
|
|
|
Exit:
|
|
DEBUGPRINT("\rIperf2: iperf2 test command format error!\r\n");
|
|
DEBUGPRINT("\r\nUsage: \"iperf -s\" to start iperf2 server or \"iperf -c\" to start iperf2 client or \"iperf -h\" for help\r\n");
|
|
}
|