SORecvFromMulti

Syntax

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

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

struct recvfrom_multi_buffers
{
    void            *buffer;
    unsigned int     bufferlen;
    struct sockaddr *froms;
    unsigned int     fromslen;
    int             *results;
    unsigned int     resultslen;
};

int SORecvFromMulti (int                            fd,
                     int                            flags,
                     struct recvfrom_multi_buffers *buffs,
                     int                            recv_datagram_len,
                     int                            recv_datagram_count,
                     struct timeval                *timeout);

Parameters

fd Descriptor of socket from which to receive. Only UDP sockets are supported.
flags Following value can be specified:
  • MSG_OOB........Request out of band data.
buffs Pointer to struct recvfrom_multi_buffers. This structure has following members.
buffer Destination buffer for received data.
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_RCVFMM_BUF_LEN(recv_datagram_len * redv_datagram_count) when allocating memory for buffer.
froms Pointer to the buffer for source address.
It has to be aligned to the PPC cache line size (defined by PPC_IO_BUFFER_ALIGN).
fromslen The length of froms. It has to be calculated by ROUNDUP_RCVFMM_BUF_LEN(sizeof(struct sockaddr) * redv_datagram_count) when allocating memory for froms.
results Pointer to the buffer which is used to store the number of bytes received.
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_RCVFMM_BUF_LEN(sizeof(int) * redv_datagram_count) when allocating memory for results.
recv_datagram_len The maximum length of each datagram to receive This is same as len for SORecvFrom function. If it is greater than 1478 then it will cause an SO_EMSGSIZE error.
recv_datagram_count The number of datagrams to wait before returning from this function. The valid range is between 1 and 64.
timeout Pointer to struct timeval. SORecvFromMulti function is blocked until timeout.
  • If specified timeout is 0 then it tries to read datagram as long as there is a datagram in a socket receive buffer until receiving recv_datagram_count datagrams.
  • If NULL is specified, it keeps blocking until receiving recv_datagram_count datagrams.

Return Values

positive value The number of receive requests which was successfully performed. The number of bytes received is stored to results.
If this value is less than recv_datagram_count, it indicates that one of the receive request failed before processing all requests and SOCKET_ERROR is set to corresponding results, also errno which is obtained from SOLastError is set.

For example, when n-3 is returned despite n was specified as recv_datagram_count, it indicates n-2th receive request was failed. SOCKET_ERROR is set to results[n-2], and errno is also set in this situation. n-1th and nth receive requests are not processed in this situation.
0 Timeout occurred before any receive request completes.
SOCKET_ERROR An error occurred while validating arguments (no receive request is processed yet).

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, one of the buffer is 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 Specified recv_datagram_len is invalid. The valid range is between 1 and 1478.
SO_EWOULDBLOCK No datagram is available in the socket receive buffer. Or other receive request is already waiting on the same socket.
SO_ENOBUFS Something occurred when manipulating socket receive buffer to read datagrams. If it occurs repeatedly should be treated as a serious error and the corresponding socket should be closed.
SO_ETIMEDOUT Timed out occurred before receiving specified number of datagrams..
SO_EFAULT An abnormal operation occurred in the stack. 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

The SORecvFromMulti function is an extended version of the SORecvFrom function. An application can pick up many datagrams from socket receive buffer by one function call instead of calling SORecvFrom function many times.

Limitations

Required Buffers For Data Transfer


fig2. Required memories and data stored location

Timeout

This function blocks until it receives specified number (recv_datagram_count) of datagrams or timeout.

If timeout is NULL then it blocks until it receives recv_datagram_count datagrams.

If timeout is specified but 0 then it keeps trying to read data as long as there is a datagram in a socket receive buffer until receiving recv_datagram_count datagrams. This function returns when there is no data anymore in a socket buffer even if it does not receive recv_datagram_count datagrams yet. Whether datagram is received can be distinguished by return value and results.

If timeout occurs before completing all requests, this function is unblocked and SO_ETIMEDOUT is set to errno, also SOCKET_ERROR is set to corresponding results. An application can detect it by comparing return value and specified recv_datagram_count (return value is smaller than recv_datagram_count in this case). Note that SOCKET_ERROR is not returned.

E.g.

Sample Code

#define TOTAL_PACKET_NUM 16
#define RECV_DATA_LEN    1024
#define SERVER_PORT_NUM  5555

void cleanup_mem (struct recvfrom_multi_buffers *rcvfmm_args)
{
    if (rcvfmm_args.buffer)
        MEMFreeToDefaultHeap(rcvfmm_args.buffer);
    if (rcvfmm_args.froms)
        MEMFreeToDefaultHeap(rcvfmm_args.froms);
    if (rcvfmm_args.results)
        MEMFreeToDefaultHeap(rcvfmm_args.results);
}

