SOSendToMultiEx

Syntax

#include <cafe.h>
#include <cafe/network.h>

#define ROUNDUP_SNDTMEX_BUFF_LEN(len) \
	(((u32)(len) + (PPC_IO_BUFFER_ALIGN)-1) & ~((PPC_IO_BUFFER_ALIGN)-1))

struct sendto_multi_ex_buffers
{
    void            *buffer;
    unsigned int     bufferlen;
    int             *datagram_lens;
    unsigned int     datagram_lens_len;
    struct sockaddr *dests;
    unsigned int     destslen;
    int             *results;
    unsigned int     resultslen;
};

int SOSendToMultiEx (
                     int                             fd, 
                     int                             flags,
                     struct sendto_multi_ex_buffers *buffs, 
                     int                             send_datagram_count
                     );

Parameters

fd Descriptor of the sending socket. Only UDP sockets are supported.
flags This argument supports the following value:

MSG_DONTROUTE: Do not use a gateway to send the packet.
buffs Pointer to struct sendto_multi_ex_buffers. This structure has following members.
buffer Pointer to the array of datagrams to send. It has to be aligned to the PPC cache line size (defined by PPC_IO_BUFFER_ALIGN).
bufferlen The length of buffer. It has to be calculated by ROUNDUP_SNDTMEX_BUFF_LEN(datagram_lens[0]+datagram_lens[1]+ ... + datagram_lens[send_datagram_count]) when allocating memory for buffer.
datagram_lens Pointer to the array of lengths of each datagram. It has to be aligned to the PPC cache line size (defined by PPC_IO_BUFFER_ALIGN).
datagram_lens_len The length of datagram_lens. It has to be calculated by ROUNDUP_SNDTMEX_BUFF_LEN(sizeof(int) * send_datagram_count) when allocating memory for datagram_lens.
dests Pointer to the array of destination addresses. It has to be aligned to the PPC cache line size (defined by PPC_IO_BUFFER_ALIGN).
destslen The length of dests. It has to be calculated by ROUNDUP_SNDTMEX_BUFF_LEN(sizeof(struct sockaddr) * send_datagram_count) when allocating memory for results.
results Pointer to the buffer which is used to store the number of bytes sent. It has to be aligned to the PPC cache line size (defined by PPC_IO_BUFFER_ALIGN).
resultslen The length of results. It has to be calculated by ROUNDUP_SNDTMEX_BUFF_LEN(sizeof(int) * send_datagram_count) when allocating memory for results.
send_datagram_count The number of destinations and datagrams. The valid range is between 1 and 64. This function sends this number of datagrams to this number of destinations.

Return Values

Upon success, returned int value will be the total number of characters sent. If one of the send requests is failed while processing send_datagram_count number of send requests for some reason (SO_ENOBUFS, etc.), then this function returns immediately with -1 and set errno accordingly.

Note that the numbers of bytes sent are stored in results. When the caller gets -1 from this function, it should check results also to see which send request was failed and how many bytes are actually sent.

For example, when -1 is returned despite 5 was specified as send_datagram_count, and when results[0] and result[1] are positive value but results[2] is -1, it indicates that first and second send requests were processed but third send request was failed (and corresponding error code was set to errno). result[3] and result[4] are 0 in this situation.

Errors

These are errors generated by the socket layer; additional errors may be generated by the underlying protocol modules. For information, see SOLastError.

