237 lines
7.0 KiB
Prolog
237 lines
7.0 KiB
Prolog
/**
|
|
* @fileChunkSize library/sockets.yap
|
|
*/
|
|
|
|
:- module(yap_sockets,
|
|
[ ip_socket/2, % +Domain, -Socket
|
|
ip_socket/4, % +Domain, +Type, +Protocol, -Socket
|
|
socket_close/1, % +Socket
|
|
socket_bind/2, % +Socket, 'AF_INET'(+Host,+Port)
|
|
tcp_socket_connect/3, % +Socket, 'AF_INET'(+Host,+Port), -Stream
|
|
socket_listen/2, % +Socket, +Length
|
|
socket_accept/2, % +Socket, -Stream
|
|
socket_accept/3, % +Socket, -Client, -Stream
|
|
% socket_select/5, % +TermsSockets, -NewTermsStreams,
|
|
% +TimeOut, +Streams, -ReadStreams
|
|
current_host/1, % ?HostName
|
|
hostname_address/2 % ?HostName, ?HostAddress
|
|
]).
|
|
:- use_module(library(socket)).
|
|
:- use_module(library(error)).
|
|
:- use_module(library(apply)).
|
|
|
|
/** uses SWI code
|
|
|
|
@aaddtogroup SICStus compatible socket library
|
|
|
|
@ingroup builtins
|
|
|
|
YAP includes a SICStus Prolog compatible socket interface. In YAP-6.3
|
|
this uses the `clib` package to emulate the old low level interface that
|
|
provides direct access to the major socket system calls. These calls
|
|
can be used both to open a new connection in the network or connect to
|
|
a networked server. Socket connections are described as read/write
|
|
streams, and standard Input/Output built-ins can be used to write on or read
|
|
from sockets. The following calls are available:
|
|
|
|
|
|
@tbd Our implementation does not support AF_UNIX sockets.
|
|
@tbd Implement socket_select/5
|
|
@see http://www.sics.se/sicstus/docs/3.7.1/html/sicstus_28.html
|
|
*/
|
|
|
|
|
|
/** @pred current_host(? _HOSTNAME_)
|
|
|
|
Unify _HOSTNAME_ with an atom representing the fully qualified
|
|
hostname for the current host. Also succeeds if _HOSTNAME_ is bound
|
|
to the unqualified hostname.
|
|
|
|
|
|
*/
|
|
/** @pred hostname_address(? _HOSTNAME_,? _IP_ADDRESS_)
|
|
|
|
_HOSTNAME_ is an host name and _IP_ADDRESS_ its IP
|
|
address in number and dots notation.
|
|
|
|
|
|
|
|
|
|
*/
|
|
/** @pred socket_accept(+ _SOCKET_, - _CLIENT_, - _STREAM_)
|
|
|
|
|
|
Interface to system call `accept`, used for servers to wait for
|
|
connections at socket _SOCKET_. The stream descriptor _STREAM_
|
|
represents the resulting connection. If the socket belongs to the
|
|
domain `AF_INET`, _CLIENT_ unifies with an atom containing
|
|
the IP address for the client in numbers and dots notation.
|
|
|
|
|
|
*/
|
|
/** @pred socket_accept(+ _SOCKET_, - _STREAM_)
|
|
|
|
Accept a connection but do not return client information.
|
|
|
|
|
|
*/
|
|
/** @pred socket_bind(+ _SOCKET_, ? _PORT_)
|
|
|
|
|
|
|
|
Interface to system call `bind`, as used for servers: bind socket
|
|
to a port. Port information depends on the domain:
|
|
|
|
+ 'AF_UNIX'(+ _FILENAME_) (unsupported)
|
|
+ 'AF_FILE'(+ _FILENAME_)
|
|
use file name _FILENAME_ for UNIX or local sockets.
|
|
|
|
+ 'AF_INET'(? _HOST_,?PORT)
|
|
If _HOST_ is bound to an atom, bind to host _HOST_, otherwise
|
|
if unbound bind to local host ( _HOST_ remains unbound). If port
|
|
_PORT_ is bound to an integer, try to bind to the corresponding
|
|
port. If variable _PORT_ is unbound allow operating systems to
|
|
choose a port number, which is unified with _PORT_.
|
|
|
|
|
|
|
|
|
|
*/
|
|
/** @pred socket_close(+ _SOCKET_)
|
|
|
|
|
|
|
|
Close socket _SOCKET_. Note that sockets used in
|
|
`socket_connect` (that is, client sockets) should not be closed with
|
|
`socket_close`, as they will be automatically closed when the
|
|
corresponding stream is closed with close/1 or `close/2`.
|
|
|
|
|
|
*/
|
|
/** @pred socket_listen(+ _SOCKET_, + _LENGTH_)
|
|
|
|
|
|
Interface to system call `listen`, used for servers to indicate
|
|
willingness to wait for connections at socket _SOCKET_. The
|
|
integer _LENGTH_ gives the queue limit for incoming connections,
|
|
and should be limited to `5` for portable applications. The socket
|
|
must be of type `SOCK_STREAM` or `SOCK_SEQPACKET`.
|
|
|
|
|
|
*/
|
|
|
|
%socket(+@var{DOMAIN},+@var{TYPE},+@var{PROTOCOL},-@var{SOCKET})
|
|
|
|
ip_socket(Domain, 'SOCK_DGRAM', Protocol, SOCKET) :-
|
|
must_be(oneof(['AF_INET']), Domain),
|
|
must_be(oneof([0]), Protocol),
|
|
udp_socket(SOCKET),
|
|
assert(yap_socket(udp, SOCKET)).
|
|
ip_socket(Domain, 'SOCK_STREAM', Protocol, SOCKET) :-
|
|
must_be(oneof(['AF_INET']), Domain),
|
|
must_be(oneof([0]), Protocol),
|
|
tcp_socket(SOCKET),
|
|
assert(yap_socket(tcp, SOCKET)).
|
|
|
|
ip_socket(Domain, SOCK) :-
|
|
socket(Domain, 'SOCK_STREAM', 0, SOCK).
|
|
|
|
socket_close(Socket) :-
|
|
retract(yap_socket(udp, Socket)), !.
|
|
socket_close(Socket) :-
|
|
retract(yap_socket(tcp, Socket)), !,
|
|
tcp_close_socket(Socket).
|
|
|
|
socket_bind(Socket, 'AF_INET'(Host,Port)) :-
|
|
( Address = 'AF_INET'(Host, Port)
|
|
-> true
|
|
; type_error(socket_address, Address)
|
|
),
|
|
( var(Host)
|
|
-> gethostname(Host)
|
|
; true % Warning?
|
|
),
|
|
tcp_bind(Socket, Port).
|
|
|
|
tcp_socket_connect(Socket, Address, StreamPair) :-
|
|
( Address = 'AF_INET'(Host, Port)
|
|
-> true
|
|
; type_error(socket_address, Address)
|
|
),
|
|
tcp_connect(Socket, Host:Port),
|
|
tcp_open_socket(Socket, Read, Write),
|
|
stream_pair(StreamPair, Read, Write).
|
|
|
|
socket_listen(SOCKET, BACKLOG) :-
|
|
tcp_listen(SOCKET, BACKLOG).
|
|
|
|
socket_accept(Socket, Client, StreamPair) :-
|
|
tcp_accept(Socket, Socket2, Peer),
|
|
peer_to_client(Peer, Client),
|
|
tcp_open_socket(Socket2, Read, Write),
|
|
stream_pair(StreamPair, Read, Write).
|
|
|
|
/** @pred socket_buffering(+ _SOCKET_, - _MODE_, - _OLD_, + _NEW_)
|
|
|
|
|
|
Set buffering for _SOCKET_ in `read` or `write`
|
|
_MODE_. _OLD_ is unified with the previous status, and _NEW_
|
|
receives the new status which may be one of `unbuf` or
|
|
`fullbuf`.
|
|
|
|
|
|
*/
|
|
socket_buffering(STREAM, _, CUR, NEW) :-
|
|
stream_property(STREAM, buffer(Prop) ),
|
|
translate_buffer(Prop, CUR),
|
|
translate_buffer(NProp, NEW),
|
|
stream_property(STREAM, buffer(NProp) ).
|
|
|
|
translate_buffer(false, unbuf).
|
|
translate_buffer(full, fullbuf).
|
|
|
|
current_host(Host) :-
|
|
gethostname(Host).
|
|
|
|
hostname_address(Host, Address) :-
|
|
nonvar(Host), !,
|
|
tcp_host_to_address(Host, IP),
|
|
peer_to_client(IP, Address).
|
|
|
|
peer_to_client(ip(A,B,C,D), Client) :-
|
|
Parts = [A,B,C,D],
|
|
ground(Parts), !,
|
|
atomic_list_concat(Parts, '.', Client).
|
|
peer_to_client(ip(A,B,C,D), Client) :-
|
|
atomic_list_concat(Parts, '.', Client),
|
|
maplist(atom_number, Parts, Numbers),
|
|
Numbers = [A,B,C,D].
|
|
|
|
/** @pred socket_select(+ _SOCKETS_, - _NEWSTREAMS_, + _TIMEOUT_, + _STREAMS_, - _READSTREAMS_) [unsupported in YAP-6.3]
|
|
|
|
Interface to system call `select`, used for servers to wait for
|
|
connection requests or for data at sockets. The variable
|
|
_SOCKETS_ is a list of form _KEY-SOCKET_, where _KEY_ is
|
|
an user-defined identifier and _SOCKET_ is a socket descriptor. The
|
|
variable _TIMEOUT_ is either `off`, indicating execution will
|
|
wait until something is available, or of the form _SEC-USEC_, where
|
|
_SEC_ and _USEC_ give the seconds and microseconds before
|
|
socket_select/5 returns. The variable _SOCKETS_ is a list of
|
|
form _KEY-STREAM_, where _KEY_ is an user-defined identifier
|
|
and _STREAM_ is a stream descriptor
|
|
|
|
Execution of socket_select/5 unifies _READSTREAMS_ from
|
|
_STREAMS_ with readable data, and _NEWSTREAMS_ with a list of
|
|
the form _KEY-STREAM_, where _KEY_ was the key for a socket
|
|
with pending data, and _STREAM_ the stream descriptor resulting
|
|
from accepting the connection.
|
|
|
|
|
|
*/
|
|
socket_select(_,_,_,_,_) :-
|
|
format( user_error, "Unsupported in this version, please use wait_for_input/3~n", []).
|
|
|
|
/**
|
|
@}
|
|
*/
|