1.. _socket_service_interface:
2
3Socket Services
4###############
5
6.. contents::
7    :local:
8    :depth: 2
9
10Overview
11********
12
13The socket service API can be used to install a handler that is called if there
14is data received on a socket. The API helps to avoid creating a dedicated thread
15for each TCP or UDP service that the application provides. Instead one thread
16is created that serves data to multiple listening sockets which saves memory
17as only one thread needs to be created in the system in this case.
18
19See :zephyr:code-sample:`sockets-service-echo` sample application to learn how
20to create a simple BSD socket based server application using the sockets service API.
21The source code for this sample application can be found at:
22:zephyr_file:`samples/net/sockets/echo_service`.
23
24API Description
25***************
26
27Socket service API is enabled using :kconfig:option:`CONFIG_NET_SOCKETS_SERVICE`
28config option and implements the following operations:
29
30* :c:macro:`NET_SOCKET_SERVICE_SYNC_DEFINE`
31
32  Define a network socket service. This socket service is created with extern
33  scope so it can be used from multiple C source files.
34
35* :c:macro:`NET_SOCKET_SERVICE_SYNC_DEFINE_STATIC`
36
37  Define a network socket service with static scope. This socket service can only
38  be used within one C source file.
39
40* :c:func:`net_socket_service_register`
41
42  Register pollable sockets for this service. User must create the sockets
43  before this call.
44
45* :c:func:`net_socket_service_unregister`
46
47  Remove pollable sockets for this service. User can close the sockets after
48  this call.
49
50* :c:type:`net_socket_service_handler_t`
51
52  User specified callback which is called when data is received on the
53  listening socket.
54
55Application Overview
56********************
57
58If the socket service API is enabled, an application must create the service like
59this:
60
61.. code-block:: c
62
63   #define MAX_BUF_LEN 1500
64   #define MAX_SERVICES 1
65
66   static void udp_service_handler(struct net_socket_service_event *pev)
67   {
68	struct pollfd *pfd = &pev->event;
69	int client = pfd->fd;
70	struct sockaddr_in6 addr;
71	socklen_t addrlen = sizeof(addr);
72
73	/* In this example we use one static buffer in order to avoid
74	 * having a large stack.
75	 */
76	static char buf[MAX_BUF_LEN];
77
78	len = recvfrom(client, buf, sizeof(buf), 0,
79		       (struct sockaddr *)&addr, &addrlen);
80	if (len <= 0) {
81		/* Error */
82		...
83		return;
84	}
85
86	/* Do something with the received data. The pev variable contains
87	 * user data that was stored in the socket service when it was
88	 * registered.
89	 */
90   }
91
92   NET_SOCKET_SERVICE_SYNC_DEFINE_STATIC(service_udp, udp_service_handler, MAX_SERVICES);
93
94The application needs to create the sockets, then register them to the socket
95service after which the socket service thread will start to call the callback
96for any incoming data.
97
98.. code-block:: c
99
100   /* Create one or multiple sockets */
101
102   struct pollfd sockfd_udp[1] = { 0 };
103   int sock, ret;
104
105   sock = socket(AF_INET6, SOCK_DGRAM, IPPROTO_UDP);
106   if (sock < 0) {
107	LOG_ERR("socket: %d", -errno);
108	return -errno;
109   }
110
111   /* Set possible socket options after creation */
112   ...
113
114   /* Then bind the socket to local address */
115   if (bind(sock, (struct sockaddr *)addr, sizeof(*addr)) < 0) {
116	LOG_ERR("bind: %d", -errno);
117	return -errno;
118   }
119
120   /* Set the polled sockets */
121   sockfd_udp[0].fd = sock;
122   sockfd_udp[0].events = POLLIN;
123
124   /* Register UDP socket to service handler */
125   ret = net_socket_service_register(&service_udp, sockfd_udp,
126				     ARRAY_SIZE(sockfd_udp), NULL);
127   if (ret < 0) {
128	LOG_ERR("Cannot register socket service handler (%d)", ret);
129	return ret;
130   }
131
132   /* Application logic happens here. When application is ready to
133    * quit, one should unregister the socket service and close the
134    * socket.
135    */
136
137   (void)net_socket_service_unregister(&service_udp);
138   close(sock);
139
140The TCP socket needs slightly different logic as we need to add any
141accepted socket to the listening socket by calling the
142:c:func:`net_socket_service_register` for the accepted socket.
143
144.. code-block:: c
145
146   struct sockaddr_in6 client_addr;
147   socklen_t client_addr_len = sizeof(client_addr);
148   struct pollfd sockfd_tcp[1] = { 0 };
149   int client;
150
151   /* TCP socket service is created similar way as the UDP one */
152   sock = socket(AF_INET6, SOCK_STREAM, IPPROTO_TCP);
153   if (sock < 0) {
154	LOG_ERR("socket: %d", -errno);
155	return -errno;
156   }
157
158   if (bind(sock, (struct sockaddr *)addr, sizeof(*addr)) < 0) {
159	LOG_ERR("bind: %d", -errno);
160	return -errno;
161   }
162
163   if (listen(sock, 5) < 0) {
164	LOG_ERR("listen: %d", -errno);
165	return -errno;
166   }
167
168   while (1) {
169	client = accept(tcp_sock, (struct sockaddr *)&client_addr,
170			&client_addr_len);
171	if (client < 0) {
172		LOG_ERR("accept: %d", -errno);
173		continue;
174	}
175
176	inet_ntop(client_addr.sin6_family, &client_addr.sin6_addr,
177		  addr_str, sizeof(addr_str));
178	LOG_INF("Connection from %s (%d)", addr_str, client);
179
180	sockfd_tcp[0].fd = client;
181	sockfd_tcp[0].events = POLLIN;
182
183	/* Register all the sockets to service handler */
184	ret = net_socket_service_register(&service_tcp, sockfd_tcp,
185					  ARRAY_SIZE(sockfd_tcp), NULL);
186	if (ret < 0) {
187		LOG_ERR("Cannot register socket service handler (%d)", ret);
188		break;
189	}
190   }
191
192For any closed TCP client connection we need to remove the closed
193socket from the polled socket list.
194
195.. code-block:: c
196
197   /* If the TCP socket is closed while reading the data in the handler,
198    * mark it as non pollable.
199    */
200   if (sockfd_tcp[0].fd == client) {
201	sockfd_tcp[0].fd = -1;
202
203	/* Update the handler so that client connection is
204	 * not monitored any more.
205	 */
206	 (void)net_socket_service_register(&service_tcp, sockfd_tcp,
207					   ARRAY_SIZE(sockfd_tcp), NULL);
208	 close(client);
209
210	 LOG_INF("Connection from %s closed", addr_str);
211   }
212
213Please see a more complete example in the ``echo_service`` sample source
214code at :zephyr_file:`samples/net/sockets/echo_service/src/main.c`.
215
216API Reference
217*************
218
219.. doxygengroup:: bsd_socket_service
220