diff --git a/features/netsocket/Socket.cpp b/features/netsocket/InternetSocket.cpp similarity index 62% rename from features/netsocket/Socket.cpp rename to features/netsocket/InternetSocket.cpp index 7317648b65..4e44ae574b 100644 --- a/features/netsocket/Socket.cpp +++ b/features/netsocket/InternetSocket.cpp @@ -1,4 +1,4 @@ -/* Socket +/* InternetSocket * Copyright (c) 2015 ARM Limited * * Licensed under the Apache License, Version 2.0 (the "License"); @@ -14,17 +14,19 @@ * limitations under the License. */ -#include "Socket.h" -#include "mbed.h" +#include "InternetSocket.h" +#include "platform/Callback.h" -Socket::Socket() - : _stack(0) - , _socket(0) - , _timeout(osWaitForever) +using namespace mbed; + +InternetSocket::InternetSocket() + : _stack(0), _socket(0), _timeout(osWaitForever), + _readers(0), _writers(0), _factory_allocated(false), + _pending(0) { } -nsapi_error_t Socket::open(NetworkStack *stack) +nsapi_error_t InternetSocket::open(NetworkStack *stack) { _lock.lock(); @@ -42,14 +44,14 @@ nsapi_error_t Socket::open(NetworkStack *stack) } _socket = socket; - _event = callback(this, &Socket::event); + _event = callback(this, &InternetSocket::event); _stack->socket_attach(_socket, Callback::thunk, &_event); _lock.unlock(); return NSAPI_ERROR_OK; } -nsapi_error_t Socket::close() +nsapi_error_t InternetSocket::close() { _lock.lock(); @@ -66,11 +68,23 @@ nsapi_error_t Socket::close() // on this socket event(); + // Wait until all readers and writers are gone + while (_readers || _writers) { + _lock.unlock(); + _event_flag.wait_any(FINISHED_FLAG, osWaitForever); + _lock.lock(); + } + _lock.unlock(); + + // When allocated by accept() call, will self desctruct on close(); + if (_factory_allocated) { + delete this; + } return ret; } -int Socket::modify_multicast_group(const SocketAddress &address, nsapi_socket_option_t socketopt) +int InternetSocket::modify_multicast_group(const SocketAddress &address, nsapi_socket_option_t socketopt) { nsapi_ip_mreq_t mreq; @@ -81,32 +95,32 @@ int Socket::modify_multicast_group(const SocketAddress &address, nsapi_socket_op return this->setsockopt(NSAPI_SOCKET, socketopt, &mreq, sizeof(mreq)); } -int Socket::join_multicast_group(const SocketAddress &address) +int InternetSocket::join_multicast_group(const SocketAddress &address) { return modify_multicast_group(address, NSAPI_ADD_MEMBERSHIP); } -int Socket::leave_multicast_group(const SocketAddress &address) +int InternetSocket::leave_multicast_group(const SocketAddress &address) { return modify_multicast_group(address, NSAPI_DROP_MEMBERSHIP); } -nsapi_error_t Socket::bind(uint16_t port) +nsapi_error_t InternetSocket::bind(uint16_t port) { // Underlying bind is thread safe SocketAddress addr(0, port); return bind(addr); } -nsapi_error_t Socket::bind(const char *address, uint16_t port) +nsapi_error_t InternetSocket::bind(const char *address, uint16_t port) { // Underlying bind is thread safe SocketAddress addr(address, port); return bind(addr); } -nsapi_error_t Socket::bind(const SocketAddress &address) +nsapi_error_t InternetSocket::bind(const SocketAddress &address) { _lock.lock(); nsapi_error_t ret; @@ -121,13 +135,13 @@ nsapi_error_t Socket::bind(const SocketAddress &address) return ret; } -void Socket::set_blocking(bool blocking) +void InternetSocket::set_blocking(bool blocking) { - // Socket::set_timeout is thread safe + // InternetSocket::set_timeout is thread safe set_timeout(blocking ? -1 : 0); } -void Socket::set_timeout(int timeout) +void InternetSocket::set_timeout(int timeout) { _lock.lock(); @@ -140,7 +154,7 @@ void Socket::set_timeout(int timeout) _lock.unlock(); } -nsapi_error_t Socket::setsockopt(int level, int optname, const void *optval, unsigned optlen) +nsapi_error_t InternetSocket::setsockopt(int level, int optname, const void *optval, unsigned optlen) { _lock.lock(); nsapi_error_t ret; @@ -155,7 +169,7 @@ nsapi_error_t Socket::setsockopt(int level, int optname, const void *optval, uns return ret; } -nsapi_error_t Socket::getsockopt(int level, int optname, void *optval, unsigned *optlen) +nsapi_error_t InternetSocket::getsockopt(int level, int optname, void *optval, unsigned *optlen) { _lock.lock(); nsapi_error_t ret; @@ -170,15 +184,24 @@ nsapi_error_t Socket::getsockopt(int level, int optname, void *optval, unsigned return ret; } +void InternetSocket::event() +{ + _event_flag.set(READ_FLAG|WRITE_FLAG); -void Socket::sigio(Callback callback) + _pending += 1; + if (_callback && _pending == 1) { + _callback(); + } +} + +void InternetSocket::sigio(Callback callback) { _lock.lock(); _callback = callback; _lock.unlock(); } -void Socket::attach(Callback callback) +void InternetSocket::attach(Callback callback) { sigio(callback); } diff --git a/features/netsocket/InternetSocket.h b/features/netsocket/InternetSocket.h new file mode 100644 index 0000000000..aea5d40c0d --- /dev/null +++ b/features/netsocket/InternetSocket.h @@ -0,0 +1,245 @@ + +/** \addtogroup netsocket */ +/** @{*/ +/* Socket + * Copyright (c) 2015 ARM Limited + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef INTERNETSOCKET_H +#define INTERNETSOCKET_H + +#include "netsocket/Socket.h" +#include "netsocket/NetworkStack.h" +#include "rtos/Mutex.h" +#include "rtos/EventFlags.h" +#include "Callback.h" +#include "mbed_toolchain.h" + +/** Socket implementation that uses IP network stack. + * Not to be directly used by applications. Cannot be directly instantiated. + */ +class InternetSocket : public Socket { +public: + /** Destroy a socket + * + * Closes socket if the socket is still open + */ + virtual ~InternetSocket() {} + + /** Opens a socket + * + * Creates a network socket on the network stack of the given + * network interface. Not needed if stack is passed to the + * socket's constructor. + * + * @param stack Network stack as target for socket + * @return 0 on success, negative error code on failure + */ + nsapi_error_t open(NetworkStack *stack); + + template + nsapi_error_t open(S *stack) { + return open(nsapi_create_stack(stack)); + } + + /** Close the socket + * + * Closes any open connection and deallocates any memory associated + * with the socket. Called from destructor if socket is not closed. + * + * @return 0 on success, negative error code on failure + */ + virtual nsapi_error_t close(); + + /** Subscribes to an IP multicast group + * + * @param address Multicast group IP address + * @return Negative error code on failure + */ + int join_multicast_group(const SocketAddress &address); + + /** Leave an IP multicast group + * + * @param address Multicast group IP address + * @return Negative error code on failure + */ + int leave_multicast_group(const SocketAddress &address); + + + /** Bind a specific address to a socket + * + * Binding a socket specifies the address and port on which to receive + * data. + * + * @param port Local port to bind + * @return 0 on success, negative error code on failure. + */ + nsapi_error_t bind(uint16_t port); + + /** Bind a specific address to a socket + * + * Binding a socket specifies the address and port on which to receive + * data. If the IP address is zeroed, only the port is bound. + * + * @param address Null-terminated local address to bind + * @param port Local port to bind + * @return 0 on success, negative error code on failure. + */ + nsapi_error_t bind(const char *address, uint16_t port); + + /** Bind a specific address to a socket + * + * Binding a socket specifies the address and port on which to receive + * data. If the IP address is zeroed, only the port is bound. + * + * @param address Local address to bind + * @return 0 on success, negative error code on failure. + */ + virtual nsapi_error_t bind(const SocketAddress &address); + + /** Set blocking or non-blocking mode of the socket + * + * Initially all sockets are in blocking mode. In non-blocking mode + * blocking operations such as send/recv/accept return + * NSAPI_ERROR_WOULD_BLOCK if they can not continue. + * + * set_blocking(false) is equivalent to set_timeout(-1) + * set_blocking(true) is equivalent to set_timeout(0) + * + * @param blocking true for blocking mode, false for non-blocking mode. + */ + virtual void set_blocking(bool blocking); + + /** Set timeout on blocking socket operations + * + * Initially all sockets have unbounded timeouts. NSAPI_ERROR_WOULD_BLOCK + * is returned if a blocking operation takes longer than the specified + * timeout. A timeout of 0 removes the timeout from the socket. A negative + * value give the socket an unbounded timeout. + * + * set_timeout(0) is equivalent to set_blocking(false) + * set_timeout(-1) is equivalent to set_blocking(true) + * + * @param timeout Timeout in milliseconds + */ + virtual void set_timeout(int timeout); + + /* Set socket options + * + * setsockopt allows an application to pass stack-specific options + * to the underlying stack using stack-specific level and option names, + * or to request generic options using levels from nsapi_socket_level_t. + * + * For unsupported options, NSAPI_ERROR_UNSUPPORTED is returned + * and the socket is unmodified. + * + * @param level Stack-specific protocol level or nsapi_socket_level_t + * @param optname Level-specific option name + * @param optval Option value + * @param optlen Length of the option value + * @return 0 on success, negative error code on failure + */ + virtual nsapi_error_t setsockopt(int level, int optname, const void *optval, unsigned optlen); + + /* Get socket options + * + * getsockopt allows an application to retrieve stack-specific options + * from the underlying stack using stack-specific level and option names, + * or to request generic options using levels from nsapi_socket_level_t. + * + * For unsupported options, NSAPI_ERROR_UNSUPPORTED is returned + * and the socket is unmodified. + * + * @param level Stack-specific protocol level or nsapi_socket_level_t + * @param optname Level-specific option name + * @param optval Destination for option value + * @param optlen Length of the option value + * @return 0 on success, negative error code on failure + */ + virtual nsapi_error_t getsockopt(int level, int optname, void *optval, unsigned *optlen); + + /** Register a callback on state change of the socket + * + * The specified callback will be called on state changes such as when + * the socket can recv/send/accept successfully and on when an error + * occurs. The callback may also be called spuriously without reason. + * + * The callback may be called in an interrupt context and should not + * perform expensive operations such as recv/send calls. + * + * Note! This is not intended as a replacement for a poll or attach-like + * asynchronous api, but rather as a building block for constructing + * such functionality. The exact timing of when the registered function + * is called is not guaranteed and susceptible to change. + * + * @param func Function to call on state change + */ + virtual void sigio(mbed::Callback func); + + /** Register a callback on state change of the socket + * + * @see Socket::sigio + * @deprecated + * The behaviour of Socket::attach differs from other attach functions in + * mbed OS and has been known to cause confusion. Replaced by Socket::sigio. + */ + MBED_DEPRECATED_SINCE("mbed-os-5.4", + "The behaviour of Socket::attach differs from other attach functions in " + "mbed OS and has been known to cause confusion. Replaced by Socket::sigio.") + void attach(mbed::Callback func); + + /** Register a callback on state change of the socket + * + * @see Socket::sigio + * @deprecated + * The attach function does not support cv-qualifiers. Replaced by + * attach(callback(obj, method)). + */ + template + MBED_DEPRECATED_SINCE("mbed-os-5.1", + "The attach function does not support cv-qualifiers. Replaced by " + "attach(callback(obj, method)).") + void attach(T *obj, M method) { + attach(mbed::callback(obj, method)); + } + +protected: + InternetSocket(); + virtual nsapi_protocol_t get_proto() = 0; + virtual void event(); + int modify_multicast_group(const SocketAddress &address, nsapi_socket_option_t socketopt); + + NetworkStack *_stack; + nsapi_socket_t _socket; + uint32_t _timeout; + mbed::Callback _event; + mbed::Callback _callback; + rtos::EventFlags _event_flag; + rtos::Mutex _lock; + SocketAddress _remote_peer; + uint8_t _readers; + uint8_t _writers; + volatile unsigned _pending; + bool _factory_allocated; + + // Event flags + static const int READ_FLAG = 0x1u; + static const int WRITE_FLAG = 0x2u; + static const int FINISHED_FLAG = 0x3u; +}; + +#endif // INTERNETSOCKET_H + +/** @}*/ diff --git a/features/netsocket/NetworkInterface.h b/features/netsocket/NetworkInterface.h index e164288bb0..5c9afd57da 100644 --- a/features/netsocket/NetworkInterface.h +++ b/features/netsocket/NetworkInterface.h @@ -84,7 +84,7 @@ public: * Provided MAC address is intended for info or debug purposes and * may not be provided if the underlying network interface does not * provide a MAC address - * + * * @return Null-terminated representation of the local MAC address * or null if no MAC address is available */ @@ -99,7 +99,7 @@ public: /** Get the local network mask * - * @return Null-terminated representation of the local network mask + * @return Null-terminated representation of the local network mask * or null if no network mask has been received */ virtual const char *get_netmask(); @@ -270,7 +270,7 @@ public: } protected: - friend class Socket; + friend class InternetSocket; friend class UDPSocket; friend class TCPSocket; friend class TCPServer; diff --git a/features/netsocket/NetworkStack.h b/features/netsocket/NetworkStack.h index 6e8fcb7f0e..96acf96626 100644 --- a/features/netsocket/NetworkStack.h +++ b/features/netsocket/NetworkStack.h @@ -165,7 +165,7 @@ public: virtual OnboardNetworkStack *onboardNetworkStack() { return 0; } protected: - friend class Socket; + friend class InternetSocket; friend class UDPSocket; friend class TCPSocket; friend class TCPServer; diff --git a/features/netsocket/Socket.h b/features/netsocket/Socket.h index 8c74a9e60c..f2a055e829 100644 --- a/features/netsocket/Socket.h +++ b/features/netsocket/Socket.h @@ -21,93 +21,135 @@ #define SOCKET_H #include "netsocket/SocketAddress.h" -#include "netsocket/NetworkStack.h" -#include "rtos/Mutex.h" #include "Callback.h" -#include "mbed_toolchain.h" - /** Abstract socket class */ class Socket { public: - /** Destroy a socket + /** Destroy a socket. * * Closes socket if the socket is still open */ virtual ~Socket() {} - /** Opens a socket - * - * Creates a network socket on the network stack of the given - * network interface. Not needed if stack is passed to the - * socket's constructor. - * - * @param stack Network stack as target for socket - * @return 0 on success, negative error code on failure - */ - nsapi_error_t open(NetworkStack *stack); - - template - nsapi_error_t open(S *stack) { - return open(nsapi_create_stack(stack)); - } - - /** Close the socket + /** Close the socket. * * Closes any open connection and deallocates any memory associated * with the socket. Called from destructor if socket is not closed. * * @return 0 on success, negative error code on failure */ - nsapi_error_t close(); - - /** Subscribes to an IP multicast group - * - * @param address Multicast group IP address - * @return Negative error code on failure - */ - int join_multicast_group(const SocketAddress &address); + virtual nsapi_error_t close() = 0; - /** Leave an IP multicast group + /** Connects socket to a remote address. * - * @param address Multicast group IP address - * @return Negative error code on failure + * Attempt to make connection on connection-mode protocol or set or reset + * the peer address on connectionless protocol. + * + * Also connectionless protocols use the connected address to filter + * incoming packets for recv() and recvfrom() calls. + * + * To reset the peer address, zero initialised(default constructor) SocketAddress + * object have to be in the address parameter. + * + * @param address The SocketAddress of the remote peer + * @return 0 on success, negative error code on failure */ - int leave_multicast_group(const SocketAddress &address); + virtual nsapi_error_t connect(const SocketAddress &address) = 0; - /** Bind a specific address to a socket + /** Send data on a socket * - * Binding a socket specifies the address and port on which to receive - * data. + * The socket must be connected to a remote host before send() call. + * Returns the number of bytes sent from the buffer. + * In case of connectionless socket, send data to pre-specified remote. * - * @param port Local port to bind - * @return 0 on success, negative error code on failure. + * By default, send blocks until all data is sent. If socket is set to + * non-blocking or times out, a partial amount can be written. + * NSAPI_ERROR_WOULD_BLOCK is returned if no data was written. + * + * @param data Buffer of data to send to the host + * @param size Size of the buffer in bytes + * @return Number of sent bytes on success, negative error + * code on failure. */ - nsapi_error_t bind(uint16_t port); + virtual nsapi_size_or_error_t send(const void *data, nsapi_size_t size) = 0; - /** Bind a specific address to a socket + /** Receive data from a socket. * - * Binding a socket specifies the address and port on which to receive - * data. If the IP address is zeroed, only the port is bound. + * Receive data from connected socket or in case of connectionless socket + * this is equivalent of calling recvfrom(NULL, data, size). * - * @param address Null-terminated local address to bind - * @param port Local port to bind - * @return 0 on success, negative error code on failure. + * If socket is connected, only packets coming from connected peer address + * are accepted. + * + * @note recv() is allowed write to data buffer even if error occurs. + * + * By default, recv blocks until some data is received. If socket is set to + * non-blocking or times out, NSAPI_ERROR_WOULD_BLOCK can be returned to + * indicate no data. + * + * @param data Destination buffer for data received from the host + * @param size Size of the buffer in bytes + * @return Number of received bytes on success, negative error + * code on failure. If no data is available to be received + * and the peer has performed an orderly shutdown, + * recv() returns 0. */ - nsapi_error_t bind(const char *address, uint16_t port); + virtual nsapi_size_or_error_t recv(void *data, nsapi_size_t size) = 0; - /** Bind a specific address to a socket + /** Send a message on a socket. * - * Binding a socket specifies the address and port on which to receive - * data. If the IP address is zeroed, only the port is bound. + * The sendto() function shall send a message through a connection-mode or connectionless-mode socket. + * If the socket is connectionless-mode, the message shall be sent to the address specified. + * If the socket is connection-mode, address shall be ignored. + * + * By default, sendto blocks until data is sent. If socket is set to + * non-blocking or times out, NSAPI_ERROR_WOULD_BLOCK is returned + * immediately. + * + * @param address Remote address + * @param data Buffer of data to send to the host + * @param size Size of the buffer in bytes + * @return Number of sent bytes on success, negative error + * code on failure + */ + virtual nsapi_size_or_error_t sendto(const SocketAddress &address, + const void *data, nsapi_size_t size) = 0; + + /** Receive a data from a socket + * + * Receives a data and stores the source address in address if address + * is not NULL. Returns the number of bytes written into the buffer. + * + * If socket is connected, only packets coming from connected peer address + * are accepted. + * + * @note recvfrom() is allowed write to address and data buffers even if error occurs. + * + * By default, recvfrom blocks until a datagram is received. If socket is set to + * non-blocking or times out with no data, NSAPI_ERROR_WOULD_BLOCK + * is returned. + * + * @param address Destination for the source address or NULL + * @param data Destination buffer for datagram received from the host + * @param size Size of the buffer in bytes + * @return Number of received bytes on success, negative error + * code on failure + */ + virtual nsapi_size_or_error_t recvfrom(SocketAddress *address, + void *data, nsapi_size_t size) = 0; + + /** Bind a specific address to a socket. + * + * Binding assigns a local address to a socket. * * @param address Local address to bind * @return 0 on success, negative error code on failure. */ - nsapi_error_t bind(const SocketAddress &address); - - /** Set blocking or non-blocking mode of the socket + virtual nsapi_error_t bind(const SocketAddress &address) = 0; + + /** Set blocking or non-blocking mode of the socket. * * Initially all sockets are in blocking mode. In non-blocking mode * blocking operations such as send/recv/accept return @@ -118,9 +160,9 @@ public: * * @param blocking true for blocking mode, false for non-blocking mode. */ - void set_blocking(bool blocking); - - /** Set timeout on blocking socket operations + virtual void set_blocking(bool blocking) = 0; + + /** Set timeout on blocking socket operations. * * Initially all sockets have unbounded timeouts. NSAPI_ERROR_WOULD_BLOCK * is returned if a blocking operation takes longer than the specified @@ -132,43 +174,9 @@ public: * * @param timeout Timeout in milliseconds */ - void set_timeout(int timeout); + virtual void set_timeout(int timeout) = 0; - /* Set socket options - * - * setsockopt allows an application to pass stack-specific options - * to the underlying stack using stack-specific level and option names, - * or to request generic options using levels from nsapi_socket_level_t. - * - * For unsupported options, NSAPI_ERROR_UNSUPPORTED is returned - * and the socket is unmodified. - * - * @param level Stack-specific protocol level or nsapi_socket_level_t - * @param optname Level-specific option name - * @param optval Option value - * @param optlen Length of the option value - * @return 0 on success, negative error code on failure - */ - nsapi_error_t setsockopt(int level, int optname, const void *optval, unsigned optlen); - - /* Get socket options - * - * getsockopt allows an application to retrieve stack-specific options - * from the underlying stack using stack-specific level and option names, - * or to request generic options using levels from nsapi_socket_level_t. - * - * For unsupported options, NSAPI_ERROR_UNSUPPORTED is returned - * and the socket is unmodified. - * - * @param level Stack-specific protocol level or nsapi_socket_level_t - * @param optname Level-specific option name - * @param optval Destination for option value - * @param optlen Length of the option value - * @return 0 on success, negative error code on failure - */ - nsapi_error_t getsockopt(int level, int optname, void *optval, unsigned *optlen); - - /** Register a callback on state change of the socket + /** Register a callback on state change of the socket. * * The specified callback will be called on state changes such as when * the socket can recv/send/accept successfully and on when an error @@ -184,47 +192,67 @@ public: * * @param func Function to call on state change */ - void sigio(mbed::Callback func); + virtual void sigio(mbed::Callback func) = 0; - /** Register a callback on state change of the socket + /* Set socket options. * - * @see Socket::sigio - * @deprecated - * The behaviour of Socket::attach differs from other attach functions in - * mbed OS and has been known to cause confusion. Replaced by Socket::sigio. - */ - MBED_DEPRECATED_SINCE("mbed-os-5.4", - "The behaviour of Socket::attach differs from other attach functions in " - "mbed OS and has been known to cause confusion. Replaced by Socket::sigio.") - void attach(mbed::Callback func); - - /** Register a callback on state change of the socket + * setsockopt() allows an application to pass stack-specific options + * to the underlying stack using stack-specific level and option names, + * or to request generic options using levels from nsapi_socket_level_t. * - * @see Socket::sigio - * @deprecated - * The attach function does not support cv-qualifiers. Replaced by - * attach(callback(obj, method)). + * For unsupported options, NSAPI_ERROR_UNSUPPORTED is returned + * and the socket is unmodified. + * + * @param level Stack-specific protocol level or nsapi_socket_level_t + * @param optname Level-specific option name + * @param optval Option value + * @param optlen Length of the option value + * @return 0 on success, negative error code on failure */ - template - MBED_DEPRECATED_SINCE("mbed-os-5.1", - "The attach function does not support cv-qualifiers. Replaced by " - "attach(callback(obj, method)).") - void attach(T *obj, M method) { - attach(mbed::callback(obj, method)); - } + virtual nsapi_error_t setsockopt(int level, int optname, const void *optval, unsigned optlen) = 0; -protected: - Socket(); - virtual nsapi_protocol_t get_proto() = 0; - virtual void event() = 0; - int modify_multicast_group(const SocketAddress &address, nsapi_socket_option_t socketopt); + /* Get socket options. + * + * getsockopt() allows an application to retrieve stack-specific options + * from the underlying stack using stack-specific level and option names, + * or to request generic options using levels from nsapi_socket_level_t. + * + * For unsupported options, NSAPI_ERROR_UNSUPPORTED is returned + * and the socket is unmodified. + * + * @param level Stack-specific protocol level or nsapi_socket_level_t + * @param optname Level-specific option name + * @param optval Destination for option value + * @param optlen Length of the option value + * @return 0 on success, negative error code on failure + */ + virtual nsapi_error_t getsockopt(int level, int optname, void *optval, unsigned *optlen) = 0; - NetworkStack *_stack; - nsapi_socket_t _socket; - uint32_t _timeout; - mbed::Callback _event; - mbed::Callback _callback; - rtos::Mutex _lock; + /** Accepts a connection on a socket. + * + * The server socket must be bound and set to listen for connections. + * On a new connection, returns connected network socket which user is expected to call close() + * and that deallocates the resources. Referencing a returned pointer after a close() + * call is not allowed and leads to undefined behaviour. + * + * By default, accept blocks until incomming connection occurs. If socket is set to + * non-blocking or times out, error is set to NSAPI_ERROR_WOULD_BLOCK. + * + * @param error pointer to storage of the error value or NULL + * @return pointer to a socket + */ + virtual Socket *accept(nsapi_error_t *error = NULL) = 0; + + /** Listen for incoming connections. + * + * Marks the socket as a passive socket that can be used to accept + * incoming connections. + * + * @param backlog Number of pending connections that can be queued + * simultaneously, defaults to 1 + * @return 0 on success, negative error code on failure + */ + virtual nsapi_error_t listen(int backlog = 1) = 0; }; diff --git a/features/netsocket/SocketAddress.cpp b/features/netsocket/SocketAddress.cpp index 953eb66194..4851eb16e5 100644 --- a/features/netsocket/SocketAddress.cpp +++ b/features/netsocket/SocketAddress.cpp @@ -18,8 +18,9 @@ #include "NetworkInterface.h" #include "NetworkStack.h" #include +#include #include "ip6string.h" -#include "mbed.h" + static bool ipv4_is_valid(const char *addr) @@ -105,35 +106,36 @@ static void ipv6_to_address(char *addr, const uint8_t *bytes) SocketAddress::SocketAddress(nsapi_addr_t addr, uint16_t port) { - _ip_address[0] = '\0'; + _ip_address = NULL; set_addr(addr); set_port(port); } SocketAddress::SocketAddress(const char *addr, uint16_t port) { - _ip_address[0] = '\0'; + _ip_address = NULL; set_ip_address(addr); set_port(port); } SocketAddress::SocketAddress(const void *bytes, nsapi_version_t version, uint16_t port) { - _ip_address[0] = '\0'; + _ip_address = NULL; set_ip_bytes(bytes, version); set_port(port); } SocketAddress::SocketAddress(const SocketAddress &addr) { - _ip_address[0] = '\0'; + _ip_address = NULL; set_addr(addr.get_addr()); set_port(addr.get_port()); } bool SocketAddress::set_ip_address(const char *addr) { - _ip_address[0] = '\0'; + delete[] _ip_address; + _ip_address = NULL; if (addr && ipv4_is_valid(addr)) { _addr.version = NSAPI_IPv4; @@ -165,7 +167,8 @@ void SocketAddress::set_ip_bytes(const void *bytes, nsapi_version_t version) void SocketAddress::set_addr(nsapi_addr_t addr) { - _ip_address[0] = '\0'; + delete[] _ip_address; + _ip_address = NULL; _addr = addr; } @@ -180,7 +183,8 @@ const char *SocketAddress::get_ip_address() const return NULL; } - if (!_ip_address[0]) { + if (!_ip_address) { + _ip_address = new char[NSAPI_IP_SIZE]; if (_addr.version == NSAPI_IPv4) { ipv4_to_address(_ip_address, _addr.bytes); } else if (_addr.version == NSAPI_IPv6) { @@ -234,6 +238,15 @@ SocketAddress::operator bool() const } } +SocketAddress &SocketAddress::operator=(const SocketAddress &addr) +{ + delete[] _ip_address; + _ip_address = NULL; + set_addr(addr.get_addr()); + set_port(addr.get_port()); + return *this; +} + bool operator==(const SocketAddress &a, const SocketAddress &b) { if (!a && !b) { @@ -256,7 +269,7 @@ bool operator!=(const SocketAddress &a, const SocketAddress &b) void SocketAddress::_SocketAddress(NetworkStack *iface, const char *host, uint16_t port) { - _ip_address[0] = '\0'; + _ip_address = NULL; // gethostbyname must check for literals, so can call it directly int err = iface->gethostbyname(host, this); @@ -266,3 +279,8 @@ void SocketAddress::_SocketAddress(NetworkStack *iface, const char *host, uint16 _port = 0; } } + +SocketAddress::~SocketAddress() +{ + delete[] _ip_address; +} diff --git a/features/netsocket/SocketAddress.h b/features/netsocket/SocketAddress.h index 42c5684e6f..c7cc0a097f 100644 --- a/features/netsocket/SocketAddress.h +++ b/features/netsocket/SocketAddress.h @@ -28,7 +28,7 @@ class NetworkInterface; /** SocketAddress class * - * Representation of an IP address and port pair. + * Representation of an IP address and port pair. * @addtogroup netsocket */ class SocketAddress { @@ -83,7 +83,10 @@ public: * @param addr SocketAddress to copy */ SocketAddress(const SocketAddress &addr); - + + /** Destructor */ + ~SocketAddress(); + /** Set the IP address * * @param addr Null-terminated represention of the IP address @@ -110,8 +113,11 @@ public: * @param port 16-bit port */ void set_port(uint16_t port); - - /** Get the IP address + + /** Get the human-readable IP address + * + * Allocates memory for a string and converts binary address to + * human-readable format. String is freed in the destructor. * * @return Null-terminated representation of the IP Address */ @@ -134,7 +140,7 @@ public: * @return Raw IP address */ nsapi_addr_t get_addr() const; - + /** Get the port * * @return The 16-bit port @@ -147,6 +153,12 @@ public: */ operator bool() const; + /** Copy address from another SocketAddress + * + * @param addr SocketAddress to copy + */ + SocketAddress &operator=(const SocketAddress &addr); + /** Compare two addresses for equality * * @return True if both addresses are equal @@ -162,7 +174,7 @@ public: private: void _SocketAddress(NetworkStack *iface, const char *host, uint16_t port); - mutable char _ip_address[NSAPI_IP_SIZE]; + mutable char *_ip_address; nsapi_addr_t _addr; uint16_t _port; }; diff --git a/features/netsocket/TCPServer.cpp b/features/netsocket/TCPServer.cpp index 85f3249341..4198511f67 100644 --- a/features/netsocket/TCPServer.cpp +++ b/features/netsocket/TCPServer.cpp @@ -18,7 +18,6 @@ #include "mbed.h" TCPServer::TCPServer() - : _pending(0), _accept_sem(0) { } @@ -27,36 +26,15 @@ TCPServer::~TCPServer() close(); } -nsapi_protocol_t TCPServer::get_proto() -{ - return NSAPI_TCP; -} - -nsapi_error_t TCPServer::listen(int backlog) -{ - _lock.lock(); - nsapi_error_t ret; - - if (!_socket) { - ret = NSAPI_ERROR_NO_SOCKET; - } else { - ret = _stack->socket_listen(_socket, backlog); - } - - _lock.unlock(); - return ret; -} - nsapi_error_t TCPServer::accept(TCPSocket *connection, SocketAddress *address) { - _lock.lock(); nsapi_error_t ret; while (true) { if (!_socket) { ret = NSAPI_ERROR_NO_SOCKET; break; - } + } _pending = 0; void *socket; @@ -76,19 +54,19 @@ nsapi_error_t TCPServer::accept(TCPSocket *connection, SocketAddress *address) connection->_lock.unlock(); break; - } else if (NSAPI_ERROR_WOULD_BLOCK != ret) { + } else if ((_timeout == 0) || (ret != NSAPI_ERROR_WOULD_BLOCK)) { break; } else { - int32_t count; + uint32_t flag; // Release lock before blocking so other threads // accessing this object aren't blocked _lock.unlock(); - count = _accept_sem.wait(_timeout); + flag = _event_flag.wait_any(READ_FLAG, _timeout); _lock.lock(); - if (count < 1) { - // Semaphore wait timed out so break out and return + if (flag & osFlagsError) { + // Timeout break ret = NSAPI_ERROR_WOULD_BLOCK; break; } @@ -98,16 +76,3 @@ nsapi_error_t TCPServer::accept(TCPSocket *connection, SocketAddress *address) _lock.unlock(); return ret; } - -void TCPServer::event() -{ - int32_t acount = _accept_sem.wait(0); - if (acount <= 1) { - _accept_sem.release(); - } - - _pending += 1; - if (_callback && _pending == 1) { - _callback(); - } -} diff --git a/features/netsocket/TCPServer.h b/features/netsocket/TCPServer.h index af9a4e66e3..d00c860af9 100644 --- a/features/netsocket/TCPServer.h +++ b/features/netsocket/TCPServer.h @@ -18,7 +18,7 @@ #ifndef TCPSERVER_H #define TCPSERVER_H -#include "netsocket/Socket.h" +#include "netsocket/InternetSocket.h" #include "netsocket/TCPSocket.h" #include "netsocket/NetworkStack.h" #include "netsocket/NetworkInterface.h" @@ -28,12 +28,14 @@ /** TCP socket server * @addtogroup netsocket */ -class TCPServer : public Socket { +class TCPServer : public TCPSocket { public: /** Create an uninitialized socket * * Must call open to initialize the socket on a network stack. */ + MBED_DEPRECATED_SINCE("mbed-os-5.10", + "TCPServer is deprecated, use TCPSocket") TCPServer(); /** Create a socket on a network interface @@ -44,8 +46,9 @@ public: * @param stack Network stack as target for socket */ template + MBED_DEPRECATED_SINCE("mbed-os-5.10", + "TCPServer is deprecated, use TCPSocket") TCPServer(S *stack) - : _pending(0), _accept_sem(0) { open(stack); } @@ -56,17 +59,6 @@ public: */ virtual ~TCPServer(); - /** Listen for connections on a TCP socket - * - * Marks the socket as a passive socket that can be used to accept - * incoming connections. - * - * @param backlog Number of pending connections that can be queued - * simultaneously, defaults to 1 - * @return 0 on success, negative error code on failure - */ - nsapi_error_t listen(int backlog = 1); - /** Accepts a connection on a TCP socket * * The server socket must be bound and set to listen for connections. @@ -81,15 +73,9 @@ public: * @param address Destination for the remote address or NULL * @return 0 on success, negative error code on failure */ + MBED_DEPRECATED_SINCE("mbed-os-5.10", + "TCPServer::accept() is deprecated, use Socket *Socket::accept() instead") nsapi_error_t accept(TCPSocket *connection, SocketAddress *address = NULL); - -protected: - virtual nsapi_protocol_t get_proto(); - virtual void event(); - - volatile unsigned _pending; - rtos::Semaphore _accept_sem; }; - #endif diff --git a/features/netsocket/TCPSocket.cpp b/features/netsocket/TCPSocket.cpp index 7897f8acca..00bac9a3f9 100644 --- a/features/netsocket/TCPSocket.cpp +++ b/features/netsocket/TCPSocket.cpp @@ -18,12 +18,7 @@ #include "Timer.h" #include "mbed_assert.h" -#define READ_FLAG 0x1u -#define WRITE_FLAG 0x2u - TCPSocket::TCPSocket() - : _pending(0), _event_flag(), - _read_in_progress(false), _write_in_progress(false) { } @@ -45,8 +40,8 @@ nsapi_error_t TCPSocket::connect(const SocketAddress &address) // If this assert is hit then there are two threads // performing a send at the same time which is undefined // behavior - MBED_ASSERT(!_write_in_progress); - _write_in_progress = true; + MBED_ASSERT(_writers == 0); + _writers++; bool blocking_connect_in_progress = false; @@ -77,13 +72,20 @@ nsapi_error_t TCPSocket::connect(const SocketAddress &address) } } - _write_in_progress = false; + _writers--; + if (!_socket) { + _event_flag.set(FINISHED_FLAG); + } /* Non-blocking connect gives "EISCONN" once done - convert to OK for blocking mode if we became connected during this call */ if (ret == NSAPI_ERROR_IS_CONNECTED && blocking_connect_in_progress) { ret = NSAPI_ERROR_OK; } + if (ret == NSAPI_ERROR_OK || ret == NSAPI_ERROR_IN_PROGRESS) { + _remote_peer = address; + } + _lock.unlock(); return ret; } @@ -112,8 +114,8 @@ nsapi_size_or_error_t TCPSocket::send(const void *data, nsapi_size_t size) // If this assert is hit then there are two threads // performing a send at the same time which is undefined // behavior - MBED_ASSERT(!_write_in_progress); - _write_in_progress = true; + MBED_ASSERT(_writers == 0); + _writers++; // Unlike recv, we should write the whole thing if blocking. POSIX only // allows partial as a side-effect of signal handling; it normally tries to @@ -152,7 +154,11 @@ nsapi_size_or_error_t TCPSocket::send(const void *data, nsapi_size_t size) } } - _write_in_progress = false; + _writers--; + if (!_socket) { + _event_flag.set(FINISHED_FLAG); + } + _lock.unlock(); if (ret <= 0 && ret != NSAPI_ERROR_WOULD_BLOCK) { return ret; @@ -163,6 +169,12 @@ nsapi_size_or_error_t TCPSocket::send(const void *data, nsapi_size_t size) } } +nsapi_size_or_error_t TCPSocket::sendto(const SocketAddress &address, const void *data, nsapi_size_t size) +{ + (void)address; + return send(data, size); +} + nsapi_size_or_error_t TCPSocket::recv(void *data, nsapi_size_t size) { _lock.lock(); @@ -171,8 +183,8 @@ nsapi_size_or_error_t TCPSocket::recv(void *data, nsapi_size_t size) // If this assert is hit then there are two threads // performing a recv at the same time which is undefined // behavior - MBED_ASSERT(!_read_in_progress); - _read_in_progress = true; + MBED_ASSERT(_readers == 0); + _readers++; while (true) { if (!_socket) { @@ -201,17 +213,95 @@ nsapi_size_or_error_t TCPSocket::recv(void *data, nsapi_size_t size) } } - _read_in_progress = false; + _readers--; + if (!_socket) { + _event_flag.set(FINISHED_FLAG); + } + _lock.unlock(); return ret; } -void TCPSocket::event() +nsapi_size_or_error_t TCPSocket::recvfrom(SocketAddress *address, void *data, nsapi_size_t size) { - _event_flag.set(READ_FLAG|WRITE_FLAG); - - _pending += 1; - if (_callback && _pending == 1) { - _callback(); + if (address) { + *address = _remote_peer; } + return recv(data, size); +} + +nsapi_error_t TCPSocket::listen(int backlog) +{ + _lock.lock(); + nsapi_error_t ret; + + if (!_socket) { + ret = NSAPI_ERROR_NO_SOCKET; + } else { + ret = _stack->socket_listen(_socket, backlog); + } + + _lock.unlock(); + return ret; +} + +TCPSocket *TCPSocket::accept(nsapi_error_t *error) +{ + _lock.lock(); + TCPSocket *connection = NULL; + nsapi_error_t ret; + + _readers++; + + while (true) { + if (!_socket) { + ret = NSAPI_ERROR_NO_SOCKET; + break; + } + + _pending = 0; + void *socket; + SocketAddress address; + ret = _stack->socket_accept(_socket, &socket, &address); + + if (0 == ret) { + TCPSocket *connection = new TCPSocket(); + connection->_lock.lock(); + connection->_factory_allocated = true; // Destroy automatically on close() + connection->_remote_peer = address; + connection->_stack = _stack; + connection->_socket = socket; + connection->_event = mbed::Callback(connection, &TCPSocket::event); + _stack->socket_attach(socket, &mbed::Callback::thunk, &connection->_event); + + connection->_lock.unlock(); + break; + } else if ((_timeout == 0) || (ret != NSAPI_ERROR_WOULD_BLOCK)) { + break; + } else { + uint32_t flag; + + // Release lock before blocking so other threads + // accessing this object aren't blocked + _lock.unlock(); + flag = _event_flag.wait_any(READ_FLAG, _timeout); + _lock.lock(); + + if (flag & osFlagsError) { + // Timeout break + ret = NSAPI_ERROR_WOULD_BLOCK; + break; + } + } + } + + _readers--; + if (!_socket) { + _event_flag.set(FINISHED_FLAG); + } + _lock.unlock(); + if (error) { + *error = ret; + } + return connection; } diff --git a/features/netsocket/TCPSocket.h b/features/netsocket/TCPSocket.h index 39c547d7af..49bef5b904 100644 --- a/features/netsocket/TCPSocket.h +++ b/features/netsocket/TCPSocket.h @@ -20,7 +20,7 @@ #ifndef TCPSOCKET_H #define TCPSOCKET_H -#include "netsocket/Socket.h" +#include "netsocket/InternetSocket.h" #include "netsocket/NetworkStack.h" #include "netsocket/NetworkInterface.h" #include "rtos/EventFlags.h" @@ -28,7 +28,7 @@ /** TCP socket connection */ -class TCPSocket : public Socket { +class TCPSocket : public InternetSocket { public: /** Create an uninitialized socket * @@ -45,8 +45,6 @@ public: */ template TCPSocket(S *stack) - : _pending(0), _event_flag(0), - _read_in_progress(false), _write_in_progress(false) { open(stack); } @@ -60,7 +58,7 @@ public: /** Override multicast functions to return error for TCP * */ - int join_multicast_group(const SocketAddress &address) { return NSAPI_ERROR_UNSUPPORTED; } + virtual int join_multicast_group(const SocketAddress &address) { return NSAPI_ERROR_UNSUPPORTED; } /** Connects TCP socket to a remote host * @@ -81,8 +79,8 @@ public: * @param address The SocketAddress of the remote host * @return 0 on success, negative error code on failure */ - nsapi_error_t connect(const SocketAddress &address); - + virtual nsapi_error_t connect(const SocketAddress &address); + /** Send data over a TCP socket * * The socket must be connected to a remote host. Returns the number of @@ -97,8 +95,8 @@ public: * @return Number of sent bytes on success, negative error * code on failure */ - nsapi_size_or_error_t send(const void *data, nsapi_size_t size); - + virtual nsapi_size_or_error_t send(const void *data, nsapi_size_t size); + /** Receive data over a TCP socket * * The socket must be connected to a remote host. Returns the number of @@ -115,18 +113,72 @@ public: * and the peer has performed an orderly shutdown, * recv() returns 0. */ - nsapi_size_or_error_t recv(void *data, nsapi_size_t size); + virtual nsapi_size_or_error_t recv(void *data, nsapi_size_t size); + + /** Send data on a socket. + * + * TCP socket is connection oriented protocol, so address is ignored. + * + * By default, sendto blocks until data is sent. If socket is set to + * non-blocking or times out, NSAPI_ERROR_WOULD_BLOCK is returned + * immediately. + * + * @param address Remote address + * @param data Buffer of data to send to the host + * @param size Size of the buffer in bytes + * @return Number of sent bytes on success, negative error + * code on failure + */ + virtual nsapi_size_or_error_t sendto(const SocketAddress &address, + const void *data, nsapi_size_t size); + + /** Receive a data from a socket + * + * Receives a data and stores the source address in address if address + * is not NULL. Returns the number of bytes written into the buffer. + * + * By default, recvfrom blocks until a data is received. If socket is set to + * non-blocking or times out with no datagram, NSAPI_ERROR_WOULD_BLOCK + * is returned. + * + * @param address Destination for the source address or NULL + * @param data Destination buffer for datagram received from the host + * @param size Size of the buffer in bytes + * @return Number of received bytes on success, negative error + * code on failure + */ + virtual nsapi_size_or_error_t recvfrom(SocketAddress *address, + void *data, nsapi_size_t size); + + /** Accepts a connection on a socket. + * + * The server socket must be bound and set to listen for connections. + * On a new connection, returns connected network socket which user is expected to call close() + * and that deallocates the resources. Referencing a returned pointer after a close() + * call is not allowed and leads to undefined behaviour. + * + * By default, accept blocks until incomming connection occurs. If socket is set to + * non-blocking or times out, error is set to NSAPI_ERROR_WOULD_BLOCK. + * + * @param error pointer to storage of the error value or NULL + * @return pointer to a socket + */ + virtual TCPSocket *accept(nsapi_error_t *error = NULL); + + /** Listen for incoming connections. + * + * Marks the socket as a passive socket that can be used to accept + * incoming connections. + * + * @param backlog Number of pending connections that can be queued + * simultaneously, defaults to 1 + * @return 0 on success, negative error code on failure + */ + virtual nsapi_error_t listen(int backlog = 1); protected: friend class TCPServer; - virtual nsapi_protocol_t get_proto(); - virtual void event(); - - volatile unsigned _pending; - rtos::EventFlags _event_flag; - bool _read_in_progress; - bool _write_in_progress; }; diff --git a/features/netsocket/UDPSocket.cpp b/features/netsocket/UDPSocket.cpp index cb6fbd67ce..c7c22e8af9 100644 --- a/features/netsocket/UDPSocket.cpp +++ b/features/netsocket/UDPSocket.cpp @@ -18,12 +18,7 @@ #include "Timer.h" #include "mbed_assert.h" -#define TCP_EVENT "UDP_Events" -#define READ_FLAG 0x1u -#define WRITE_FLAG 0x2u - UDPSocket::UDPSocket() - : _pending(0), _event_flag() { } @@ -37,6 +32,11 @@ nsapi_protocol_t UDPSocket::get_proto() return NSAPI_UDP; } +nsapi_error_t UDPSocket::connect(const SocketAddress &address) +{ + _remote_peer = address; + return NSAPI_ERROR_OK; +} nsapi_size_or_error_t UDPSocket::sendto(const char *host, uint16_t port, const void *data, nsapi_size_t size) { @@ -57,6 +57,8 @@ nsapi_size_or_error_t UDPSocket::sendto(const SocketAddress &address, const void _lock.lock(); nsapi_size_or_error_t ret; + _writers++; + while (true) { if (!_socket) { ret = NSAPI_ERROR_NO_SOCKET; @@ -85,14 +87,32 @@ nsapi_size_or_error_t UDPSocket::sendto(const SocketAddress &address, const void } } + _writers--; + if (!_socket || !_writers) { + _event_flag.set(FINISHED_FLAG); + } _lock.unlock(); return ret; } +nsapi_size_or_error_t UDPSocket::send(const void *data, nsapi_size_t size) +{ + if (!_remote_peer) + return NSAPI_ERROR_NO_ADDRESS; + return sendto(_remote_peer, data, size); +} + nsapi_size_or_error_t UDPSocket::recvfrom(SocketAddress *address, void *buffer, nsapi_size_t size) { _lock.lock(); nsapi_size_or_error_t ret; + SocketAddress ignored; + + if (!address) { + address = &ignored; + } + + _readers++; while (true) { if (!_socket) { @@ -102,6 +122,13 @@ nsapi_size_or_error_t UDPSocket::recvfrom(SocketAddress *address, void *buffer, _pending = 0; nsapi_size_or_error_t recv = _stack->socket_recvfrom(_socket, address, buffer, size); + + // Filter incomming packets using connected peer address + if (recv >= 0 && _remote_peer && _remote_peer == *address) { + continue; + } + + // Non-blocking sockets always return. Blocking only returns when success or errors other than WOULD_BLOCK if ((0 == _timeout) || (NSAPI_ERROR_WOULD_BLOCK != recv)) { ret = recv; break; @@ -122,16 +149,29 @@ nsapi_size_or_error_t UDPSocket::recvfrom(SocketAddress *address, void *buffer, } } + _readers--; + if (!_socket || !_readers) { + _event_flag.set(FINISHED_FLAG); + } + _lock.unlock(); return ret; } -void UDPSocket::event() +nsapi_size_or_error_t UDPSocket::recv(void *buffer, nsapi_size_t size) { - _event_flag.set(READ_FLAG|WRITE_FLAG); - - _pending += 1; - if (_callback && _pending == 1) { - _callback(); - } + return recvfrom(NULL, buffer, size); +} + +Socket *UDPSocket::accept(nsapi_error_t *error) +{ + if (error) { + *error = NSAPI_ERROR_UNSUPPORTED; + } + return NULL; +} + +nsapi_error_t UDPSocket::listen(int) +{ + return NSAPI_ERROR_UNSUPPORTED; } diff --git a/features/netsocket/UDPSocket.h b/features/netsocket/UDPSocket.h index a4570fa80e..60db9cf637 100644 --- a/features/netsocket/UDPSocket.h +++ b/features/netsocket/UDPSocket.h @@ -20,7 +20,7 @@ #ifndef UDPSOCKET_H #define UDPSOCKET_H -#include "netsocket/Socket.h" +#include "netsocket/InternetSocket.h" #include "netsocket/NetworkStack.h" #include "netsocket/NetworkInterface.h" #include "rtos/EventFlags.h" @@ -28,7 +28,7 @@ /** UDP socket */ -class UDPSocket : public Socket { +class UDPSocket : public InternetSocket { public: /** Create an uninitialized socket * @@ -45,7 +45,6 @@ public: */ template UDPSocket(S *stack) - : _pending(0), _event_flag(0) { open(stack); } @@ -73,7 +72,7 @@ public: * @return Number of sent bytes on success, negative error * code on failure */ - nsapi_size_or_error_t sendto(const char *host, uint16_t port, + virtual nsapi_size_or_error_t sendto(const char *host, uint16_t port, const void *data, nsapi_size_t size); /** Send a packet over a UDP socket @@ -91,7 +90,7 @@ public: * @return Number of sent bytes on success, negative error * code on failure */ - nsapi_size_or_error_t sendto(const SocketAddress &address, + virtual nsapi_size_or_error_t sendto(const SocketAddress &address, const void *data, nsapi_size_t size); /** Receive a datagram over a UDP socket @@ -100,6 +99,11 @@ public: * is not NULL. Returns the number of bytes written into the buffer. If the * datagram is larger than the buffer, the excess data is silently discarded. * + * If socket is connected, only packets coming from connected peer address + * are accepted. + * + * @note recvfrom() is allowed write to address and data buffers even if error occurs. + * * By default, recvfrom blocks until a datagram is received. If socket is set to * non-blocking or times out with no datagram, NSAPI_ERROR_WOULD_BLOCK * is returned. @@ -110,15 +114,72 @@ public: * @return Number of received bytes on success, negative error * code on failure */ - nsapi_size_or_error_t recvfrom(SocketAddress *address, + virtual nsapi_size_or_error_t recvfrom(SocketAddress *address, void *data, nsapi_size_t size); + /** Set remote peer address + * + * Set the remote address for next send() call and filtering + * for incomming packets. To reset the address, zero initialised + * SocketAddress must be in the address parameter. + * + * @param address The SocketAddress of the remote host + * @return 0 on success, negative error code on failure + */ + virtual nsapi_error_t connect(const SocketAddress &address); + + /** Send a datagram to pre-specified remote. + * + * The socket must be connected to a remote host before send() call. + * Returns the number of bytes sent from the buffer. + * + * By default, send blocks until all data is sent. If socket is set to + * non-blocking or times out, a partial amount can be written. + * NSAPI_ERROR_WOULD_BLOCK is returned if no data was written. + * + * @param data Buffer of data to send to the host + * @param size Size of the buffer in bytes + * @return Number of sent bytes on success, negative error + * code on failure. + */ + virtual nsapi_size_or_error_t send(const void *data, nsapi_size_t size); + + /** Receive data from a socket. + * + * This is equivalent of calling recvfrom(NULL, data, size). + * + * If socket is connected, only packets coming from connected peer address + * are accepted. + * + * @note recv() is allowed write to data buffer even if error occurs. + * + * By default, recv blocks until some data is received. If socket is set to + * non-blocking or times out, NSAPI_ERROR_WOULD_BLOCK can be returned to + * indicate no data. + * + * @param data Destination buffer for data received from the host + * @param size Size of the buffer in bytes + * @return Number of received bytes on success, negative error + * code on failure. + */ + virtual nsapi_size_or_error_t recv(void *data, nsapi_size_t size); + + /** Not implemented for UDP + * + * @param error unused + * @return NSAPI_ERROR_UNSUPPORTED + */ + virtual Socket *accept(nsapi_error_t *error = NULL); + + /** Not implemented for UDP + * + * @param backlog unused + * @return NSAPI_ERROR_UNSUPPORTED + */ + virtual nsapi_error_t listen(int backlog = 1); + protected: virtual nsapi_protocol_t get_proto(); - virtual void event(); - - volatile unsigned _pending; - rtos::EventFlags _event_flag; };