weixin_39564831
weixin_39564831
2020-12-05 12:30

TCP/UDP Stuttering

Bug Report

Expected Behavior

Near real-time communication with a Linux computer (Ubuntu 16.04 LTS) using either UDP or TCP.

This is also happening at the same time the Argon board is running multiple threads to get data from an IMU and write pixel data to an OLED. The threading was necessary to keep things running in reasonable amounts of time.

Observed Behavior

The argons would recieve packets in "bursts", meaning they would get all the packets from a certain period of time all at once. This was not good for getting nearly real-time data.

Steps to Reproduce

Firmware Versions: 1.0.0 - 1.2.0

We set up a client of either TCP or UDP, and also enabled threading, and attempting to connect to the server

Test App

Argon Code:

c
#include <neopixel.h>
#include <mpu6050>
#include <adafruit_mfgfx.h>
#include <adafruit_ssd1351_photon.h>
#include <i2cdev.h>
SYSTEM_THREAD(ENABLED)

#include <stdlib.h>
#include <string.h>
#include <math.h>
#include <wire.h>
#include "spark_wiring.h"
#include <particle.h>

#include "screen.h"
#include "drivetrain.h"
#include "easy_tcp.h"
#include "imu_calibrate.h"
#include "charger_status.h"
#define INTEGRATED_DEBUG 1
#define COMMAND_DEBUG 0
// // FOR AN ARGON BOARD
// #define mosi D12 //blue - DIN - MO on Argon Board
// #define sclk D13 //yellow
// #define cs A5    //orange
// #define dc D4    //green
// #define rst D5   //white

// IMPORTANT: Set pixel COUNT, PIN and TYPE
#define PIXEL_PIN D7
#define PIXEL_COUNT 1
#define PIXEL_TYPE WS2812B

#define NETWORK_LED 0

Adafruit_NeoPixel strip(PIXEL_COUNT, PIXEL_PIN, PIXEL_TYPE);

typedef struct command {
    char str[64];
} command;

Thread oledThread; //("testThread", threadFunction);
void threadOled(void);
DiffDrive diff_drive;
IMUCalibrate imu;
Screen screenObject;
 int port = 4321;
     byte ip[4] = {192, 168, 10, 187};
String registerString = "";
void nameFinder(const char *topic, const char *data) {
    //Serial.println("namefinder!");
    String name = String(data);
    Serial.println(name);
    int length=strlen(name);
    registerString += name[length-2];
    registerString += name[length-1];
    registerString += '\0';
}
EasyTCP tcpClient;
struct command c;
float theta = -100, pos = 10;
float prevtheta=theta;
bool latency_timeout = false;
unsigned long system_timeout;
ChargerStatus chargeStat;
unsigned long lastBattPublish;
String prevMessage;
String message;


void setup(void)
{

    Serial.begin(9600);
    waitUntil(WiFi.ready);
    waitUntil(Particle.connected);
    Serial.println("Connected to Cloud");
    //Gets device name from cloud and parses string to get the state name
    Particle.subscribe("particle/device/name", nameFinder);
    Particle.publish("particle/device/name");
    // Initialize tcp client
    while(registerString.equals("")){
        delay(1000);
        Serial.println("dont have name");
    }
    tcpClient = EasyTCP(port, ip, registerString);

#if INTEGRATED_DEBUG
    Serial.println("Connected to wifi");
#endif

    // Initialize screen
    screenObject.init(registerString);

    //Send it's first battery status then intialize the lastPublish for when it last published status
    prevMessage=chargeStat.checkChargingState();
    tcpClient.println(prevMessage);
    lastBattPublish=millis();



    // Set up pins
    // pinSetup();
    pinMode(PWR, INPUT);
    pinMode(CHG, INPUT);

    // disable on-board RGB LED on Photon/Electron / You can also set these pins for other uses. also kept this because idk what it is
    pinMode(RGBR, INPUT);
    pinMode(RGBG, INPUT);
    pinMode(RGBB, INPUT);

    Wire.begin();
    // Initialize drivetrain
    diff_drive.init();

    // Initialize IMU
    //imu.init();

    // TODO: neopixel stuff on hold until i get the reset working

    Serial.println("registerString");
    Serial.println(registerString);
    while(!tcpClient.init(10000)) tone(A4, 1760, 1000); // screams out an A6 on pin A4 :^)
    oledThread = Thread("oled", threadOled);
    system_timeout=millis();
}

