1.1.24 Client - Berkeley Sockets APIs
This interface provides a Berkeley style sockets API. While closely matching the Berkeley standard API this interface does differ due to the architecture of the WINCS02. Only asynchronous sockets are supported; no function will block.
This API supports:
IPaddr and IPv6 sockets.
Datagram (UDP) and streaming (TCP) sockets.
TLS for TCP sockets.
Server and client operations.
Socket Event Callback
A socket event callback of type WINC_SOCKET_EVENT_CALLBACK
should be registered with the socket API by calling WDRV_WINC_SocketRegisterEventCallback
.
Creating Sockets
Sockets are created by calling the function socket
.
/* Creating IPaddr TCP socket. */ int sockfd = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
/* Creating IPv6 TLS socket. */ int sockfd = socket(AF_INET6, SOCK_STREAM, IPPROTO_TLS);
/* Creating IPaddr UDP socket. */ int sockfd = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);
Once created the WINCS02 device will need to allocate resources. Completion of the creation process will be signalled via the callback using event WINC_SOCKET_EVENT_OPEN
. Attempting to call another socket function before this has occurred will normally result in that call failing with errno
value ENOTSOCK
.
Server Listening Sockets
Once created a socket can be bound to a local endpoint to create a server listening socket. Multicast sockets are also bound to a local endpoint.
struct sockaddr_in addr;
addr.sin_family = AF_INET;
addr.sin_port = htons(80);
addr.sin_addr.s_addr = htonl(INADDR_ANY);
/* Bind IPv4 TCP socket to local port 80. */
bind(sockfd, (struct sockaddr*)&addr, sizeof(struct sockaddr_in))
struct sockaddr_in6 addr;
addr.sin_family = AF_INET;
addr.sin_port = htons(80);
memcpy(&addr.sin6_addr, &in6addr_any, sizeof(in6addr_any));
/* Bind IPv6 TCP socket to local port 80. */
bind(sockfd, (struct sockaddr*)&addr, sizeof(struct sockaddr_in))
If the socket supports TLS, the socket must be associated with a TLS context. The TLS API manages TLS contexts and creates context handles; the handle will need to be passed to the socket via the setsockopt
function after being converted to an index value by calling WDRV_WINC_TLSCtxHandleToCfgIdx
.
int tlsIndex = WDRV_WINC_TLSCtxHandleToCfgIdx(tlsHandle); setsockopt(sockfd, IPPROTO_TLS, TLS_CONF_IDX, &tlsIndex, sizeof(tlsIndex));
TCP server listening sockets will then need to be set to a listening state by calling listen
. A backlog can be specified which sets the number of simultaneous sockets supported: 0
means default (a single socket) and the maximum supported is specified by SOMAXCONN
.
listen(sockfd, 0);
Once a TCP socket has been configured for listening the event callback will receive the event WINC_SOCKET_EVENT_LISTEN
. A UDP socket will generate this event in response to the earlier bind
request.
Client Sockets
If the socket supports TLS, the socket must be associated with a TLS context. The TLS API manages TLS contexts and creates context handles; the handle will need to be passed to the socket via the setsockopt
function after being converted to an index value by calling WDRV_WINC_TLSCtxHandleToCfgIdx
.
int tlsIndex = WDRV_WINC_TLSCtxHandleToCfgIdx(tlsHandle); setsockopt(sockfd, IPPROTO_TLS, TLS_CONF_IDX, &tlsIndex, sizeof(tlsIndex));
TCP sockets will then need to be connected to a remote endpoint by calling connect
.
struct sockaddr_in addr; addr.sin_family = AF_INET; addr.sin_port = htons(80); addr.sin_addr.s_addr = htonl(0xC0A80064); /* Connect an IPv4 socket to 192.168.0.100:80. */ connect(sockfd, (struct sockaddr*)&addr, sizeof(sockaddr_in));
Multicast Sockets
Multicast sockets start like server listening sockets by being bound to a local endpoint. The socket then needs to be joined into the appropriate multicast group.
struct in_addr addr = htonl(0xE0000001U); struct ip_mreqn group; memcpy(&group.imr_multiaddr, &addr, sizeof(struct in_addr)); memcpy(&group.imr_address, &addr, sizeof(struct in_addr)); group.imr_ifindex = 0; /* Join the IPv4 multicast group 224.0.0.1. */ setsockopt(sockfd, IPPROTO_IP, IP_ADD_MEMBERSHIP, &group, sizeof(group));
struct in6_addr addr = {/* IPv6 multicast address*/}
struct ipv6_mreq group;
memcpy(&group.ipv6mr_multiaddr, &addr, sizeof(struct in6_addr));
group.ipv6mr_interface = 0;
/* Joining a IPv6 multicast. */
setsockopt(sockfd, IPPROTO_IP, IPV6_ADD_MEMBERSHIP, &group, sizeof(group));
Incoming Server Socket Connections
For TCP sockets a new incoming connection request is signalled via the callback event WINC_SOCKET_EVENT_CONNECT_REQ
. In response to this event the application should accept the connection by calling accept
.
struct sockaddr_in addr; socklen_t addrLen = sizeof(struct sockaddr_in); /* Accept an incoming IPv4 connection request. */ new_sockfd = accept(sockfd, (struct sockaddr*)&addr, &addrLen);
struct sockaddr_in6 addr; socklen_t addrLen = sizeof(struct sockaddr_in6); /* Accept an incoming IPv6 connection request. */ new_sockfd = accept(sockfd, (struct sockaddr*)&addr, &addrLen);
Client Socket Connections
When a new TCP client socket connects to its remote endpoint a WINC_SOCKET_EVENT_CONNECT
event will be sent to the event callback.
If TLS has been configured on the socket a second event, WINC_SOCKET_EVENT_TLS_CONNECT
will indicate that TLS has also connected and that data is ready to flow.
Sending Data
To send data the application can call either send
for streaming sockets (TCP/TLS) or sendto
for datagram sockets (UDP).
/* Send data via TCP socket. */
send(sockfd, dataPtr, dataLength, 0);
struct sockaddr_in addr addr.sin_family = AF_INET; addr.sin_port = htons(1000); addr.sin_addr.s_addr = htonl(0xC0A80064); /* Send data via UDP socket to 192.168.0.100:1000. */ sendto(sockfd, dataPtr, dataLength, 0, (struct sockaddr*)&addr, sizeof(struct sockaddr_in));
Receiving Data
To receive data the application can call either recv
for streaming sockets (TCP/TLS) or recvfrom
for datagram sockets (UDP).
uint8_t buffer[128]; /* Receive data from a TCP socket. */ recv(sockfd, buffer, sizeof(buffer), 0);
uint8_t buffer[128]; struct sockaddr_in addr; socklen_t fromLen = sizeof(struct sockaddr_in); /* Receive data from a UDP socket. */ recvfrom(sockfd, buffer, sizeof(buffer), 0, (struct sockaddr*)&addr, &fromLen);
Closing Sockets
To close a socket the application should use the function shutdown
.
DNS Resolution.
This API includes the function getaddrinfo
which can be used to resolve hostnames. Resolved names result in a list of struct addrinfo
structures which are dynamically created, therefore the application must call freeaddrinfo
when finished with the results.
For IPv6 AAAA record resolution a hints structure can be included.
struct addrinfo *pAddrInfo; int result; result = getaddrinfo("www.example.com", NULL, NULL, &pAddrInfo); if (0 == result) { /* Result is available. */ } else if (EAI_AGAIN == result) { /* Result is not available yet, try again. */ } else { /* An error occurred. */ } if (NULL != pAddrInfo) { /* Clean up allocation results structures. */ freeaddrinfo(pAddrInfo); }
struct addrinfo hints; struct addrinfo *pAddrInfo; int result; /* Indicate IPv6 AAAA records are required. */ memset(&hints, 0, sizeof(hints)); hints.ai_family = AF_INET6; result = getaddrinfo("www.example.com", NULL, &hints, &pAddrInfo);