SO_EBUSY The socket resource manager is busy and cannot process requests. Try again later.
SO_EINVAL One of the specified arguments is invalid (e.g., buffs is NULL, send_datagram_count is out of range, buffers inside sendto_multi_ex_buffers are not aligned, lengths inside sendto_multi_ex_buffers are not aligned, etc.).
SO_EABORTED The operation was aborted. When a socket is closed, operations that are blocked on the socket are aborted.
SO_EUNKNOWN An unknown error occurred for some reason. Should be treated as a serious error and the corresponding socket should be closed.
SO_ELIBNOTREADY Socket library is not initialized.
SO_ENOMEM Indicates insufficient memory in the network stack. Usually a transient condition and the operation can be retried after a few seconds. If it occurs repeatedly should be treated as a serious error and the corresponding socket should be closed.
SO_ENOTSOCK The argument fd does not refer to a socket.
SO_EMSGSIZE datagram_lens inside sendto_multi_ex_buffers structure contains invalid length. The valid range for a length of UDP datagram is between 0 and 1478.
SO_EWOULDBLOCK The socket is marked non-blocking and the requested operation would block.
SO_EPIPE The socket is unable to send any more data. This typically indicates that the socket is not connected.
SO_EDESTADDRREQ Indicates destination address has not been specified for the send on UDP sockets.
SO_ENOBUFS Indicates insufficient packet buffers in the network stack. Usually a transient condition and the operation can be retried after a few seconds. If it occurs repeatedly should be treated as a serious error and the corresponding socket should be closed.
SO_ECONNREFUSED The socket received an ICMP destination unreachable message from other side while processing send request.
SO_EADDRNOTAVAIL Returned only for UDP sends to broadcast address when network interface is not UP. Should be treated as a serious error and the corresponding socket should be closed.
SO_ENETUNREACH The specified destination is unreachable.
SO_EFAULT An abnormal operation occurred in the stack. Should be treated as a serious error and the corresponding socket should be closed.
SO_EIEIO An error occurred in the network card driver while sending out the packet. Returned only for UDP sockets. Usually a transient condition and the operation can be retried after a few seconds. If it occurs repeatedly should be treated as a serious error and the corresponding socket should be closed.
SO_EPROTOTYPE The specified socket is not UDP socket.
SO_ERANGEINVALID An unknown error occurred for some reason in the stack. Should be treated as a serious error and the corresponding socket should be closed.
SO_EAPIERROR An error occurred when processing the request in the stack. Should be treated as a serious error and the corresponding socket should be closed.

Description

This function is an extended version of the SOSendTo function. This function can send different UDP packets to different destinations with one function call instead of needing to call the SOSendTo function several times.

No indication of failure to deliver is implicit in SOSendToMultiEx function. Only locally detected errors are indicated by a return value of -1. When this occurs, errno is set to the error code for the error.

Unlike SOSendToMulti function, this function can send "different UDP packets" (to different destinations). SOSendToMulti function was prepared to send "same UDP packets" to different destinations. If an application do not have to send different UDP packets (but has to send it to different destinations) then using SOSendToMulti function is recommended because it is more efficient than calling SOSendToMultiEx function by specifying same destination addresses in dests.

Limitations

Required buffers for data transfer


Fig.1. Required buffers and data stored location

Sample Code

#define TOTAL_PACKET_NUM        64
#define SEND_DATA_LEN           1460

void cleanup_mem (struct sendto_multi_ex_buffers *sndtmex_args)
{
    if (sndtmex_args.buffer)
        MEMFreeToDefaultHeap(sndtmex_args.buffer);
    if (sndtmex_args.datagram_lens)
        MEMFreeToDefaultHeap(sndtmex_args.datagram_lens);
    if (sndtmex_args.dests)
        MEMFreeToDefaultHeap(sndtmex_args.dests);
    if (sndtmex_args.results)
        MEMFreeToDefaultHeap(sndtmex_args.results);
}