void loop()
{

    if (!tcpClient.connected()) {
        // Stop moving
        diff_drive.fullStop();

        // Keep trying to reconnect as needed
        while(!tcpClient.init(10000)) tone(A4, 1760, 1000);

        // Restart drivetrain
        diff_drive.restart();
    }

    // Read from TCP
    char sys_comm[16] = {'\0'};
    int temp = tcpClient.read((uint8_t *)(&c), sizeof(struct command), theta, pos, sys_comm, latency_timeout);

    // If got a heading from VICON, just update IMU calibration
    if (temp > 0)
    {
        imu.getIMUHeading(theta);
#if COMMAND_DEBUG
        Serial.print("VICON\tR: ");
        Serial.print(pos);
        Serial.print("\tTH: ");
        Serial.print(theta);
#endif
        // parsing sys_comm
        if (strcmp(sys_comm, "discon") == 0) // the call was a disconnection request
        {
            tcpClient.disconnect();
            diff_drive.fullStop();
            tcpClient.init(10000);
        }
    }
    // If no data, use IMU estimate
    else if (temp == 0)
    {
        theta = imu.getIMUHeading(theta);
#if COMMAND_DEBUG
        Serial.print("IMU\tR: ");
        Serial.print(pos);
        Serial.print("\tTH: ");
        Serial.print(theta);
#endif
    }
    // Else, error
    else
    {
#if INTEGRATED_DEBUG
        Serial.println("Read Error!");
#endif
    }
    //need to get this from some decision betweeen imu and vicon

    diff_drive.drive(theta, pos, imu.getYawRate(),latency_timeout);
   // Serial.print("Finish drive command check client ");
    //Serial.println(millis());

    message=chargeStat.checkChargingState();
    if(!message.equals(prevMessage)) // Send it
        tcpClient.println(message);
        prevMessage=message;
    if(millis()-lastBattPublish>60000){
        tcpClient.println(chargeStat.giveBatteryVoltage());
        lastBattPublish=millis();
    }
    if (theta != prevtheta){
        prevtheta=theta;
        system_timeout =millis();
    }else if (millis()-system_timeout >3000) {
        System.reset();
    }
    Particle.process();
}

void threadOled(void)
{
    while (true)
    {
        screenObject.updateScreen(theta, tcpClient.connected(),tcpClient.readTimer);
    }
}
</particle.h></wire.h></math.h></string.h></stdlib.h></i2cdev.h></adafruit_ssd1351_photon.h></adafruit_mfgfx.h></mpu6050></neopixel.h>

The Server code:

c
////HEADER////
/*********************************************************************
* Software License Agreement (BSD License)
*
* Copyright (c) 2019, WVU Interactive Robotics Laboratory
*                       https://web.statler.wvu.edu/~irl/
* All rights reserved.
*
*  Redistribution and use in source and binary forms, with or without
*  modification, are permitted provided that the following conditions
*  are met:
*
*   * Redistributions of source code must retain the above copyright
*     notice, this list of conditions and the following disclaimer.
*   * 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.
*   * Neither the name of the Willow Garage 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 OWNER 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.
*********************************************************************/

#ifndef SERVER_HH
#define SERVER_HH

/**
 *
 * Server
 *
 * Author: Henry Vos
 *
 * Purpose:
 *   This section is to accept connections from all the robots.
 *   The clients will each get their own process. (this may pose issues)
 *   The server will also pipe messages to the clients
 */

// Includes
#include <swarm_server>
#include <pthread.h>
#include <unistd.h>
#include <sys>
#include <sys>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys>
#include <sys>
#include <netinet>
#include <arpa>
#include <sys>
#include <time.h>
#include <sys>
#include <fcntl.h>
#include <sys>

#include <functional>
#include <vector>
#include <map>
#include <string>

#define SERVER_PORT 4321 // port number
#define COMMAND_SIZE 64

bool g_server_failure = false;

// datastructure that is used to send/recieve commands
// this is using a struct as it is only a public data storing application
typedef struct
{
    char str[COMMAND_SIZE];
} command;

/**
 *  Class contains all the data necessesary to talk with one robot
 *  as well as act as a filter with the rid to reduce bandwidth usage
 *
 */
class ConnectionInfo
{
private:
    int connection_descriptor; // the connection connection_descriptor for the socket
    int rid; // the id of the robot (or other client) connected

public:
    ConnectionInfo(int connection_descriptor);

    int getRID(); // returns the RID of the client
    void setRID(int rid); // sets the RID of the client

    int getConnectionDescriptor(); // returns the connection descriptor of the client
};

