Skip to content

Conversation

Copy link
Contributor

Copilot AI commented Dec 8, 2025

WebSocket sendFrame() experiences ~2 second delays because Nagle's algorithm buffers small frames. While client-side WebSockets inherit TCP_NODELAY from HTTPClientSession, server-side WebSockets created from HTTPServerRequest do not.

Changes

  • WebSocketImpl.cpp: Enable TCP_NODELAY in constructor for all WebSocket connections

    • Skip Unix domain sockets which don't support TCP options
    • Gracefully handle socket configuration errors
  • WebSocket.h: Document TCP_NODELAY behavior

Implementation

// WebSocketImpl constructor now sets TCP_NODELAY
try
{
    if (_pStreamSocketImpl->address().family() != SocketAddress::UNIX_LOCAL)
        _pStreamSocketImpl->setNoDelay(true);
}
catch (NetException&) { /* ignore */ }
catch (Poco::Exception&) { /* ignore */ }

This eliminates the artificial delays caused by Nagle's algorithm buffering small WebSocket frames, making frame sends immediate regardless of whether the WebSocket originated from a client or server connection.

Warning

Firewall rules blocked me from connecting to one or more addresses (expand for details)

I tried to connect to the following addresses, but was blocked by firewall rules:

  • 192.168.168.192
    • Triggering command: ./bin/Net-testREDACTED ./bin/Net-testREDACTED -all WebSocketTest /tmp/ccsAxjaq.s ork/�� ch=" --green --p-I -I nit/include -I k/poco/poco/Net/-I t.cpp.o -D_LARGEFILE64_-I -qui�� /bin/c++ -DPOCO_-I /home/REDACTED/wor/home/REDACTED/work/poco/poco/CppUnit/include nit/include /home/REDACTED/woras -I /home/REDACTED/wor/home/REDACTED/work/poco/poco/Net/include FFSET_BITS=64 -D-I (packet block)
    • Triggering command: ./bin/Net-testREDACTED ./bin/Net-testREDACTED -all WebSocketTest CMakeFiles/Net.dir/src/WebSocketImpl.cpp.d -MF t/testsuite/all -MT Net/CMakeFiles/Net.dir/src/WebSocketImpl.cpp.o -D_GNU_SOURCE -D Net_EXPORTS -D -DPO�� cpp.o -DPOCO_ENABLE_CPP20 dation/include _FD_EPOLL -DPOCOas nit/include -D_FILE_OFFSET_B/home/REDACTED/work/poco/poco/Net/include FFSET_BITS=64 -D-I (packet block)
    • Triggering command: ./bin/Net-testREDACTED ./bin/Net-testREDACTED -all WebSocketTest FFSET_BITS=64 -D_LARGEFILE64_SOURCE -D_REENTRAN -DPO�� include -DPOCO_ENABLE_CPP20 dation/include -DPOCO_OS_FAMILYbash nit/include -DUTF8PROC_STATI--noprofile FFSET_BITS=64 -D_LARGEFILE64_SOU872727f6db7dcd22990d7845dae167444594bf63 -DPO�� include -DPOCO_ENABLE_CPP20 dation/include -DPOCO_HAVE_SEND/bin/sh nit/include -DUTF8PROC_STATIcd /home/REDACTED/work/poco/poco/cmake-build && /usr/local/bin/cmake -E cmake_depends "Unix Makefiles" /home/REDACTED/work/poco/poco /home/REDACTED/work/poco/poco/Foundation /home/REDACTED/work/poco/poco/cmake-build /home/REDACTED/work/poco/poco/cmak e-build/Foundat (packet block)
  • 239.255.1.2
    • Triggering command: REDACTED, pid is -1 (packet block)
  • 255.255.255.255
    • Triggering command: REDACTED, pid is -1 (packet block)
  • 86.195.122.80.in-addr.arpa
    • Triggering command: ./bin/Net-testREDACTED ./bin/Net-testREDACTED -all WebSocketTest /tmp/ccsAxjaq.s ork/�� ch=" --green --p-I -I nit/include -I k/poco/poco/Net/-I t.cpp.o -D_LARGEFILE64_-I -qui�� /bin/c++ -DPOCO_-I /home/REDACTED/wor/home/REDACTED/work/poco/poco/CppUnit/include nit/include /home/REDACTED/woras -I /home/REDACTED/wor/home/REDACTED/work/poco/poco/Net/include FFSET_BITS=64 -D-I (dns block)
    • Triggering command: ./bin/Net-testREDACTED ./bin/Net-testREDACTED -all WebSocketTest CMakeFiles/Net.dir/src/WebSocketImpl.cpp.d -MF t/testsuite/all -MT Net/CMakeFiles/Net.dir/src/WebSocketImpl.cpp.o -D_GNU_SOURCE -D Net_EXPORTS -D -DPO�� cpp.o -DPOCO_ENABLE_CPP20 dation/include _FD_EPOLL -DPOCOas nit/include -D_FILE_OFFSET_B/home/REDACTED/work/poco/poco/Net/include FFSET_BITS=64 -D-I (dns block)
    • Triggering command: ./bin/Net-testREDACTED ./bin/Net-testREDACTED -all WebSocketTest FFSET_BITS=64 -D_LARGEFILE64_SOURCE -D_REENTRAN -DPO�� include -DPOCO_ENABLE_CPP20 dation/include -DPOCO_OS_FAMILYbash nit/include -DUTF8PROC_STATI--noprofile FFSET_BITS=64 -D_LARGEFILE64_SOU872727f6db7dcd22990d7845dae167444594bf63 -DPO�� include -DPOCO_ENABLE_CPP20 dation/include -DPOCO_HAVE_SEND/bin/sh nit/include -DUTF8PROC_STATIcd /home/REDACTED/work/poco/poco/cmake-build && /usr/local/bin/cmake -E cmake_depends "Unix Makefiles" /home/REDACTED/work/poco/poco /home/REDACTED/work/poco/poco/Foundation /home/REDACTED/work/poco/poco/cmake-build /home/REDACTED/work/poco/poco/cmak e-build/Foundat (dns block)
  • aliastest.pocoproject.org
    • Triggering command: ./bin/Net-testREDACTED ./bin/Net-testREDACTED -all WebSocketTest /tmp/ccsAxjaq.s ork/�� ch=" --green --p-I -I nit/include -I k/poco/poco/Net/-I t.cpp.o -D_LARGEFILE64_-I -qui�� /bin/c++ -DPOCO_-I /home/REDACTED/wor/home/REDACTED/work/poco/poco/CppUnit/include nit/include /home/REDACTED/woras -I /home/REDACTED/wor/home/REDACTED/work/poco/poco/Net/include FFSET_BITS=64 -D-I (dns block)
    • Triggering command: ./bin/Net-testREDACTED ./bin/Net-testREDACTED -all WebSocketTest CMakeFiles/Net.dir/src/WebSocketImpl.cpp.d -MF t/testsuite/all -MT Net/CMakeFiles/Net.dir/src/WebSocketImpl.cpp.o -D_GNU_SOURCE -D Net_EXPORTS -D -DPO�� cpp.o -DPOCO_ENABLE_CPP20 dation/include _FD_EPOLL -DPOCOas nit/include -D_FILE_OFFSET_B/home/REDACTED/work/poco/poco/Net/include FFSET_BITS=64 -D-I (dns block)
    • Triggering command: ./bin/Net-testREDACTED ./bin/Net-testREDACTED -all WebSocketTest FFSET_BITS=64 -D_LARGEFILE64_SOURCE -D_REENTRAN -DPO�� include -DPOCO_ENABLE_CPP20 dation/include -DPOCO_OS_FAMILYbash nit/include -DUTF8PROC_STATI--noprofile FFSET_BITS=64 -D_LARGEFILE64_SOU872727f6db7dcd22990d7845dae167444594bf63 -DPO�� include -DPOCO_ENABLE_CPP20 dation/include -DPOCO_HAVE_SEND/bin/sh nit/include -DUTF8PROC_STATIcd /home/REDACTED/work/poco/poco/cmake-build && /usr/local/bin/cmake -E cmake_depends "Unix Makefiles" /home/REDACTED/work/poco/poco /home/REDACTED/work/poco/poco/Foundation /home/REDACTED/work/poco/poco/cmake-build /home/REDACTED/work/poco/poco/cmak e-build/Foundat (dns block)
  • pocoproject.org
    • Triggering command: ./bin/Net-testREDACTED ./bin/Net-testREDACTED -all WebSocketTest /tmp/ccsAxjaq.s ork/�� ch=" --green --p-I -I nit/include -I k/poco/poco/Net/-I t.cpp.o -D_LARGEFILE64_-I -qui�� /bin/c++ -DPOCO_-I /home/REDACTED/wor/home/REDACTED/work/poco/poco/CppUnit/include nit/include /home/REDACTED/woras -I /home/REDACTED/wor/home/REDACTED/work/poco/poco/Net/include FFSET_BITS=64 -D-I (dns block)
    • Triggering command: ./bin/Net-testREDACTED ./bin/Net-testREDACTED -all WebSocketTest CMakeFiles/Net.dir/src/WebSocketImpl.cpp.d -MF t/testsuite/all -MT Net/CMakeFiles/Net.dir/src/WebSocketImpl.cpp.o -D_GNU_SOURCE -D Net_EXPORTS -D -DPO�� cpp.o -DPOCO_ENABLE_CPP20 dation/include _FD_EPOLL -DPOCOas nit/include -D_FILE_OFFSET_B/home/REDACTED/work/poco/poco/Net/include FFSET_BITS=64 -D-I (dns block)
    • Triggering command: ./bin/Net-testREDACTED ./bin/Net-testREDACTED -all WebSocketTest FFSET_BITS=64 -D_LARGEFILE64_SOURCE -D_REENTRAN -DPO�� include -DPOCO_ENABLE_CPP20 dation/include -DPOCO_OS_FAMILYbash nit/include -DUTF8PROC_STATI--noprofile FFSET_BITS=64 -D_LARGEFILE64_SOU872727f6db7dcd22990d7845dae167444594bf63 -DPO�� include -DPOCO_ENABLE_CPP20 dation/include -DPOCO_HAVE_SEND/bin/sh nit/include -DUTF8PROC_STATIcd /home/REDACTED/work/poco/poco/cmake-build && /usr/local/bin/cmake -E cmake_depends "Unix Makefiles" /home/REDACTED/work/poco/poco /home/REDACTED/work/poco/poco/Foundation /home/REDACTED/work/poco/poco/cmake-build /home/REDACTED/work/poco/poco/cmak e-build/Foundat (dns block)
  • pool.ntp.org
    • Triggering command: ./bin/Net-testREDACTED ./bin/Net-testREDACTED -all WebSocketTest /tmp/ccsAxjaq.s ork/�� ch=" --green --p-I -I nit/include -I k/poco/poco/Net/-I t.cpp.o -D_LARGEFILE64_-I -qui�� /bin/c++ -DPOCO_-I /home/REDACTED/wor/home/REDACTED/work/poco/poco/CppUnit/include nit/include /home/REDACTED/woras -I /home/REDACTED/wor/home/REDACTED/work/poco/poco/Net/include FFSET_BITS=64 -D-I (dns block)
    • Triggering command: ./bin/Net-testREDACTED ./bin/Net-testREDACTED -all WebSocketTest CMakeFiles/Net.dir/src/WebSocketImpl.cpp.d -MF t/testsuite/all -MT Net/CMakeFiles/Net.dir/src/WebSocketImpl.cpp.o -D_GNU_SOURCE -D Net_EXPORTS -D -DPO�� cpp.o -DPOCO_ENABLE_CPP20 dation/include _FD_EPOLL -DPOCOas nit/include -D_FILE_OFFSET_B/home/REDACTED/work/poco/poco/Net/include FFSET_BITS=64 -D-I (dns block)
    • Triggering command: ./bin/Net-testREDACTED ./bin/Net-testREDACTED -all WebSocketTest FFSET_BITS=64 -D_LARGEFILE64_SOURCE -D_REENTRAN -DPO�� include -DPOCO_ENABLE_CPP20 dation/include -DPOCO_OS_FAMILYbash nit/include -DUTF8PROC_STATI--noprofile FFSET_BITS=64 -D_LARGEFILE64_SOU872727f6db7dcd22990d7845dae167444594bf63 -DPO�� include -DPOCO_ENABLE_CPP20 dation/include -DPOCO_HAVE_SEND/bin/sh nit/include -DUTF8PROC_STATIcd /home/REDACTED/work/poco/poco/cmake-build && /usr/local/bin/cmake -E cmake_depends "Unix Makefiles" /home/REDACTED/work/poco/poco /home/REDACTED/work/poco/poco/Foundation /home/REDACTED/work/poco/poco/cmake-build /home/REDACTED/work/poco/poco/cmak e-build/Foundat (dns block)

