struct sockaddr_in
struct hostent
socket :: NativeSocketOptNames
Options for socket, use with setsockoptsocket :: NativeSocketPollValues
Used for the poll function of a socket, mix several Poll values to check for events on more than one type of eventsocket :: NativeSocketProtocolFamilies
Socket protocol familiesstruct sockaddr_in
struct hostent
socket $ NativeSocketPollValues
Used for the poll function of a socket, mix several Poll values to check for events on more than one type of eventsocket $ NativeSocketProtocolFamilies
Socket protocol familiescore :: union_find
union–find algorithm using an efficient disjoint-set data structuresocket :: socket_simple_server
Simple server example using a non-blockingTCPServer
# Low-level socket functionalities
module socket_c
in "C Header" `{
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <sys/socket.h>
#include <sys/types.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <netdb.h>
#include <sys/poll.h>
`}
in "C" `{
#include <fcntl.h>
#include <netinet/tcp.h>
`}
# Wrapper for the data structure used for polling on a socket
class PollFD
super FinalizableOnce
# The PollFD object
private var poll_struct: NativeSocketPollFD
# A collection of the events to be watched
var events: Array[NativeSocketPollValues]
# Create a PollFD object from NativePollFD informations
init from_poll_values(pid: Int, events: Array[NativeSocketPollValues])
do
assert events.length >= 1
var events_in_one = events[0]
for i in [1 .. events.length - 1] do
events_in_one += events[i]
end
var poll_struct = new NativeSocketPollFD(pid, events_in_one)
init(poll_struct, events)
end
# Reads the response and returns an array with the type of events that have been found
private fun check_response(response: Int): Array[NativeSocketPollValues]
do
var resp_array = new Array[NativeSocketPollValues]
for i in events do
if c_check_resp(response, i) != 0 then
resp_array.push(i)
end
end
return resp_array
end
# Checks if the poll call has returned true for a particular type of event
private fun c_check_resp(response: Int, mask: NativeSocketPollValues): Int
`{
return response & mask;
`}
redef fun finalize_once
do
poll_struct.free
end
end
# Data structure used by the poll function
private extern class NativeSocketPollFD `{ struct pollfd * `}
# File descriptor
fun fd: Int `{ return self->fd; `}
# List of events to be watched
fun events: Int `{ return self->events; `}
# List of events received by the last poll function
fun revents: Int `{ return self->revents; `}
new (pid: Int, events: NativeSocketPollValues) `{
struct pollfd *poll = malloc(sizeof(struct pollfd));
poll->fd = pid;
poll->events = events;
return poll;
`}
end
# Native C socket
extern class NativeSocket `{ int* `}
# Create a new C socket
new socket(domain: NativeSocketAddressFamilies, socketType: NativeSocketTypes, protocol: NativeSocketProtocolFamilies) `{
int ds = socket(domain, socketType, protocol);
if(ds == -1){
return NULL;
}
int *d = malloc(sizeof(int));
memcpy(d, &ds, sizeof(ds));
return d;
`}
# Free the socket
fun destroy `{ free(self); `}
# Close the socket in both read/write
fun close: Int `{ return close(*self); `}
# Get the FD related to `self`
fun descriptor: Int `{ return *self; `}
# Connect to another open socket
#
# SEE: C documentation for more details on the `connect` operation
fun connect(addrIn: NativeSocketAddrIn): Int `{
return connect(*self, (struct sockaddr*)addrIn, sizeof(*addrIn));
`}
# Write `length` bytes from `buffer`
fun write(buffer: CString, length: Int): Int `{
return write(*self, buffer, length);
`}
# Write `value` as a single byte
fun write_byte(value: Int): Int `{
unsigned char byt = (unsigned char)value;
return write(*self, &byt, 1);
`}
# Read `length` bytes into `buffer`, returns the number of bytes read
fun read(buffer: CString, length: Int): Int `{
return read(*self, buffer, length);
`}
# Sets an option for the socket
#
# Returns `true` on success.
fun setsockopt(level: NativeSocketOptLevels, option_name: NativeSocketOptNames, option_value: Int): Bool `{
int err = setsockopt(*self, level, option_name, &option_value, sizeof(int));
if(err != 0){
return 0;
}
return 1;
`}
# Bind the socket to a local address
#
# SEE: C documentation for more details on the bind operation
fun bind(addrIn: NativeSocketAddrIn): Int `{ return bind(*self, (struct sockaddr*)addrIn, sizeof(*addrIn)); `}
# Prepare for listening to incoming connections
fun listen(size: Int): Int `{ return listen(*self, size); `}
# Checks if the buffer is ready for any event specified when creating the pollfd structure
fun socket_poll(filedesc: PollFD, timeout: Int): Array[NativeSocketPollValues]
do
var result = native_poll(filedesc.poll_struct, timeout)
assert result != -1
return filedesc.check_response(result)
end
# Poll this socket with `POLLHUP|POLLERR`
#
# A return value of 0 means there is no errors.
fun poll_hup_err: Int `{
struct pollfd fd = {*self, POLLHUP|POLLERR, 0};
int res = poll(&fd, 1, 0);
return res;
`}
# Call to the poll function of the C socket
#
# Signature:
# int poll(struct pollfd fds[], nfds_t nfds, int timeout);
#
# Official documentation of the poll function:
#
# The poll() function provides applications with a mechanism for multiplexing input/output over a set of file descriptors.
# For each member of the array pointed to by fds, poll() shall examine the given file descriptor for the event(s) specified in events.
# The number of pollfd structures in the fds array is specified by nfds.
# The poll() function shall identify those file descriptors on which an application can read or write data, or on which certain events have occurred.
# The fds argument specifies the file descriptors to be examined and the events of interest for each file descriptor.
# It is a pointer to an array with one member for each open file descriptor of interest.
# The array's members are pollfd structures within which fd specifies an open file descriptor and events and revents are bitmasks constructed by
# OR'ing a combination of the pollfd flags.
private fun native_poll(filedesc: NativeSocketPollFD, timeout: Int): Int `{
int poll_return = poll(filedesc, 1, timeout);
return poll_return;
`}
private fun native_accept(addr_in: NativeSocketAddrIn): NativeSocket `{
socklen_t s = sizeof(struct sockaddr);
int socket = accept(*self, (struct sockaddr*)addr_in, &s);
if (socket == -1) return NULL;
int *ptr = malloc(sizeof(int));
*ptr = socket;
return ptr;
`}
# Accept a new connection on `self`
#
# Require the socket to be first bound and listening for connections
fun accept: nullable SocketAcceptResult
do
var addrIn = new NativeSocketAddrIn
var s = native_accept(addrIn)
if s.address_is_null then return null
return new SocketAcceptResult(s, addrIn)
end
# Set whether this socket is non blocking
fun non_blocking=(value: Bool) `{
int flags = fcntl(*self, F_GETFL, 0);
if (flags == -1) flags = 0;
if (value) {
flags = flags | O_NONBLOCK;
} else if (flags & O_NONBLOCK) {
flags = flags - O_NONBLOCK;
} else {
return;
}
fcntl(*self, F_SETFL, flags);
`}
# Send `len` bytes from `buf` to `dest_addr`
fun sendto(buf: CString, len: Int, flags: Int, dest_addr: NativeSocketAddrIn): Int `{
return sendto(*self, buf, len, flags, (struct sockaddr*)dest_addr, sizeof(struct sockaddr_in));
`}
# Receive a message into `buf` of maximum `len` bytes
fun recv(buf: CString, len: Int, flags: Int): Int `{
return recv(*self, buf, len, flags);
`}
# Receive a message into `buf` of maximum `len` bytes and store sender info into `src_addr`
fun recvfrom(buf: CString, len: Int, flags: Int, src_addr: NativeSocketAddrIn): Int `{
socklen_t srclen = sizeof(struct sockaddr_in);
return recvfrom(*self, buf, len, flags, (struct sockaddr*)src_addr, &srclen);
`}
end
# Result of a call to `NativeSocket::accept`
class SocketAcceptResult
# Opened socket
var socket: NativeSocket
# Address of the remote client
var addr_in: NativeSocketAddrIn
end
# Socket address in the Internet namespace, pointer to a `struct sockaddr_in`
extern class NativeSocketAddrIn `{ struct sockaddr_in* `}
# `NULL` pointer
new nul `{ return NULL; `}
# `malloc` a new instance
new `{
struct sockaddr_in *sai = NULL;
sai = malloc(sizeof(struct sockaddr_in));
return sai;
`}
# Set `address` and `family` from `hostent` (to use with `Sys::gethostbyname`)
fun fill_from_hostent(hostent: NativeSocketHostent) `{
self->sin_family = hostent->h_addrtype;
memcpy((char*)&self->sin_addr.s_addr,
(char*)hostent->h_addr,
hostent->h_length);
`}
# Internet address as then IPV4 numbers-and-dots notation
fun address: CString `{ return (char*)inet_ntoa(self->sin_addr); `}
# Set `address` to `INADDR_ANY`
fun address_any `{ self->sin_addr.s_addr = INADDR_ANY; `}
# Set `address` to `INADDR_BROADCAST`
fun address_broadcast `{ self->sin_addr.s_addr = INADDR_BROADCAST; `}
# Address family
fun family: NativeSocketAddressFamilies `{ return self->sin_family; `}
# Address family
fun family=(value: NativeSocketAddressFamilies) `{ self->sin_family = value; `}
# Port
fun port: Int `{ return ntohs(self->sin_port); `}
# Port
fun port=(value: Int) `{ self->sin_port = htons(value); `}
end
# Host entry information, a pointer to a `struct hostent`
extern class NativeSocketHostent `{ struct hostent* `}
private fun native_h_aliases(i: Int): CString `{
return self->h_aliases[i];
`}
# Alternative names for the host
fun h_aliases: Array[String]
do
var res = new Array[String]
loop
var ha = native_h_aliases(res.length)
if ha.address_is_null then break
res.add ha.to_s
end
return res
end
# Host IPv4 address
fun h_addr: CString `{
return (char*)inet_ntoa(*(struct in_addr*)self->h_addr);
`}
# Host address type
fun h_addrtype: Int `{ return self->h_addrtype; `}
# Length in bytes of the addresses
fun h_length: Int `{ return self->h_length; `}
# Host name
fun h_name: CString `{ return self->h_name; `}
end
# Time structure, with a microsecond resolution
extern class NativeTimeval `{ struct timeval* `}
new (seconds: Int, microseconds: Int) `{
struct timeval* tv = NULL;
tv = malloc(sizeof(struct timeval));
tv->tv_sec = seconds;
tv->tv_usec = microseconds;
return tv;
`}
# Number of seconds recorded
fun seconds: Int `{ return self->tv_sec; `}
# Number of microseconds recorded
fun microseconds: Int `{ return self->tv_usec; `}
# Destory `self`
fun destroy `{ free(self); `}
end
# Structure used to register FDs for a Select
#
# FIXME: This should not be Socket-specific
# FIXME: This is Unix-specific
extern class NativeSocketSet `{ fd_set* `}
new `{
fd_set *f = NULL;
f = malloc(sizeof(fd_set));
return f;
`}
# Add a file descriptor to the set
fun set(s: NativeSocket) `{ FD_SET(*s, self); `}
# Check if `s` is in the set
fun is_set(s: NativeSocket): Bool `{ return FD_ISSET(*s, self); `}
# Clear the set
fun zero `{ FD_ZERO(self); `}
# Remove `s` from the set
fun clear(s: NativeSocket) `{ FD_CLR(*s, self); `}
# Free the set
fun destroy `{ free(self); `}
end
# Socket observer
class NativeSocketObserver
# FIXME this implementation is broken. `reads`, `write` and `except`
# are boxed objects, passing them to a C function is illegal.
fun select(max: NativeSocket, reads: nullable NativeSocketSet, write: nullable NativeSocketSet,
except: nullable NativeSocketSet, timeout: NativeTimeval): Int `{
fd_set *rds = NULL,
*wts = NULL,
*exs = NULL;
struct timeval *tm = NULL;
if (reads != NULL) rds = (fd_set*)reads;
if (write != NULL) wts = (fd_set*)write;
if (except != NULL) exs = (fd_set*)except;
if (timeout != NULL) tm = (struct timeval*)timeout;
return select(*max, rds, wts, exs, tm);
`}
end
# Socket types
extern class NativeSocketTypes `{ int `}
# STREAM socket, used for sequential writes/reads
new sock_stream `{ return SOCK_STREAM; `}
# DGRAM socket, used for packet-oriented communication
new sock_dgram `{ return SOCK_DGRAM; `}
# RAW socket, access raw data, without it being handled by the IP stack
new sock_raw `{ return SOCK_RAW; `}
# SEQPACKET, packet-oriented communication with guarantees in packet order
new sock_seqpacket `{ return SOCK_SEQPACKET; `}
end
# Socket families
extern class NativeSocketAddressFamilies `{ int `}
# Unspecified
new af_unspec `{ return AF_UNSPEC; `}
# Local to host (pipes)
new af_unix `{ return AF_UNIX; `}
# For backward compatibility
new af_local `{ return AF_LOCAL; `}
# Internetwork: UDP, TCP, etc.
new af_inet `{ return AF_INET; `}
# IBM SNA
new af_sna `{ return AF_SNA; `}
# DECnet
new af_decnet `{ return AF_DECnet; `}
# Internal Routing Protocol
new af_route `{ return AF_ROUTE; `}
# Novell Internet Protocol
new af_ipx `{ return AF_IPX; `}
# IPv6
new af_inet6 `{ return AF_INET6; `}
# Maximum identifier for socket families
new af_max `{ return AF_MAX; `}
end
# Socket protocol families
extern class NativeSocketProtocolFamilies `{ int `}
# Unspecified
new pf_unspec `{ return PF_UNSPEC; `}
# Local socket
new pf_local `{ return PF_LOCAL; `}
# Unix socket
new pf_unix `{ return PF_UNIX; `}
# Internet (IPv4) socket
new pf_inet `{ return PF_INET; `}
# SNA (IBM) socket
new pf_sna `{ return PF_SNA; `}
# DECnet socket
new pf_decnet `{ return PF_DECnet; `}
# Routing tables control
new pf_route `{ return PF_ROUTE; `}
# Novell internet protocol
new pf_ipx `{ return PF_IPX; `}
# Key management protocol
new pf_key `{ return PF_KEY; `}
# Internet (IPv6) socket
new pf_inet6 `{ return PF_INET6; `}
# Maximum identifier for socket families
new pf_max `{ return PF_MAX; `}
end
# Level on which to set options
extern class NativeSocketOptLevels `{ int `}
# Dummy for IP (As defined in C)
new ip `{ return IPPROTO_IP;`}
# Control message protocol
new icmp `{ return IPPROTO_ICMP;`}
# Use TCP
new tcp `{ return IPPROTO_TCP; `}
# Socket level options
new socket `{ return SOL_SOCKET; `}
end
# Options for socket, use with setsockopt
extern class NativeSocketOptNames `{ int `}
# Enables debugging information
new debug `{ return SO_DEBUG; `}
# Authorizes the broadcasting of messages
new broadcast `{ return SO_BROADCAST; `}
# Authorizes the reuse of the local address
new reuseaddr `{ return SO_REUSEADDR; `}
# Authorizes the use of keep-alive packets in a connection
new keepalive `{ return SO_KEEPALIVE; `}
# Disable the Nagle algorithm and send data as soon as possible, in smaller packets
new tcp_nodelay `{ return TCP_NODELAY; `}
end
# Used for the poll function of a socket, mix several Poll values to check for events on more than one type of event
extern class NativeSocketPollValues `{ int `}
# Data other than high-priority data may be read without blocking.
new pollin `{ return POLLIN; `}
# Normal data may be read without blocking.
new pollrdnorm `{ return POLLRDNORM; `}
# Priority data may be read without blocking.
new pollrdband `{ return POLLRDBAND; `}
# High-priority data may be read without blocking.
new pollpri `{ return POLLPRI; `}
# Normal data may be written without blocking.
new pollout `{ return POLLOUT; `}
# Equivalent to POLLOUT
new pollwrnorm `{ return POLLWRNORM; `}
# Priority data may be written.
new pollwrband `{ return POLLWRBAND; `}
# An error has occurred on the device or stream.
#
# This flag is only valid in the revents bitmask; it shall be ignored in the events member.
new pollerr `{ return POLLERR; `}
# The device has been disconnected.
#
# This event and POLLOUT are mutually-exclusive; a stream can never be
# writable if a hangup has occurred. However, this event and POLLIN,
# POLLRDNORM, POLLRDBAND, or POLLPRI are not mutually-exclusive.
#
# This flag is only valid in the revents bitmask; it shall be ignored in the events member.
new pollhup `{ return POLLHUP; `}
# The specified fd value is invalid.
#
# This flag is only valid in the revents member; it shall ignored in the events member.
new pollnval `{ return POLLNVAL; `}
# Combines two NativeSocketPollValues
private fun +(other: NativeSocketPollValues): NativeSocketPollValues `{
return self | other;
`}
end
redef class Sys
# Get network host entry
fun gethostbyname(name: CString): NativeSocketHostent `{
return gethostbyname(name);
`}
# Last error raised by `gethostbyname`
fun h_errno: HErrno `{ return h_errno; `}
end
# Error code of `Sys::h_errno`
extern class HErrno `{ int `}
# The specified host is unknown
fun host_not_found: Bool `{ return self == HOST_NOT_FOUND; `}
# The requested name is valid but does not have an IP address
#
# Same as `no_data`.
fun no_address: Bool `{ return self == NO_ADDRESS; `}
# The requested name is valid byt does not have an IP address
#
# Same as `no_address`.
fun no_data: Bool `{ return self == NO_DATA; `}
# A nonrecoverable name server error occurred
fun no_recovery: Bool `{ return self == NO_RECOVERY; `}
# A temporary error occurred on an authoritative name server, try again later
fun try_again: Bool `{ return self == TRY_AGAIN; `}
redef fun to_s
do
if host_not_found then
return "The specified host is unknown"
else if no_address then
return "The requested name is valid but does not have an IP address"
else if no_recovery then
return "A nonrecoverable name server error occurred"
else if try_again then
return "A temporary error occurred on an authoritative name server, try again later"
else
# This may happen if another call was made to `gethostbyname`
# before we fetch the error code.
return "Unknown error on `gethostbyname`"
end
end
end
lib/socket/socket_c.nit:17,1--626,3