/**
 * Send command to robots sends commands to robots
 *
 * cmd is the content of the comannd
 *
 * recip_rid is the recipiant's RID
 *  special cases:
 *  a connection with registered id of -2 will recieve all sent commands
 *  a recip_rid of -1 will send a command to all robots
 */
void sendCommandToRobots(command cmd, int recip_rid);

// function responsible for recieving information from a client
void runClient(std::function<void int> command_callback,
        std::function<void char void> info_callback,
        std::function<void char> error_callback,
        std::function<bool> exit_condition_callback, int id);

/**
 * Begins accepting connections to the server and processes commands from them
 *
 * command_callback is a registered function for taking care of command contents
 * info_callback is the registered function for printing basic information to the screen
 * error_callback is the registered function for showing server errors
 * exit_condition_callback is the registered function that dictates a successful exit condition
 *                                                  exits when exit_condition_callback() == false
 */
int beginServer(std::function<void int> command_callback,
        std::function<void char void> info_callback,
        std::function<void char> error_callback,
        std::function<bool> exit_condition_callback,
        std::function<void char> warn_callback);
#include "arduino_server_source.cpp"
#endif


////Source////
/*********************************************************************
* Software License Agreement (BSD License)
*
* Copyright (c) 2019, WVU Interactive Robotics Laboratory
*                       https://web.statler.wvu.edu/~irl/
* All rights reserved.
*
*  Redistribution and use in source and binary forms, with or without
*  modification, are permitted provided that the following conditions
*  are met:
*
*   * Redistributions of source code must retain the above copyright
*     notice, this list of conditions and the following disclaimer.
*   * 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.
*   * Neither the name of the Willow Garage 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 OWNER 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.
*********************************************************************/

#ifndef ARDINO_SERVER_SOURCE
#define ARDINO_SERVER_SOURCE
// definition of a "verbose" option
#define DEBUG_CPP 0

// setting this to 1 shows what messages failed and succeeded
#define DEBUG_MESSAGE 1

#if DEBUG_CPP || DEBUG_MESSAGE
#include <iostream>
#include <chrono>
using namespace std::chrono;

#define PRINTF_TS(form, dat...) (printf(("\033[32m[%ld.%09ld] \033[0m" + std::string(form) + "\n").c_str(),\
        (long) duration_cast<seconds>(high_resolution_clock::now().time_since_epoch()).count(), \
        (long)duration_cast<nanoseconds>(high_resolution_clock::now().time_since_epoch()).count() % 1000000000,dat))
#define PUTS_TS(form) (printf(("\033[32m[%ld.%09ld] \033[0m" + std::string(form) + "\n").c_str(), \
        (long)duration_cast<seconds>(high_resolution_clock::now().time_since_epoch()).count(), \
        (long)((long)duration_cast<nanoseconds>(high_resolution_clock::now().time_since_epoch()).count() % 1000000000)))
#endif

#include "arduino_server.h"

// struct not really useful to anything outside this file
// this struct is used to pass information between the main accept loop
// and the client processing thread
struct client_param
{
    std::function<void int> command_callback;
    std::function<void char void> info_callback;
    std::function<void char> error_callback;
    std::function<bool> exit_condition_callback;
    int id;
};

// ConnectionInfo class implementation
ConnectionInfo::ConnectionInfo(int connection_descriptor)
{
#if DEBUG_CPP
    puts("SERVER (OBJ): Started connecting info");
    printf("SERVER (OBJ): descriptor exists: %d\n",
            &connection_descriptor != NULL);
#endif
    this->connection_descriptor = connection_descriptor;
    this->rid = -1;

#if DEBUG_CPP
    puts("SERVER (OBJ): Constructed");
#endif
}

// accessors
int ConnectionInfo::getRID()
{
    return this->rid;
}

void ConnectionInfo::setRID(int rid)
{
    this->rid = rid;
}

int ConnectionInfo::getConnectionDescriptor()
{
    return this->connection_descriptor;
}

// structure for all the socket descriptors
std::vector<connectioninfo> *sockets;

// map for quick access
std::map<int connectioninfo> *registry;
std::vector<connectioninfo> *monitors;