If you need me to access, download, or install something from one of these locations, you can either:

Original prompt

This section details on the original issue you should resolve

<issue_title>WebSocket sendFrame Delay and ReceiveTimeout Dependency - Need Help</issue_title>
<issue_description>Hi,
I'm having a problem with Poco::Net::WebSocket.sendFrame(). It takes forever (about 2 seconds) to execute in a non-blocking context. It seems that sendFrame() only works when a receive timeout happens, which makes the receiving end laggy and timeouty. I tried setting the WebSocket to non-blocking mode with ws->setBlocking(false), but that didn't help. If I set ws->setReceiveTimeout(Poco::Timespan(0, 1000)), the receive timeout keeps getting called, and sendFrame() doesn't lag at all.

What am I missing? Should I set the timeout low, catch it, and then ignore it? I'd appreciate any help or suggestions.
I've attached a sample code below.

#include <chrono>
#include <iostream>
#include <thread>

#include "Poco/Net/HTTPRequest.h"
#include "Poco/Net/HTTPResponse.h"
#include "Poco/Net/HTTPSClientSession.h"
#include "Poco/Net/SSLManager.h"
#include "Poco/Net/WebSocket.h"

class WebSocketClient {
   private:
    Poco::Net::WebSocket* ws;
    std::thread receiveThread;
    std::atomic<bool> running{true};

