Skip to content

Commit

Permalink
code review -- more doc and example cleanup
Browse files Browse the repository at this point in the history
  • Loading branch information
fadushin committed Aug 19, 2023
1 parent 93c0428 commit 704db5f
Show file tree
Hide file tree
Showing 5 changed files with 58 additions and 58 deletions.
63 changes: 27 additions & 36 deletions doc/src/programmers-guide.md
Original file line number Diff line number Diff line change
Expand Up @@ -1573,6 +1573,8 @@ For more information about the `gen_tcp` client interface, consults the AtomVM A

AtomVM supports a subset of the OTP [`socket`](https://www.erlang.org/doc/man/socket.html) interface, giving users more fine-grained control in socket programming.

The OTP socket APIs are relatively new (they were introduced in OTP 22 and have seen revisions in OTP 24). These APIs broadly mirror the [BSD Sockets](https://en.wikipedia.org/wiki/Berkeley_sockets) API, and should be familiar to most programmers who have had to work with low-level operating system networking interfaces. AtomVM supports a strict subset of the OTP APIs. Future versions of AtomVM may add additional coverage of these APIs.

The following types are relevant to this interface and are referenced in the remainder of this section:

-type domain() :: inet.
Expand All @@ -1587,6 +1589,7 @@ The following types are relevant to this interface and are referenced in the rem
}.
-type in_addr() :: {0..255, 0..255, 0..255, 0..255}.
-type port_number() :: 0..65535.
-type socket_option() :: {socket, reuseaddr} | {socket, linger}.

Create a socket using the `socket:open/3` function, providing a domain, type, and protocol. Currently, AtomVM supports the `inet` domain, `stream` and `dgram` types, and `tcp` and `udp` protocols.

Expand Down Expand Up @@ -1627,24 +1630,6 @@ This function will block the current execution context (i.e., Erlang process) un

> Note. Many applications will spawn processes to listen for socket connections, so that the main execution context of your application is not blocked.
#### Sending and Receiving Data

Once you have a connected socket, you can send and receive data on that socket using the `socket:send/2` and `socket:recv/1` functions. Like the `socket:accept/1` function, these functions will block until data is sent to a connected peer (or until the data is written to operating system buffers) or received from a connected peer.

For example,

%% erlang
case socket:recv(ConnectedSocket) of
{ok, Data} ->
{ok, _Rest} = socket:send(ConnectedSocket, Data);
{error, closed} ->
io:format("Connection closed.~n");
{error, Reason} ->
io:format("An error occurred waiting on a connected socket: ~p~n", [Reason])
end.

> Note. The `socket:recv/1` function will block the current process until a packet has arrived or until the local or remote socket has been closed, or some other error occurs.
### Client-side TCP Socket Programming

To program using sockets on the client side, you can connect an opened socket to an address and port number using the `socket:connect/2` function, supplying a map that specifies the address and port number.
Expand All @@ -1654,36 +1639,44 @@ This map may contain the following entries:
| Key | Type | Default | Description |
|-----|------|---------|-------------|
| `family` | `inet` | | The address family. (Currently, only `inet` is supported) |
| `addr` | `addr()` | | The address to which to bind. The `loopback` value will bind the socket to the loopback interface on the device. |
| `addr` | `sockaddr()` | | The address to which to bind. The `loopback` value will bind the socket to the loopback interface on the device. |
| `port` | `port_num()` | | The port to which to bind the socket. |

%% erlang
ok = socket:connect(Socket, #{family => inet, addr => loopback, port => 44404})

#### Sending and Receiving Data
### Sending and Receiving Data

Once you have a connected socket, you can send and receive data on that socket using the `socket:send/2` and `socket:recv/1` functions.
Once you have a connected socket (either via `socket:connect/2` or `socket:accept/1`), you can send and receive data on that socket using the `socket:send/2` and `socket:recv/1` functions. Like the `socket:accept/1` function, these functions will block until data is sent to a connected peer (or until the data is written to operating system buffers) or received from a connected peer.

For example:
The `socket:send/2` function can take a binary blob of data or an io-list, containing binary data.

For example, a process that receives data and echos it back to the connected peer might be implemented as follows:

%% erlang
case socket:send(ConnectedSocket, <<"ping">>) of
{ok, _Rest} ->
case socket:recv(ConnectedSocket) of
{ok, Data} ->
loop(ConnectedSocket);
case socket:recv(ConnectedSocket) of
{ok, Data} ->
case socket:send(ConnectedSocket, Data) of
ok ->
io:format("All data was sent~n");
{ok, Rest} ->
io:format("Some data was sent. Remaining: ~p~n", [Rest]);
{error, Reason} ->
io:format("Error on recv: ~p~n", [Reason])
io:format("An error occurred sending data: ~p~n", [Reason])
end;
{error, closed} ->
io:format("Connection closed.~n");
{error, Reason} ->
io:format("An error occurred sending data through a connected socket: ~p~n", [Reason])
io:format("An error occurred waiting on a connected socket: ~p~n", [Reason])
end.

The `socket:recv/1` function will block the current process until a packet has arrived or until the local or remote socket has been closed, or some other error occurs.

Note that the `socket:send/2` function may return `ok` if all of the data has been sent, or `{ok, Rest}`, where `Rest` is the remaining part of the data that was not sent to the operating system. If the supplied input to `socket:send/2` is an io-list, then the `Rest` will be a binary containing the rest of the data in the io-list.

### Getting Information about Connected Sockets

You can ontain information about connected sockets using the `socket:sockname/1` and `socket:peername/1` functions. Supply the connected socket as a parameter. The address and port are returned in a map structure
You can obtain information about connected sockets using the `socket:sockname/1` and `socket:peername/1` functions. Supply the connected socket as a parameter. The address and port are returned in a map structure

For example:

Expand Down Expand Up @@ -1757,12 +1750,10 @@ For example:
%% erlang
Dest = #{family => inet, addr => loopback, port => 512},
case socket:sendto(Socket, Data, Dest) of
{ok, _Rest} ->
io:format("Send packet ~p to ~p.~n", [
Data, Dest
]);
{error, closed} ->
io:format("Connection closed.~n");
ok ->
io:format("Send packet ~p to ~p.~n", [Data, Dest]);
{ok, Rest} ->
io:format("Send packet ~p to ~p. Remaining: ~p~n", [Data, Dest, Rest]);
{error, Reason} ->
io:format("An error occurred sending a packet: ~p~n", [Reason])
end
Expand Down
6 changes: 3 additions & 3 deletions examples/erlang/tcp_socket_client.erl
Original file line number Diff line number Diff line change
Expand Up @@ -28,14 +28,14 @@ start() ->
case socket:connect(Socket, #{family => inet, addr => loopback, port => 44404}) of
ok ->
loop(Socket);
Error ->
io:format("An error occurred connecting to TCP server: ~p~n", [Error])
{error, Reason} ->
io:format("An error occurred connecting to TCP server: ~p~n", [Reason])
end.

loop(ConnectedSocket) ->
io:format("Sending data...~n"),
case socket:send(ConnectedSocket, <<"ping">>) of
{ok, _Rest} ->
ok ->
io:format("Sent ping to ~p.~n", [socket:peername(ConnectedSocket)]),
case socket:recv(ConnectedSocket) of
{ok, Data} ->
Expand Down
15 changes: 12 additions & 3 deletions examples/erlang/tcp_socket_server.erl
Original file line number Diff line number Diff line change
Expand Up @@ -25,12 +25,16 @@
-spec start() -> no_return().
start() ->
{ok, ListeningSocket} = socket:open(inet, stream, tcp),

ok = socket:setopt(ListeningSocket, {socket, reuseaddr}, true),
ok = socket:setopt(ListeningSocket, {socket, linger}, #{onoff => true, linger => 0}),

ok = socket:bind(ListeningSocket, #{family => inet, addr => any, port => 44404}),
ok = socket:listen(ListeningSocket),
io:format("Listening on ~p.~n", [socket:sockname(ListeningSocket)]),

spawn(fun() -> accept(ListeningSocket) end),

timer:sleep(infinity).

accept(ListeningSocket) ->
Expand All @@ -54,10 +58,15 @@ echo(ConnectedSocket) ->
io:format("Received data ~p from ~p. Echoing back...~n", [
Data, socket:peername(ConnectedSocket)
]),
{ok, _Rest} = socket:send(ConnectedSocket, Data),
case socket:send(ConnectedSocket, Data) of
ok ->
io:format("All data was sent~n");
{ok, Rest} ->
io:format("Some data was sent. Remaining: ~p~n", [Rest]);
{error, Reason} ->
io:format("An error occurred sending data: ~p~n", [Reason])
end,
echo(ConnectedSocket);
{error, closed} ->
io:format("Client closed connection.~n");
{error, Reason} ->
io:format("An error occurred waiting on a connected socket: ~p~n", [Reason])
end.
20 changes: 11 additions & 9 deletions examples/erlang/udp_socket_client.erl
Original file line number Diff line number Diff line change
Expand Up @@ -33,14 +33,16 @@ loop(Socket) ->
io:format("Sending packet to ~p...~n", [Dest]),
Data = <<"Hello AtomVM!\n">>,
case socket:sendto(Socket, Data, Dest) of
{ok, _Rest} ->
ok ->
io:format("Send packet ~p to ~p.~n", [
Data, Dest
]),
timer:sleep(1000),
loop(Socket);
{error, closed} ->
io:format("closed connection.~n");
Error ->
io:format("An error occurred sending a packet: ~p~n", [Error])
end.
]);
{ok, Rest} ->
io:format("Send packet ~p to ~p. Remaining: ~p~n", [
Data, Dest, Rest
]);
{error, Reason} ->
io:format("An error occurred sending a packet: ~p~n", [Reason])
end,
timer:sleep(1000),
loop(Socket).
12 changes: 5 additions & 7 deletions examples/erlang/udp_socket_server.erl
Original file line number Diff line number Diff line change
Expand Up @@ -41,10 +41,8 @@ loop(Socket) ->
{ok, {Source, Data}} ->
io:format("Received packet ~p from ~p.~n", [
Data, Source
]),
loop(Socket);
{error, closed} ->
io:format("closed connection.~n");
Error ->
io:format("An error occurred receiving data on a socket: ~p~n", [Error])
end.
]);
{error, Reason} ->
io:format("An error occurred receiving data on a socket: ~p~n", [Reason])
end,
loop(Socket).

0 comments on commit 704db5f

Please sign in to comment.