void sendCommandToRobots(command cmd, int recip_rid)
{
#if DEBUG_CPP || DEBUG_MESSAGE
//  printf("[\033[1;33marduino_server_source\033[0m] Command executing\033[0m\n");
#endif

    int nbytes = 0;
    // sending directly to recipiant
    if (registry->find(recip_rid) != registry->end() && registry->size() > 0) // check to see that the location in the map exists
    {
        int connection_descriptor =
                registry->at(recip_rid).getConnectionDescriptor();

        nbytes = send(connection_descriptor, &cmd, COMMAND_SIZE, 0); // sending message
    }
#if DEBUG_CPP || DEBUG_MESSAGE
    else
        PRINTF_TS(
                "\033[30;41mCould not locate ConnectionInfo for %02d  %s\033[0m",
                recip_rid, rid_indexing[recip_rid].c_str());

    if (nbytes == COMMAND_SIZE && registry->find(recip_rid) != registry->end())
        PRINTF_TS("\033[30;42mSERVER: sending message [%02d  %s]: %s\t%d\033[0m",
                recip_rid, rid_indexing[recip_rid].c_str(), cmd.str,
                registry->at(recip_rid).getConnectionDescriptor());

    if (nbytes != COMMAND_SIZE && registry->find(recip_rid) != registry->end())
        PRINTF_TS(
                "\033[30;43mSERVER: Failed sending message [%02d  %s]: %s\t%d\033[0m",
                recip_rid, rid_indexing[recip_rid].c_str(), cmd.str,
                registry->at(recip_rid).getConnectionDescriptor());
#endif

    // checking for monitors
    if (monitors->size() > 0)
    {
        char mon_str[COMMAND_SIZE * 2];
        // creating monitor message
        sprintf(mon_str, "[%02d / %2s]:\t%s", recip_rid,
                rid_indexing[recip_rid].c_str(), cmd.str);
        strncpy(cmd.str, mon_str, sizeof(cmd.str)); // safe copy
#if DEBUG_CPP || DEBUG_MESSAGE
        PRINTF_TS("\033[37;44mSERVER: sending message to monitor: %s\033[0m",
                cmd.str);
#endif
        for (ConnectionInfo ci : *monitors) // sending message to all open monitors
        {
            send(ci.getConnectionDescriptor(), &cmd, COMMAND_SIZE, 0);
        }
    }
}

void* runClient(void *args)
{
#if DEBUG_CPP
    PUTS_TS("Starting client thread");
#endif

    // getting parameters
    struct client_param *vals = (struct client_param*) args; // separating out parameter type

    // putting parameters into easily useable variables
    std::function<void int> command_callback = vals->command_callback;
    std::function<void char void> info_callback = vals->info_callback;
    std::function<void char> error_callback = vals->error_callback;
    std::function < bool() > exit_condition_callback =
            vals->exit_condition_callback;
    int id = vals->id;

    // getting the connection_descriptor from the connection this thread is monitoring
    int connection_descriptor = sockets->at(id).getConnectionDescriptor();

    while (exit_condition_callback()) // running until told to stop
    {
#if DEBUG_CPP
            printf("\033[35;1m[Thread: %d] Main loop\033[0m\n", id);
#endif
        // Read data from connection into buffer. Continue to loop while message size is >0.
        int message_size = 0;
        command *buffer = ((command*) malloc(sizeof(command))); // allocating memory for read buffer
        //reading the message
        fd_set rfds;
        FD_ZERO(&rfds);
        FD_SET(connection_descriptor, &rfds);

        struct timeval timeout;
        timeout.tv_sec = 1;
        timeout.tv_usec = 0;

        int recVal = select(connection_descriptor + 2, &rfds, NULL, NULL, &timeout);

        if (recVal != 0 && recVal != -1
                && (message_size = read(connection_descriptor, buffer, sizeof(command))
                        > 0 && exit_condition_callback()))
        {
#if DEBUG_CPP
                     printf("\033[35;1m[Thread: %d] Recieve loop\033[0m\n", id);
#endif
            // Display if there was an error
            if (message_size == -1)
            {
                error_callback("Error receiving message.");
                pthread_exit(0);
            }

            // checking if the client is registering their RID
            if (strstr(buffer->str, "register") == buffer->str) // chekcing if the input starts with "register"
            {
                try
                {
                    char num[2];

                    sscanf(buffer->str, "register %s", num); // obtaining the ID
#if DEBUG_CPP
                printf("\033[34mAttempting to register: \033[37;%dm%s\033[0m\n",
                        rid_map.find(std::string(num))->first == num ? 42 : 41, num);
#endif
                    int rid =
                            rid_map.find(std::string(num))->first == num ?
                                    rid_map.at(std::string(num)) : -1;

                    sockets->at(id).setRID(rid); // setting the RID of the related object

                    sockets->at(id).setRID(rid); // setting the RID of the related object
                    if (rid == -2)
                    {
                        monitors->push_back(sockets->at(id));
                    }
                    else
                    {
                        //Replace entry if already existing
                        if (registry->find(rid) != registry->end())
                            registry->at(rid) = sockets->at(id);
                        else
                            registry->insert(
                                    std::pair<int connectioninfo>(rid, sockets->at(id)));
#if DEBUG_CPP
                    printf("SERVER: Registry size: \033[31m%d\033[0m\n",
                            (int) registry->size());
#endif
                    }

                    info_callback("Registered %s", (void*) (buffer->str));
                } catch (std::exception &oor)
                {
                    error_callback("Registration failure");
                }
            }
            // cheking to see if the exit command was sent
            else if (sockets->size() > 0
                    && strstr(buffer->str, "exit") == buffer->str)
            {
#if DEBUG_CPP
                                printf("Exiting connection stored [thread:%d]\n", id);
#endif
                ConnectionInfo leaving = sockets->at(id); // getting ConnectionInfo for the connection

                // removing if registered as a robot
                if (registry->find(leaving.getRID())->first == leaving.getRID()
                        && registry->size() > 0)
                    registry->erase(leaving.getRID());

                // removing of registered as a monitor
                if (leaving.getRID() == -2)
                {
                    int erase_id = 0;
                    for (int i = 0; i < monitors->size() && exit_condition_callback();
                            i++)
                    {
                        if (monitors->size() > 0
                                && monitors->at(i).getConnectionDescriptor()
                                        == connection_descriptor) // matching connection descriptor
                        {
                            monitors->erase(monitors->begin() + i);
                            break;
                        }
                    }
                }
            }
            // checking if the connection is doing a latency test
            else if (strstr(buffer->str, "ping") == buffer->str)
            {
                write(connection_descriptor, "pong", 5); // returns pong to sender
            }
            else
                command_callback(*buffer, sockets->at(id).getRID()); // sending message to callback
        }

        free(buffer); // freeing buffer
    }
    write(connection_descriptor, "0.000,-1.0,discon", 18);
#if DEBUG_CPP
printf("\033[1;32mExiting thread: %d -- RID: %d\033[0m\n", id, sockets->at(id).getRID());
#endif
    close(connection_descriptor);
#if DEBUG_CPP
        printf("\t[thread %d] closed socket descriptor\n", id);
#endif
    pthread_exit(0); // exiting the client thread
}

