Skip to content

Commit 2710c18

Browse files
cfriedtcarlescufi
authored andcommitted
samples: sockets: socketpair: sample application and docs
Support for the socketpair(2) system call was recently added for 2.3.0 . This change adds a sample application that demonstrates how it can be used. Fixes #25527 Signed-off-by: Christopher Friedt <[email protected]>
1 parent 44c1533 commit 2710c18

File tree

6 files changed

+311
-0
lines changed

6 files changed

+311
-0
lines changed
Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
# SPDX-License-Identifier: Apache-2.0
2+
3+
cmake_minimum_required(VERSION 3.13.1)
4+
5+
find_package(Zephyr HINTS $ENV{ZEPHYR_BASE})
6+
project(sockets_socketpair)
7+
8+
FILE(GLOB app_sources src/*.c)
9+
target_sources(app PRIVATE ${app_sources})
10+
11+
include(${ZEPHYR_BASE}/samples/net/common/common.cmake)
Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
#
2+
# Copyright (c) 2020 Friedt Professional Engineering Services, Inc
3+
#
4+
# SPDX-License-Identifier: Apache-2.0
5+
#
6+
socketpair_example: src/socketpair_example.c
7+
$(CXX) $^ -o $@ -lpthread
Lines changed: 101 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,101 @@
1+
.. _sockets-socketpair-sample:
2+
3+
Socketpair Example
4+
##################
5+
6+
Overview
7+
********
8+
9+
The sockets/socketpair sample application for Zephyr demonstrates a
10+
multi-threaded application communicating over pairs of unnamed,
11+
connected UNIX-domain sockets. The pairs of sockets are created with
12+
socketpair(2), as you might have guessed. Such sockets are compatible
13+
with the BSD Sockets API, and therefore the purpose of this sample
14+
is also to reinforce that it is possible to develop a sockets
15+
application portable to both POSIX and Zephyr.
16+
17+
The source code for this sample application can be found at:
18+
:zephyr_file:`samples/net/sockets/socketpair`.
19+
20+
Requirements
21+
************
22+
23+
None
24+
25+
Building and Running
26+
********************
27+
28+
Build the Zephyr version of the sockets/echo application like this:
29+
30+
.. zephyr-app-commands::
31+
:zephyr-app: samples/net/sockets/socketpair
32+
:board: <board_to_use>
33+
:goals: build
34+
:compact:
35+
36+
After the sample starts, several clients thread are spawned and each client
37+
thread sends a fixed number of messages to the server (main). Each client
38+
sends a message (it's name) to the server.
39+
40+
.. code-block:: console
41+
42+
*** Booting Zephyr OS build v2.3.0-rc1-215-g0e36f9686836 ***
43+
Alpha: socketpair: 3 <=> 4
44+
Bravo: socketpair: 5 <=> 7
45+
Charlie: socketpair: 8 <=> 9
46+
Charlie closed fd 9
47+
fd: 8: read 21 bytes
48+
fd: 8: hung up
49+
main: closed fd 8
50+
joined Charlie
51+
Alpha closed fd 4
52+
fd: 3: read 15 bytes
53+
fd: 3: hung up
54+
main: closed fd 3
55+
joined Alpha
56+
Bravo closed fd 7
57+
fd: 5: read 15 bytes
58+
fd: 5: hung up
59+
main: closed fd 5
60+
joined Bravo
61+
finished!
62+
63+
Running application on POSIX Host
64+
=================================
65+
66+
The same application source code can be built for a POSIX system, e.g.
67+
Linux.
68+
69+
To build for a host POSIX OS:
70+
71+
.. code-block:: console
72+
73+
$ make -f Makefile.posix
74+
75+
To run:
76+
77+
.. code-block:: console
78+
79+
$ ./socketpair_example
80+
Alpha: socketpair: 3 <=> 4
81+
Bravo: socketpair: 5 <=> 6
82+
Charlie: socketpair: 7 <=> 8
83+
Alpha closed fd 4
84+
fd: 3: read 15 bytes
85+
fd: 3: hung up
86+
main: closed fd 3
87+
joined Alpha
88+
fd: 5: read 15 bytes
89+
fd: 5: hung up
90+
Bravo closed fd 6
91+
main: closed fd 5
92+
joined Bravo
93+
Charlie closed fd 8
94+
fd: 7: read 21 bytes
95+
fd: 7: hung up
96+
main: closed fd 7
97+
joined Charlie
98+
finished!
99+
100+
As can be seen, the behavior of the application is approximately the same as
101+
the Zephyr version.
Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
# Provide some heap space
2+
CONFIG_HEAP_MEM_POOL_SIZE=2048
3+
4+
# Networking config
5+
CONFIG_NETWORKING=y
6+
CONFIG_NET_SOCKETS=y
7+
CONFIG_NET_SOCKETS_POSIX_NAMES=y
8+
CONFIG_NET_SOCKETPAIR=y
9+
10+
# Network driver config
11+
CONFIG_TEST_RANDOM_GENERATOR=y
12+
13+
# Use Portable threads
14+
CONFIG_PTHREAD_IPC=y
15+
CONFIG_NET_SOCKETS_POSIX_NAMES=y
Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
sample:
2+
description: BSD Sockets API TCP socketpair sample
3+
name: socket_socketpair
4+
tests:
5+
sample.net.sockets.socketpair:
6+
harness: net
7+
tags: net socket
Lines changed: 170 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,170 @@
1+
/*
2+
* Copyright (c) 2020 Friedt Professional Engineering Services, Inc
3+
*
4+
* SPDX-License-Identifier: Apache-2.0
5+
*/
6+
#if defined(__ZEPHYR__) && !(defined(CONFIG_BOARD_NATIVE_POSIX_32BIT) \
7+
|| defined(CONFIG_BOARD_NATIVE_POSIX_64BIT) \
8+
|| defined(CONFIG_SOC_SERIES_BSIM_NRFXX))
9+
10+
#include <net/socket.h>
11+
#include <posix/pthread.h>
12+
#include <sys/util.h>
13+
#include <posix/unistd.h>
14+
15+
#else
16+
17+
#include <poll.h>
18+
#include <pthread.h>
19+
#include <sys/socket.h>
20+
#include <sys/types.h>
21+
#include <unistd.h>
22+
23+
#define ARRAY_SIZE(x) (sizeof(x)/sizeof((x)[0]))
24+
25+
#endif
26+
27+
#include <errno.h>
28+
#include <stdbool.h>
29+
#include <stdio.h>
30+
#include <string.h>
31+
32+
#define NUM_SOCKETPAIRS 3
33+
#define NUM_REPITITIONS 3
34+
35+
struct ctx {
36+
int spair[2];
37+
pthread_t thread;
38+
char *name;
39+
};
40+
41+
static const char *const names[] = {
42+
"Alpha",
43+
"Bravo",
44+
"Charlie",
45+
};
46+
47+
static void hello(int fd, const char *name)
48+
{
49+
/* write(2) should be used after #25443 */
50+
send(fd, name, strlen(name), 0);
51+
}
52+
53+
static void *fun(void *arg)
54+
{
55+
struct ctx *const ctx = (struct ctx *)arg;
56+
int fd = ctx->spair[1];
57+
const char *name = ctx->name;
58+
59+
for (size_t i = 0; i < NUM_REPITITIONS; ++i) {
60+
hello(fd, name);
61+
}
62+
63+
close(fd);
64+
printf("%s closed fd %d\n", name, fd);
65+
ctx->spair[1] = -1;
66+
67+
return NULL;
68+
}
69+
70+
static int fd_to_idx(int fd, struct ctx *ctx, size_t n)
71+
{
72+
int r = -1;
73+
size_t i;
74+
75+
for (i = 0; i < n; ++i) {
76+
if (ctx[i].spair[0] == fd) {
77+
r = i;
78+
break;
79+
}
80+
}
81+
82+
return r;
83+
}
84+
85+
#ifdef __ZEPHYR__
86+
void zephyr_app_main(void)
87+
{
88+
#else
89+
int main(int argc, char *argv[])
90+
{
91+
(void) argc;
92+
(void) argv;
93+
#endif
94+
95+
int r;
96+
int fd;
97+
int idx;
98+
int poll_r;
99+
size_t i;
100+
size_t num_active;
101+
char buf[32];
102+
struct ctx ctx[NUM_SOCKETPAIRS] = {};
103+
struct pollfd fds[NUM_SOCKETPAIRS] = {};
104+
void *unused;
105+
106+
for (i = 0; i < ARRAY_SIZE(ctx); ++i) {
107+
ctx[i].name = (char *)names[i];
108+
socketpair(AF_UNIX, SOCK_STREAM, 0, ctx[i].spair);
109+
pthread_create(&ctx[i].thread, NULL, fun, &ctx[i]);
110+
printf("%s: socketpair: %d <=> %d\n",
111+
ctx[i].name, ctx[i].spair[0], ctx[i].spair[1]);
112+
}
113+
114+
/* loop until all threads are done */
115+
for (;;) {
116+
117+
/* count threads that are still running and fill in pollfds */
118+
for (i = 0, num_active = 0; i < ARRAY_SIZE(ctx); ++i) {
119+
if (ctx[i].spair[0] == -1) {
120+
continue;
121+
}
122+
fds[num_active].fd = ctx[i].spair[0];
123+
fds[num_active].events = POLLIN;
124+
fds[num_active].revents = 0;
125+
num_active++;
126+
}
127+
128+
if (num_active == 0) {
129+
/* all threads are done */
130+
break;
131+
}
132+
133+
poll_r = poll(fds, num_active, -1);
134+
135+
for (size_t i = 0; i < num_active; ++i) {
136+
137+
fd = fds[i].fd;
138+
idx = fd_to_idx(fd, ctx, ARRAY_SIZE(ctx));
139+
140+
if ((fds[i].revents & POLLIN) != 0) {
141+
142+
memset(buf, '\0', sizeof(buf));
143+
144+
/* read(2) should be used after #25443 */
145+
r = recv(fd, buf, sizeof(buf), 0);
146+
printf("fd: %d: read %d bytes\n", fd, r);
147+
}
148+
149+
if ((fds[i].revents & POLLERR) != 0) {
150+
printf("fd: %d: error\n", fd);
151+
}
152+
153+
if ((fds[i].revents & POLLHUP) != 0) {
154+
printf("fd: %d: hung up\n", fd);
155+
close(ctx[idx].spair[0]);
156+
printf("main: closed fd %d\n",
157+
ctx[idx].spair[0]);
158+
pthread_join(ctx[idx].thread, &unused);
159+
printf("joined %s\n", ctx[idx].name);
160+
ctx[idx].spair[0] = -1;
161+
}
162+
}
163+
}
164+
165+
printf("finished!\n");
166+
167+
#ifndef __ZEPHYR__
168+
return 0;
169+
#endif
170+
}

0 commit comments

Comments
 (0)