Skip to content

Commit 7d34ad5

Browse files
committed
doc: net: Add socket service API usage documentation
Add information how to use socket service API from application point of view. Signed-off-by: Jukka Rissanen <[email protected]>
1 parent 2e23dfd commit 7d34ad5

File tree

2 files changed

+220
-0
lines changed

2 files changed

+220
-0
lines changed

doc/connectivity/networking/api/apis.rst

+1
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ Network APIs
77
:maxdepth: 1
88

99
sockets.rst
10+
socket_service.rst
1011
ip_4_6.rst
1112
dns_resolve.rst
1213
net_mgmt.rst
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,219 @@
1+
.. _socket_service_interface:
2+
3+
Socket Services
4+
###############
5+
6+
.. contents::
7+
:local:
8+
:depth: 2
9+
10+
Overview
11+
********
12+
13+
The socket service API can be used to install a handler that is called if there
14+
is data received on a socket. The API helps to avoid creating a dedicated thread
15+
for each TCP or UDP service that the application provides. Instead one thread
16+
is created that serves data to multiple listening sockets which saves memory
17+
as only one thread needs to be created in the system in this case.
18+
19+
See :zephyr:code-sample:`sockets-service-echo` sample application to learn how
20+
to create a simple BSD socket based server application using the sockets service API.
21+
The source code for this sample application can be found at:
22+
:zephyr_file:`samples/net/sockets/echo_service`.
23+
24+
API Description
25+
***************
26+
27+
Socket service API is enabled using :kconfig:option:`CONFIG_NET_SOCKETS_SERVICE`
28+
config 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+
55+
Application Overview
56+
********************
57+
58+
If the socket service API is enabled, an application must create the service like
59+
this:
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(udp_service, udp_service_handler, MAX_SERVICES);
93+
94+
The application needs to create the sockets, then register them to the socket
95+
service after which the socket service thread will start to call the callback
96+
for 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+
140+
The TCP socket needs slightly different logic as we need to add any
141+
accepted 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+
192+
For any closed TCP client connection we need to remove the closed
193+
socket 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+
213+
Please see a more complete example in the ``echo_service`` sample source
214+
code at :zephyr_file:`samples/net/sockets/echo_service/src/main.c`.
215+
216+
API Reference
217+
*************
218+
219+
.. doxygengroup:: bsd_socket_service

0 commit comments

Comments
 (0)