int beginServer(std::function<void int> command_callback,
        std::function<void char void> info_callback,
        std::function<void char> error_callback,
        std::function<bool> exit_condition_callback,
        std::function<void char> warn_callback)
{
#if DEBUG_CPP
    puts("SERVER: Getting socket");
#endif

    sockets = new std::vector<connectioninfo>();
    monitors = new std::vector<connectioninfo>();
    registry = new std::map<int connectioninfo>();

// starting socket
    int socket_descriptor = socket(AF_INET, SOCK_STREAM, 0);
    if (socket_descriptor == -1)
    {
        error_callback("Error getting socket.");
        return 1;
    }

// Change receive timeout to 30 seconds.
    struct timeval timeout;
    timeout.tv_sec = 1;
    setsockopt(socket_descriptor, SOL_SOCKET, SO_RCVTIMEO,
            (struct timeval*) &timeout, sizeof(struct timeval));

    int ttl = 1;
    setsockopt(socket_descriptor, IPPROTO_IP, IP_TTL, &ttl, sizeof(ttl));

// Instantiate struct to store socket settings.
    struct sockaddr_in socket_address;
    memset(&socket_address, 0, sizeof(socket_address));
    socket_address.sin_family = AF_INET;
    socket_address.sin_addr.s_addr = INADDR_ANY;
    socket_address.sin_port = htons(SERVER_PORT);

#if DEBUG_CPP
    puts("SERVER: Binding socket");
#endif

// Bind to the socket
    if (bind(socket_descriptor, (struct sockaddr*) &socket_address,
            sizeof(socket_address)) == -1)
    {
        char err[64];
        sprintf(err, "Error binding to socket (%d)", errno); // making an error message that tells what went wrong
        // with binding the socket
#if DEBUG_CPP
                printf("\033[30;41m");
                char errno_msg[16];
                sprintf(errno_msg, "errno %d", errno);
                system(errno_msg);
                printf("\033[0m");
#endif
        error_callback(err);
        g_server_failure = true;
        return 2;
    }
#if DEBUG_CPP
    puts("SERVER: Listening to socket");
#endif
// Set socket to listen for connections
    if (listen(socket_descriptor, 3) == -1)
    {
        error_callback("Error listening for connections.");
        return 1;
    }

    int connection_descriptor; // variable that will contain the connection_descriptor of the most recent client accept
    struct sockaddr connection_addr;

    std::vector < pthread_t > threads; // vector to keep track of thread ids

#if DEBUG_CPP
    puts("SERVER: Starting socket loop");
#endif
// Loop to handle connections on the socket
    while (exit_condition_callback())
    {
        // Specify struct to store information about accepted connection
        socklen_t connection_addr_size = sizeof(struct sockaddr);

#if DEBUG_CPP
        puts("SERVER: looking for accept");
#endif

        // Accept connection
        connection_descriptor = accept(socket_descriptor, &connection_addr,
                &connection_addr_size);
#if DEBUG_CPP
        printf("SERVER: accepted \033[31;1m%s\033[0m\n", connection_addr.sa_data);
#endif
        if (connection_descriptor > 1)
        {
#if DEBUG_CPP
                    puts("Adding to connections");
#endif
            sockets->push_back(ConnectionInfo(connection_descriptor));
#if DEBUG_CPP
                    puts("SERVER: Made connection info object");
#endif
        }
#if DEBUG_CPP
                else
                {
                    puts("connection timed out");
                }
#endif

        if (connection_descriptor == -1)
        {
//          warn_callback("Error accepting connection.");
            continue;
        }
        else
        {

            // collecting arguments for client thread
            struct client_param clinet_args = (struct client_param )
                    { command_callback, info_callback, error_callback,
                            exit_condition_callback, (int) sockets->size() - 1 };

            pthread_attr_t attr;
            pthread_attr_init(&attr);
            pthread_t tid;

#if DEBUG_CPP
            puts("Starting thread");
#endif

            // starting client thread
            // this was done so that the server can keep accepting connections
            // as it is simultainiously communicating with a client
            pthread_create(&tid, &attr, runClient, &clinet_args);
            threads.push_back(tid); // keeping track of thread ids
        }
    }

#if DEBUG_CPP
        puts("\033[1;32mClosing socket\033[0m");
#endif

// waiting for all client handling to die
#if DEBUG_CPP
        puts("Waiting for threads to join");
#endif
    for (pthread_t tid : threads)
    {
        pthread_join(tid, NULL); // waiting for threads to die
    }

    close(socket_descriptor);
    return 0;
}
#endif