int main (void)
{
    int                            fd    = -1;
    int                            rval  = 0;
    int                            i     = 0;

    struct sendto_multi_ex_buffers sndtmex_args;
    int                            send_datagram_len   = 0;
    int                            send_datagram_count = 0;
    int                            total_buffer_len    = 0;

    int                            total_sent_bytes = 0;

    /* Initialize socket library */
    if ((rval = SOInit()) < 0)
    {
        OSReport("SOinit failed with rval %d\n", rval);
        return -1;
    }

    memset(&sndtmex_args, 0x00, sizeof(sendto_multi_ex_buffers));

    /* Prepare variables for SOSendToMultiEx() ************************* */
    send_datagram_len   = SEND_DATA_LEN;
    send_datagram_count = TOTAL_PACKET_NUM;
    total_buffer_len    = send_datagram_len*send_datagram_count;

    sndtmex_args.bufferlen          = ROUNDUP_SNDTMEX_BUFF_LEN(total_buffer_len);
    sndtmex_args.datagram_lens_len  = ROUNDUP_SNDTMEX_BUFF_LEN(sizeof(int)*send_datagram_count);
    sndtmex_args.destslen           = ROUNDUP_SNDTMEX_BUFF_LEN(sizeof(struct sockaddr)*send_datagram_count);
    sndtmex_args.resultslen         = ROUNDUP_SNDTMEX_BUFF_LEN(sizeof(int)*send_datagram_count);

    sndtmex_args.buffer  = (void *)MEMAllocFromDefaultHeapEx(sndtmex_args.bufferlen, PPC_IO_BUFFER_ALIGN);
    sndtmex_args.datagram_lens = (int *)MEMAllocFromDefaultHeapEx(sndtmex_args.datagram_lens_len, PPC_IO_BUFFER_ALIGN);
    sndtmex_args.dests   = (struct sockaddr *)MEMAllocFromDefaultHeapEx(sndtmex_args.destslen, PPC_IO_BUFFER_ALIGN);
    sndtmex_args.results = (int *)MEMAllocFromDefaultHeapEx(sndtmex_args.resultslen, PPC_IO_BUFFER_ALIGN);

    if (!sndtmex_args.buffer || !sndtmex_args.dests || !sndtmex_args.results || !sndtmex_args.datagram_lens) 
    {
        OSReport("Failed to allocate buffer aligned with PPC cache line\n");
        cleanup_mem(&sndtmex_args);
        return -1;
    }
    else 
    {
        memset(sndtmex_args.buffer, 0x00, sndtmex_args.bufferlen);
        memset(sndtmex_args.datagram_lens, 0x00, sndtmex_args.datagram_lens_len);
        memset(sndtmex_args.dests, 0x00, sndtmex_args.destslen);
        memset(sndtmex_args.results, 0x00, sndtmex_args.resultslen);
    }

    /* Create socket *************************************************** */
    fd = SOSocket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);
    if (fd < 0)
    {
        OSReport("Failed to create udp socket %d\n", fd);
        cleanup_mem(&sndtmex_args);
        return -1;
    }

    for (i=0; i < send_datagram_count; i++)
    {
        struct sockaddr_in *curr_addr = (struct sockaddr_in *)sndtmex_args.dests + i;
        int *curr_len = (int*)sndtmex_args.datagram_lens + i;

        curr_addr->sin_family  = AF_INET;
        curr_addr->sin_port    = RECEIVER_PORT_NUMBER;
        SOInetAtoN(SERVER_ADDR, &curr_addr->sin_addr);

        *curr_len = send_datagram_len;
    }

    rval = SOSendToMultiEx(fd, 0, &sndtmex_args, send_datagram_count);
    if (rval < 0)
    {
        for (i=0; i < send_datagram_count; i++)
        {
            int* curr_ret = sndtmex_args.results + i;
            if (*curr_ret > SOCKET_ERROR)
            {
                total_sent_bytes += *curr_ret;
            } 
            else 
            {
                OSReport("[client]SOSendToMultiEx() failed (send requests are processed %d times)\n", i);
                break;
            }
        }
        
        switch (SOLastError())
        {
            case SO_ENOMEM:
            case SO_EBUSY:
            case SO_EWOULDBLOCK:
            case SO_EIEIO:
                OSReport("[client]Sending socket is temporally busy.\n");
                break;
            default:
                OSReport("[client]Failed SOSendToMultiEx() (errno:%d).\n", SOLastError());
                cleanup_mem(&sndtmex_args);
                return -1;
        }
    }
    else
    {
        total_sent_bytes += rval;
    }
                                      :
                                      :
                                      :

Do Not Call From

Callbacks Do not call this function from any callback function.
Interrupt handler Do not call this function from any interrupt handler.
Exception handler Do not call this function from any exception handler.

See Also

SOSendTo
SOSendToMulti

Revision History

2014/01/22 Fixed typo.
2013/08/01 Initial version.


CONFIDENTIAL