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);