</int></connectioninfo></connectioninfo></void></bool></void></void></void></int></void></void></void></connectioninfo></int></connectioninfo></bool></void></void></void></nanoseconds></seconds></nanoseconds></seconds></chrono></iostream></void></bool></void></void></void></bool></void></void></void></string></map></vector></functional></sys></fcntl.h></sys></time.h></sys></arpa></netinet></sys></sys></string.h></stdlib.h></stdio.h></sys></sys></unistd.h></pthread.h></swarm_server>

References

This is the exact same issue as https://community.particle.io/t/udp-delays-on-argon/50917/11

Feature/Enhancement Request

We were hoping that the UDP/TCP could be looked into, and there could be some kind of resolution to the issue. The actual usage does not have to be "thread safe" but it would be nice if threads could be near by.

该提问来源于开源项目:particle-iot/device-os

  • 点赞
  • 写回答
  • 关注问题
  • 收藏
  • 复制链接分享
  • 邀请回答

4条回答

  • weixin_39790168 weixin_39790168 5月前

    Is anybody available to comment on this problem? was able to confirm the issue in the post linked above, and since then the same issue with UDP has been echoed by more users in that forum thread. Concise test code is available there to replicate the issue without the overhead of the source code above. Thanks!

    点赞 评论 复制链接分享
  • weixin_39877805 weixin_39877805 5月前

    I have been struggling with networking issues on the Photon for a long time. A new theory I have based on latest tests is that the lack of any delays in my code might cause the system thread to get very little execution time and does not handle the network traffic properly.

    Try if adding a 1 ms delay in your app loop helps, because it yields a thread switch.

    点赞 评论 复制链接分享
  • weixin_39707478 weixin_39707478 5月前

    Thanks everyone for the wealth of info here, including Rick's tests in the community. We will look into this.

    点赞 评论 复制链接分享
  • weixin_39805720 weixin_39805720 5月前

    Just wanting to say I'm REALLY looking forward to a fix for this, it's affecting my development significantly. Thanks for looking into it.

    点赞 评论 复制链接分享

相关推荐