232 lines
		
	
	
		
			7.0 KiB
		
	
	
	
		
			Prolog
		
	
	
	
	
	
			
		
		
	
	
			232 lines
		
	
	
		
			7.0 KiB
		
	
	
	
		
			Prolog
		
	
	
	
	
	
| /** uses SWI code
 | |
| 
 | |
| <module> 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`.
 | |
| 
 | |
|  
 | |
| */
 | |
| :- 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)).
 | |
| 
 | |
| %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", []).
 | |
| 
 | |
| /**
 | |
| @}
 | |
| */
 |