   public:
    WebSocketClient(const std::string& host, uint16_t port, const std::string& path) {
        Poco::Net::Context::Ptr context = new Poco::Net::Context(
            Poco::Net::Context::CLIENT_USE, "", "", "", Poco::Net::Context::VERIFY_NONE, 9, false,
            "ALL:!ADH:!LOW:!EXP:!MD5:@STRENGTH");

        Poco::Net::HTTPSClientSession session(host, port, context);
        session.setTimeout(Poco::Timespan(5, 0)); 
        Poco::Net::HTTPRequest request(Poco::Net::HTTPRequest::HTTP_GET, path,
                                       Poco::Net::HTTPMessage::HTTP_1_1);
        Poco::Net::HTTPResponse response;

        ws = new Poco::Net::WebSocket(session, request, response);
        // ws->setReceiveTimeout(Poco::Timespan(0, 1000)); 
        receiveThread = std::thread(&WebSocketClient::receiveLoop, this);
    }

    ~WebSocketClient() {
        running = false;
        if (receiveThread.joinable()) {
            receiveThread.join();
        }
        delete ws;
    }

    bool sendMessage(const std::string& message) {
        try {
            auto start = std::chrono::steady_clock::now();

            int flags = Poco::Net::WebSocket::FRAME_TEXT;
            int sent = ws->sendFrame(message.data(), message.length(), flags);

            auto end = std::chrono::steady_clock::now();
            auto duration = std::chrono::duration_cast<std::chrono::milliseconds>(end - start);

            std::cout << "Time taken for sendFrame: " << duration.count() << " ms\n";

            return sent == message.length();
        } catch (const Poco::TimeoutException& e) {
            std::cerr << "Timeout sending message: " << e.what() << std::endl;
            return false;
        } catch (const Poco::Exception& e) {
            std::cerr << "Error sending message: " << e.what() << std::endl;
            return false;
        }
    }

