-
Notifications
You must be signed in to change notification settings - Fork 105
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Add preliminary support for the OTP socket interface
This PR adds preliminary support for the OTP socket interface, which exposes APIs for low-level socket operations, as defined in OTP 22 and later. This implementation aims to be API-compatible with the OTP implementation, though many parts of the interface have yet to be implemented. Key features of this implementation include the following: * A new socket driver has been added, which uses the BSD socket interface to implement the OTP socket driver * This driver is supported on generic UNIX and ESP32 platforms. Where possible, a common codebase is used, to minimize the cost of maintenance. * On ESP32, a separate RTOS task is run to periodically run select. Messages are sent to and from the VM via RTOS queues The following operations are supported: * socket:open/3 * socket:close/1 * socket:bind/2 * socket:listen/1,2 * socket:accept/1 * socket:sockname/1 * socket:peername/1 * socket:recv/1 * socket:recvfrom/1 * socket:send/2 * socket:sendto/3 * socket:setopt/3 * socket:connect/2 * socket:shutdown/2 Currently, the TCP and UDP protocols is supported. Additional work is needed to support other data types and protocols, as well as name resolution via the OTP net:addrinfo interface. Signed-off-by: Fred Dushin <fred@dushin.net>
- Loading branch information
Showing
27 changed files
with
2,940 additions
and
8 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,52 @@ | ||
% | ||
% This file is part of AtomVM. | ||
% | ||
% Copyright 2023 Fred Dushin <fred@dushin.net> | ||
% | ||
% Licensed under the Apache License, Version 2.0 (the "License"); | ||
% you may not use this file except in compliance with the License. | ||
% You may obtain a copy of the License at | ||
% | ||
% http://www.apache.org/licenses/LICENSE-2.0 | ||
% | ||
% Unless required by applicable law or agreed to in writing, software | ||
% distributed under the License is distributed on an "AS IS" BASIS, | ||
% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||
% See the License for the specific language governing permissions and | ||
% limitations under the License. | ||
% | ||
% SPDX-License-Identifier: Apache-2.0 OR LGPL-2.1-or-later | ||
% | ||
|
||
-module(tcp_socket_client). | ||
|
||
-export([start/0]). | ||
|
||
-spec start() -> no_return(). | ||
start() -> | ||
{ok, Socket} = socket:open(inet, stream, tcp), | ||
case socket:connect(Socket, #{family => inet, addr => loopback, port => 44404}) of | ||
ok -> | ||
loop(Socket); | ||
{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 -> | ||
io:format("Sent ping to ~p.~n", [socket:peername(ConnectedSocket)]), | ||
case socket:recv(ConnectedSocket) of | ||
{ok, Data} -> | ||
io:format("Received ~p.~n", [Data]), | ||
timer:sleep(1000), | ||
loop(ConnectedSocket); | ||
Error -> | ||
io:format("Error on recv: ~p~n", [Error]) | ||
end; | ||
{error, closed} -> | ||
io:format("Client closed connection.~n"); | ||
{error, Reason} -> | ||
io:format("An error occurred sending data through a connected socket: ~p~n", [Reason]) | ||
end. |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,72 @@ | ||
% | ||
% This file is part of AtomVM. | ||
% | ||
% Copyright 2023 Fred Dushin <fred@dushin.net> | ||
% | ||
% Licensed under the Apache License, Version 2.0 (the "License"); | ||
% you may not use this file except in compliance with the License. | ||
% You may obtain a copy of the License at | ||
% | ||
% http://www.apache.org/licenses/LICENSE-2.0 | ||
% | ||
% Unless required by applicable law or agreed to in writing, software | ||
% distributed under the License is distributed on an "AS IS" BASIS, | ||
% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||
% See the License for the specific language governing permissions and | ||
% limitations under the License. | ||
% | ||
% SPDX-License-Identifier: Apache-2.0 OR LGPL-2.1-or-later | ||
% | ||
|
||
-module(tcp_socket_server). | ||
|
||
-export([start/0]). | ||
|
||
-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) -> | ||
io:format("Waiting to accept connection...~n"), | ||
case socket:accept(ListeningSocket) of | ||
{ok, ConnectedSocket} -> | ||
io:format("Accepted connection. local: ~p peer: ~p~n", [ | ||
socket:sockname(ConnectedSocket), socket:peername(ConnectedSocket) | ||
]), | ||
spawn(fun() -> accept(ListeningSocket) end), | ||
echo(ConnectedSocket); | ||
{error, Reason} -> | ||
io:format("An error occurred accepting connection: ~p~n", [Reason]) | ||
end. | ||
|
||
-spec echo(ConnectedSocket :: socket:socket()) -> no_return(). | ||
echo(ConnectedSocket) -> | ||
io:format("Waiting to receive data...~n"), | ||
case socket:recv(ConnectedSocket) of | ||
{ok, Data} -> | ||
io:format("Received data ~p from ~p. Echoing back...~n", [ | ||
Data, socket:peername(ConnectedSocket) | ||
]), | ||
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, Reason} -> | ||
io:format("An error occurred waiting on a connected socket: ~p~n", [Reason]) | ||
end. |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,48 @@ | ||
% | ||
% This file is part of AtomVM. | ||
% | ||
% Copyright 2023 Fred Dushin <fred@dushin.net> | ||
% | ||
% Licensed under the Apache License, Version 2.0 (the "License"); | ||
% you may not use this file except in compliance with the License. | ||
% You may obtain a copy of the License at | ||
% | ||
% http://www.apache.org/licenses/LICENSE-2.0 | ||
% | ||
% Unless required by applicable law or agreed to in writing, software | ||
% distributed under the License is distributed on an "AS IS" BASIS, | ||
% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||
% See the License for the specific language governing permissions and | ||
% limitations under the License. | ||
% | ||
% SPDX-License-Identifier: Apache-2.0 OR LGPL-2.1-or-later | ||
% | ||
|
||
-module(udp_socket_client). | ||
|
||
-export([start/0]). | ||
|
||
-spec start() -> no_return(). | ||
start() -> | ||
{ok, Socket} = socket:open(inet, dgram, udp), | ||
|
||
loop(Socket). | ||
|
||
loop(Socket) -> | ||
Dest = #{family => inet, addr => loopback, port => 44405}, | ||
io:format("Sending packet to ~p...~n", [Dest]), | ||
Data = <<"Hello AtomVM!\n">>, | ||
case socket:sendto(Socket, Data, Dest) of | ||
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, | ||
timer:sleep(1000), | ||
loop(Socket). |
Oops, something went wrong.