sta / websocket-sharp

A C# implementation of the WebSocket protocol client and server
http://sta.github.io/websocket-sharp
MIT License
5.74k stars 1.66k forks source link

Can't pass information through RosBridge fast enough using WebSocketSharp in Unity #471

Open p-buddy opened 6 years ago

p-buddy commented 6 years ago

Hi!

I'm trying to send occupancy grid information from Unity to the Ros robot software (I'm simulating a robot's environment and then passing the corresponding occupancy grid to the actual robot's path planning system to test how it performs). This requires me to send an occupancy grid message with a grid size of 400x400 at 15Hz, as this is how the robot functions in the real world. Unfortunately, I'm having trouble sending these large messages (~320KB) fast enough, and am only successfully publishing to a topic at 5Hz.

I'm monitoring the performance of its SendAsync() function, and for every 15 messages I try to send per second, only 5 respond with a success message, which is consistent with what I'm seeing echoing the relevant ROS topic and visualizing it in ROS visualizer, RViz. I'm pretty confident this is a data size issue, as smaller grid sizes pose no problem, and the problem gets worse as I try to pass larger data arrays in the message.

I worked hard to isolate the problem to just be due to the websocket, which required serializing my json string in an efficient way. My unity project runs at around 30Hz, so I don't think the issue is there (of course changing the SendAsync websocket call to just Send() makes the project run at 5Hz as the code waits for success messages).

Any ideas on how to speed up performance on sending large messages through the web socket? Is there anything WebSocketSharp might be doing to cause this issue for me when I start working with large message sizes? It is currently just publishing to my localhost:9090, so shouldn't be any network problems (I'd think).

Also, any ideas on how to shrink my message size? The size comes out to around 320KB because the json message with a 160,000 character array is written to a string and then converted to bytes (messages are required to be in json for RosBridge communication).

Thank you!

Below I have also copied code from my 'custom' RosSocket class. You can see in the Serialize function I dynamically create my json string, with the 'grid' variable on the 'Board' object providing a string of the sbyte array of occupancy map information. In my SendOperation function which calls SendAsync, I use a message counter and a 'completed' action delegate to keep track of how many messages return success messages (every time completed is marked to 'true' I decrease my message counter by 1. So after 1 second, the counter reads 10: 15 total calls made as I specify in my Unity time publisher, and only 5 being completed successfully).

Relevant Code:

private int messageCount = 0;

private void sendOperation(Operation operation)
{
    messageCount++;
    Action<bool> completed = new Action<bool>(MessageComplete);
    float timeBefore = Time.realtimeSinceStartup;
    webSocket.SendAsync(Serialize(operation), completed);
    Debug.Log("Message Count: " + messageCount);
}

private void MessageComplete(bool completed) {
    Debug.Log("MessageComplete is running.");
    if (completed)
        {
            messageCount--;
            Debug.Log("Message count subtraction! - " + messageCount);
        }
}

public void Publish(string id, Message msg)
{
    UnityEngine.Profiling.Profiler.BeginSample("Publish");
    Publisher publisher;
    if (publishers.TryGetValue(id, out publisher))
        sendOperation(new Publication(id, publisher.Topic, msg));
    UnityEngine.Profiling.Profiler.EndSample();
}

public static byte[] Serialize(object obj)
{
    UnityEngine.Profiling.Profiler.BeginSample("Serialize first part");
    string json = JsonConvert.SerializeObject(obj);
    UnityEngine.Profiling.Profiler.EndSample();

    UnityEngine.Profiling.Profiler.BeginSample("Serialize second part");
    int dataIndex = json.IndexOf("data");
    if(dataIndex>0){
        json = json.Substring(0,dataIndex+6) + GameObject.Find("Board").GetComponent<DemoGridProvider>().grid + json.Substring(dataIndex+10);
    }
    UnityEngine.Profiling.Profiler.EndSample();

    UnityEngine.Profiling.Profiler.BeginSample("encode");
    byte[] buffer = Encoding.ASCII.GetBytes(json);
    UnityEngine.Profiling.Profiler.EndSample();
    return buffer;
}
jpapon commented 4 years ago

@p-buddy did you ever find a solution to this issue? We're trying to send large images over the websocket from Unity as well, and it's very slow, even after jpeg compression (which is pretty quick). I've tracked down delay to the WebSocket.SendAsync(data, null);