int main (void) {
    int                           fd    = -1;
    int                           rval  = 0;
    int                           errno = SO_SUCCESS;
    int                           i     = 0;
    struct sockaddr_in            server_addr;

    struct timeval                timeout;
    struct recvfrom_multi_buffers rcvfmm_args;
    int                           recv_datagram_len   = 0;
    int                           recv_datagram_count = 0;
    int                           total_buffer_len    = 0;

    memset(&timeout, 0x00, sizeof(struct timeval));
    memset(&rcvfmm_args, 0x00, sizeof(recvfrom_multi_buffers));

    /* Prepare variables for SORecvFromMulti() ************************* */
    recv_datagram_len   = RECV_DATA_LEN;
    recv_datagram_count = TOTAL_PACKET_NUM;
    total_buffer_len    = recv_datagram_len*recv_datagram_count;

    rcvfmm_args.bufferlen  = ROUNDUP_RCVFMM_BUFF_LEN(total_buffer_len);
    rcvfmm_args.fromslen   = ROUNDUP_RCVFMM_BUFF_LEN(sizeof(struct sockaddr)*recv_datagram_count);
    rcvfmm_args.resultslen = ROUNDUP_RCVFMM_BUFF_LEN(sizeof(int)*recv_datagram_count);

    rcvfmm_args.buffer  = (void *)MEMAllocFromDefaultHeapEx(rcvfmm_args.bufferlen, PPC_IO_BUFFER_ALIGN);
    rcvfmm_args.froms   = (struct sockaddr *)MEMAllocFromDefaultHeapEx(rcvfmm_args.fromslen, PPC_IO_BUFFER_ALIGN);
    rcvfmm_args.results = (int *)MEMAllocFromDefaultHeapEx(rcvfmm_args.resultslen, PPC_IO_BUFFER_ALIGN);

    if (!rcvfmm_args.buffer || !rcvfmm_args.froms || !rcvfmm_args.results)
    {
        OSReport("Failed to allocate buffer aligned with PPC cache line\n");
        cleanup_mem(&rcvfmm_args);
        return -1;
    }
    memset(rcvfmm_args.buffer, 0x00, rcvfmm_args.bufferlen);
    memset(rcvfmm_args.froms, 0x00, rcvfmm_args.fromslen);
    memset(rcvfmm_args.results, 0x00, rcvfmm_args.resultslen);

    /* Set 5 seconds as a timeout on SORecvFromMulti() call */
    timeout.tv_sec  = 5;
    timeout.tv_usec = 0;
 
    /* Create socket *************************************************** */
    fd = SOSocket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);
    if (fd < 0)
    {
        OSReport("Failed to create udp socket %d\n", fd);
        cleanup_mem(&rcvfmm_args);
        return -1;
    }

    /* Bind socket ***************************************************** */
    server_addr.sin_family      = AF_INET;
    server_addr.sin_port        = SERVER_PORT_NUM;
    server_addr.sin_addr.s_addr = INADDR_ANY;
    rval = SOBind(fd, (struct sockaddr *)&server_addr, sizeof(struct sockaddr_in));
    if (rval < 0)
    {
        errno = SOLastError();
        OSReport("Bind error on fd 0x%x errno %d\n", fd, errno);
        cleanup_mem(&rcvfmm_args);
        return -1;
    }

    /* Receive datagrams ********************************************** */
    rval = SORecvFromMulti(fd,                  /* socket descriptor */
                           0,                   /* flags */
                           &rcvfmm_args,        /* args */
                           recv_datagram_len,   /* data len to receive */
                           recv_datagram_count, /* data num to receive */
                           &timeout             /* timeout */
                           );
    if (rval < 0)
    {
        errno = SOLastError();
        if (errno == SO_EWOULDBLOCK) {
             OSReport("Same socket is already blocked on other receive request\n");
        } else {
            OSReport("SORecvFromMulti() is failed with invalid argument(s) (errno:%d)\n", errno);
        }
    } 
    else if (rval == 0) 
    {
        /* SORecvFromMulti() is unblocked before receiving datagram */
        errno = SOLastError();
        switch (errno) {
            case SO_EWOULDBLOCK: /* this can happen only when timeout is 0 sec */
                OSReport("No datagram is available\n");
                break;
            case SO_ETIMEDOUT:
                OSReport("Timeout occurred before receiving datagram\n");
                break;
            case SO_EABORTED:
                OSReport("Corresponding socket is closed before receiving datagram\n");
                break;
            default:
                OSReport("Error occurred for some reason (errno:%d)\n", errno);
                break;
        }
    } 
    else if (rval == recv_datagram_count)
    {
        /* Received specified number of datagrams */
        for (i=0; i&ltrecv_datagram_count; i++) {
            char               *data = (char*)(rcvfmm_args.buffer + (i * recv_datagram_len));
            struct sockaddr_in *source_addr = (struct sockaddr_in*)(rcvfmm_args.froms + i);
            int                *ret = rcvfmm_args.results + i;
            OSReport("[%d] Received %d bytes from %s:%d (data ptr:%p)\n", 
                        i, *ret, SOInetNtoA(source_addr->sin_addr), source_addr->sin_port, data);
        }
    }
    else 
    {
        /* SORecvFromMulti() is unblocked before receiving recv_datagram_count datagrams */
        for (i=0; i&ltrecv_datagram_count; i++) {
            char               *data = (char*)(rcvfmm_args.buffer + (i * recv_datagram_len));
            struct sockaddr_in *source_addr = (struct sockaddr_in*)(rcvfmm_args.froms + i);
            int                *ret = rcvfmm_args.results + i;
                
            if (*ret > 0) {
                OSReport("[%d] Received %d bytes from %s:%d (data ptr:%p)\n", 
                        i, *ret, SOInetNtoA(source_addr->sin_addr), source_addr->sin_port, data);
            } else {
                errno = SOLastError();
                if (errno == SO_ETIMEDOUT) {
                    OSReport("[%d] Specified time elapsed\n", i);
                } else {
                    OSReport("[%d] Failed to receive (errno:%d)\n", i);
                }
                break;
            }
        }
    }
                                      :
                                      :
                                      :
}

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

SORecvFrom

Revision History

2011/06/28 Initial version.


CONFIDENTIAL