Expand/Shrink

nopoll_conn_send_text

Definition: include nopoll.e

integer bytes_written = nopoll_conn_send_text(atom conn, object content, integer len=length(content))
Description: Send a UTF-8 text (op code 1) message over the provided connection with the provided length.

conn: The connection where the message will be sent.
content: The content to be sent (it should be utf-8 content or the function will fail). Should be string or atom (/raw memory address)
len: Number of bytes to take from the content to be sent. If not provided, for that default to work, content must be a string.

Returns: The number of bytes written otherwise < 0 is returned in case of failure.
The function will fail if some parameter is NULL or undefined, or the content provided is not UTF-8.
In the case of failure, also check errno variable to know more what went wrong.
See Notes below to know more about error codes and when it is possible to retry write operations.

The function returns the number of bytes sent, being the max amount of bytes that can be reported as sent by this funciton.
This means value reported by this function do not includes headers. The funciton also returns the following general indications:
N : number of bytes sent (user land bytes sent, without including web socket headers).
0 : no bytes sent (see errno indication). See also nopoll_conn_complete_pending_write()
-1 : failure found
-2 : retry operation needed (NOPOLL_EWOULDBLOCK) [PL: but that is 11!]
Notes: Retrying failed write operations
Every time you do a write operation (using for example nopoll_conn_send_text or nopoll_conn_send_text_fragment) there is a possibility that the write operation failes because the socket isn’t capable to keep on accepting more data.

In that case, errno == 11 (or NOPOLL_EWOULDBLOCK) is returned so you can check this to later retry the write operation.

Because websocket involves sending headers that already includes the size of the message sent, you cannot just retry by calling again to the send operation used (like nopoll_conn_send_text), especially because you must "continue" the send operation where it was left instead of sending more content with additional headers. In short, you must complete the operation.

To this end, you must use the following functions to check and complete pending write operations:
nopoll_conn_pending_write_bytes()
nopoll_conn_complete_pending_write()

Here is a possible complete function considering all points:

--DEV not tested!
include nopoll.e

function websocket_write(atom conn, string content)

    -- FIRST PART: normal send operation
    integer tries = 0, 
            bytes_written,
            len = length(content)
    -- do write operation and check
    bytes_written = nopoll_conn_send_text(conn, content, len)
    if bytes_written==len then
        -- operation completed, just return bytes written
        return bytes_written
    end if

    -- SECOND PART: retry in the case of failure
    --  some failure found, check errno
    while tries<5 
      and errno==NOPOLL_EWOULDBLOCK
      and nopoll_conn_pending_write_bytes(conn)>0 do
        -- ok, unable to write all data but that data is waiting to be flushed
        -- you can return here and then make your application to retry again or
        -- try it right now, but with a little pause before continue
        sleep(0.01) -- lets wait 10ms
        -- flush and check if write operation completed
        if nopoll_conn_complete_pending_write(conn)==0 then
            return len
        end if        
        -- limit loop
        tries +=1
    end while
        
    -- failure, return error code reported by the first call or the last retry
    return  bytes_written
end function
As we can see, the example tries to first write the content and then check for errors, trying to complete write in the case of errno == NOPOLL_EWOULDBLOCK, but, before going ahead retrying, the function sleeps a bit.

A very important note to consider is that this isn’t by far the best way to do this.
This example is just to demonstrate the concept.
The "ideal" implementation would be not to do any retry here (second part) but let the engine loop and waiting for this WebSocket to retry later, letting the overall application to keep on doing other things meanwhile (like writing or handling I/O in other connections) rather than locking the caller (as the example do).

Knowing this, if you want a ready to use function that implements concept (for the second part), you can directly use: nopoll_conn_flush_writes().
With it, a fairly complete and efficient write operation would be:

    -- do write operation 
    bytes_written = nopoll_conn_send_text(conn, content)
    -- complete pending write by flushing and limitting operation for 2 seconds
    -- pass to the function bytes_written as returned by nopoll_conn_send_text
    bytes_written = nopoll_conn_flush_writes(conn, 2000000, bytes_written)
PL: note that errno is defined (in C) as WSAGetLastError(), not (yet) available via nopoll.e
See Also: nopoll_conn_is_ok, nopoll_conn_is_ready, nopoll_conn_pending_write_bytes, nopoll_conn_complete_pending_write, nopoll_conn_flush_writes, nopoll_conn_send_text_fragment