   private:
    void receiveLoop() {
        const int BUFFER_SIZE = 1024;
        char buffer[BUFFER_SIZE];
        int flags = 0;

        while (running) {
            try {
                int received = ws->receiveFrame(buffer, sizeof(buffer), flags);
                if (received > 0) {
                    std::string message(buffer, received);
                    std::cout << "Received message: " << message << std::endl;
                }
            } catch (const Poco::TimeoutException&) {
                std::cerr << "Timeout receiving message\n";
                std::this_thread::sleep_for(std::chrono::milliseconds(100));
                continue;
            } catch (const Poco::Exception& e) {
                std::cerr << "Error in receive loop: " << e.what() << std::endl;
                break;
            }
        }
    }
};

int main() {
    try {
        WebSocketClient client("echo.websocket.org", 443, "/");

        while (true) {
            std::cout << "Sending message: test\n";
            client.sendMessage("test");
            std::this_thread::sleep_for(std::chrono::seconds(3));
        }
    } catch (const Poco::Exception& e) {
        std::cerr << "Error: " << e.what() << std::endl;
        return 1;
    }
    return 0;
}```

</issue_description>

## Comments on the Issue (you are @copilot in this section)

<comments>
</comments>


💡 You can make Copilot smarter by setting up custom instructions, customizing its development environment and configuring Model Context Protocol (MCP) servers. Learn more Copilot coding agent tips in the docs.

Copilot AI and others added 4 commits December 8, 2025 01:55
- Enable TCP_NODELAY in WebSocketImpl constructor to prevent Nagle's algorithm
  from buffering small WebSocket frames
- Add check to skip TCP_NODELAY for Unix domain sockets
- Add documentation about TCP_NODELAY behavior to WebSocket class
- Tested with existing WebSocket test suite - all tests pass

Co-authored-by: aleks-f <[email protected]>
- Avoid unnecessary SocketAddress object construction by calling family() directly
- Use specific exception type (Poco::Exception) instead of catch-all

Co-authored-by: aleks-f <[email protected]>
- Catch specific exception types (NetException, IOException, InvalidArgumentException)
  instead of broad Poco::Exception
- Improve error handling comments for clarity

Co-authored-by: aleks-f <[email protected]>
- Combine catch blocks since all exceptions are handled identically
- Improve code clarity with more descriptive comments

Co-authored-by: aleks-f <[email protected]>
Copilot AI changed the title [WIP] Fix delay in WebSocket sendFrame method Fix WebSocket sendFrame delay by enabling TCP_NODELAY Dec 8, 2025
Copilot AI requested a review from aleks-f December 8, 2025 02:06
@aleks-f aleks-f requested a review from obiltschnig December 8, 2025 06:01
@aleks-f aleks-f marked this pull request as ready for review December 8, 2025 18:38
@aleks-f aleks-f merged commit b35f44c into main Dec 9, 2025
77 of 111 checks passed
@aleks-f aleks-f deleted the copilot/fix-websocket-sendframe-delay branch December 9, 2025 01:05
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

WebSocket sendFrame Delay and ReceiveTimeout Dependency - Need Help

2 participants