aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--erts/emulator/nifs/common/socket_nif.c323
-rw-r--r--erts/preloaded/ebin/socket.beambin42488 -> 42660 bytes
-rw-r--r--erts/preloaded/src/socket.erl122
-rw-r--r--lib/kernel/test/Makefile1
-rw-r--r--lib/kernel/test/socket_client.erl252
-rw-r--r--lib/kernel/test/socket_lib.erl129
-rw-r--r--lib/kernel/test/socket_server.erl237
7 files changed, 848 insertions, 216 deletions
diff --git a/erts/emulator/nifs/common/socket_nif.c b/erts/emulator/nifs/common/socket_nif.c
index 027155fc92..4efca1c72d 100644
--- a/erts/emulator/nifs/common/socket_nif.c
+++ b/erts/emulator/nifs/common/socket_nif.c
@@ -354,6 +354,7 @@ typedef union {
#define SOCKET_OPT_SOCK_RCVBUF 17
#define SOCKET_OPT_SOCK_REUSEADDR 21
#define SOCKET_OPT_SOCK_SNDBUF 27
+#define SOCKET_OPT_SOCK_TYPE 32
#define SOCKET_OPT_IP_RECVTOS 25
#define SOCKET_OPT_IP_ROUTER_ALERT 28
@@ -410,6 +411,7 @@ typedef union {
#define sock_ntohs(x) ntohs((x))
#define sock_open(domain, type, proto) \
make_noninheritable_handle(socket((domain), (type), (proto)))
+#define sock_peer(s, addr, len) getpeername((s), (addr), (len))
#define sock_recv(s,buf,len,flag) recv((s),(buf),(len),(flag))
#define sock_recvfrom(s,buf,blen,flag,addr,alen) \
recvfrom((s),(buf),(blen),(flag),(addr),(alen))
@@ -448,6 +450,7 @@ static unsigned long one_value = 1;
#define sock_name(s, addr, len) getsockname((s), (addr), (len))
#define sock_ntohs(x) ntohs((x))
#define sock_open(domain, type, proto) socket((domain), (type), (proto))
+#define sock_peer(s, addr, len) getpeername((s), (addr), (len))
#define sock_recv(s,buf,len,flag) recv((s),(buf),(len),(flag))
#define sock_recvfrom(s,buf,blen,flag,addr,alen) \
recvfrom((s),(buf),(blen),(flag),(addr),(alen))
@@ -671,6 +674,12 @@ static ERL_NIF_TERM nif_setopt(ErlNifEnv* env,
static ERL_NIF_TERM nif_getopt(ErlNifEnv* env,
int argc,
const ERL_NIF_TERM argv[]);
+static ERL_NIF_TERM nif_sockname(ErlNifEnv* env,
+ int argc,
+ const ERL_NIF_TERM argv[]);
+static ERL_NIF_TERM nif_peername(ErlNifEnv* env,
+ int argc,
+ const ERL_NIF_TERM argv[]);
static ERL_NIF_TERM nif_finalize_connection(ErlNifEnv* env,
int argc,
const ERL_NIF_TERM argv[]);
@@ -950,6 +959,10 @@ static ERL_NIF_TERM ngetopt_lvl_sock_reuseaddr(ErlNifEnv* env,
static ERL_NIF_TERM ngetopt_lvl_sock_sndbuf(ErlNifEnv* env,
SocketDescriptor* descP);
#endif
+#if defined(SO_TYPE)
+static ERL_NIF_TERM ngetopt_lvl_sock_type(ErlNifEnv* env,
+ SocketDescriptor* descP);
+#endif
static ERL_NIF_TERM ngetopt_lvl_ip(ErlNifEnv* env,
SocketDescriptor* descP,
int eOpt);
@@ -1013,6 +1026,10 @@ static ERL_NIF_TERM ngetopt_lvl_sctp_nodelay(ErlNifEnv* env,
SocketDescriptor* descP);
#endif
#endif // defined(HAVE_SCTP)
+static ERL_NIF_TERM nsockname(ErlNifEnv* env,
+ SocketDescriptor* descP);
+static ERL_NIF_TERM npeername(ErlNifEnv* env,
+ SocketDescriptor* descP);
static ERL_NIF_TERM nsetopt_str_opt(ErlNifEnv* env,
SocketDescriptor* descP,
@@ -1061,6 +1078,7 @@ static ERL_NIF_TERM recv_check_result(ErlNifEnv* env,
static ERL_NIF_TERM recvfrom_check_result(ErlNifEnv* env,
SocketDescriptor* descP,
int read,
+ int saveErrno,
ErlNifBinary* bufP,
SocketAddress* fromAddrP,
unsigned int fromAddrLen,
@@ -1369,6 +1387,7 @@ ERL_NIF_TERM esock_atom_tcp;
ERL_NIF_TERM esock_atom_true;
ERL_NIF_TERM esock_atom_udp;
ERL_NIF_TERM esock_atom_undefined;
+ERL_NIF_TERM esock_atom_unknown;
/* *** "Global" error (=reason) atoms *** */
ERL_NIF_TERM esock_atom_eagain;
@@ -1447,7 +1466,7 @@ static SocketData data;
* nif_listen(Sock, Backlog)
* nif_accept(LSock, Ref)
* nif_send(Sock, SendRef, Data, Flags)
- * nif_sendto(Sock, SendRef, Data, Flags, DstSockAddr)
+ * nif_sendto(Sock, SendRef, Data, Dest, Flags)
* nif_recv(Sock, RecvRef, Length, Flags)
* nif_recvfrom(Sock, Flags)
* nif_close(Sock)
@@ -2629,8 +2648,8 @@ ERL_NIF_TERM nsend(ErlNifEnv* env,
* Socket (ref) - Points to the socket descriptor.
* SendRef - A unique id for this (send) request.
* Data - The data to send in the form of a IOVec.
- * Flags - Send flags.
* Dest - Destination (socket) address.
+ * Flags - Send flags.
*/
static
@@ -2640,24 +2659,37 @@ ERL_NIF_TERM nif_sendto(ErlNifEnv* env,
{
SocketDescriptor* descP;
ERL_NIF_TERM sendRef;
- ErlNifBinary data;
+ ErlNifBinary sndData;
unsigned int eflags;
int flags;
ERL_NIF_TERM eSockAddr;
SocketAddress remoteAddr;
unsigned int remoteAddrLen;
char* xres;
+ ERL_NIF_TERM res;
+
+ SGDBG( ("SOCKET", "nif_sendto -> entry with argc: %d\r\n", argc) );
/* Extract arguments and perform preliminary validation */
- if ((argc != 6) ||
+ if ((argc != 5) ||
!enif_get_resource(env, argv[0], sockets, (void**) &descP) ||
- !GET_BIN(env, argv[2], &data) ||
- !GET_UINT(env, argv[3], &eflags)) {
+ !GET_BIN(env, argv[2], &sndData) ||
+ !GET_UINT(env, argv[4], &eflags)) {
return enif_make_badarg(env);
}
sendRef = argv[1];
- eSockAddr = argv[4];
+ eSockAddr = argv[3];
+
+ SSDBG( descP,
+ ("SOCKET", "nif_sendto -> args when sock = %d:"
+ "\r\n Socket: %T"
+ "\r\n sendRef: %T"
+ "\r\n size of data: %d"
+ "\r\n eSockAddr: %T"
+ "\r\n eflags: %d"
+ "\r\n",
+ descP->sock, argv[0], sendRef, sndData.size, eSockAddr, eflags) );
/* THIS TEST IS NOT CORRECT!!! */
if (!IS_OPEN(descP))
@@ -2671,7 +2703,14 @@ ERL_NIF_TERM nif_sendto(ErlNifEnv* env,
&remoteAddrLen)) != NULL)
return esock_make_error_str(env, xres);
- return nsendto(env, descP, sendRef, &data, flags, &remoteAddr, remoteAddrLen);
+ res = nsendto(env, descP, sendRef, &sndData, flags,
+ &remoteAddr, remoteAddrLen);
+
+ SGDBG( ("SOCKET", "nif_sendto -> done with result: "
+ "\r\n %T"
+ "\r\n", res) );
+
+ return res;
}
@@ -2919,6 +2958,14 @@ ERL_NIF_TERM nrecv(ErlNifEnv* env,
* RecvRef - A unique id for this (send) request.
* BufSz - Size of the buffer into which we put the received message.
* Flags - Receive flags.
+ *
+ * <KOLLA>
+ *
+ * How do we handle if the peek flag is set? We need to basically keep
+ * track of if we expect any data from the read. Regardless of the
+ * number of bytes we try to read.
+ *
+ * </KOLLA>
*/
static
@@ -2933,6 +2980,10 @@ ERL_NIF_TERM nif_recvfrom(ErlNifEnv* env,
int flags;
ERL_NIF_TERM res;
+ SGDBG( ("SOCKET", "nif_recvfrom -> entry with argc: %d\r\n", argc) );
+
+ /* Extract arguments and perform preliminary validation */
+
if ((argc != 4) ||
!enif_get_resource(env, argv[0], sockets, (void**) &descP) ||
!GET_UINT(env, argv[2], &bufSz) ||
@@ -2941,6 +2992,14 @@ ERL_NIF_TERM nif_recvfrom(ErlNifEnv* env,
}
recvRef = argv[1];
+ SSDBG( descP,
+ ("SOCKET", "nif_recvfrom -> args when sock = %d:"
+ "\r\n Socket: %T"
+ "\r\n recvRef: %T"
+ "\r\n bufSz: %d"
+ "\r\n eflags: %d"
+ "\r\n", descP->sock, argv[0], recvRef, bufSz, eflags) );
+
/* if (IS_OPEN(descP)) */
/* return esock_make_error(env, atom_enotconn); */
@@ -2982,13 +3041,20 @@ static
ERL_NIF_TERM nrecvfrom(ErlNifEnv* env,
SocketDescriptor* descP,
ERL_NIF_TERM recvRef,
- uint16_t bufSz,
+ uint16_t len,
int flags)
{
SocketAddress fromAddr;
unsigned int addrLen;
ssize_t read;
+ int save_errno;
ErlNifBinary buf;
+ int bufSz = (len ? len : descP->rBufSz);
+
+ SSDBG( descP, ("SOCKET", "nrecvfrom -> entry with"
+ "\r\n len: %d (%d)"
+ "\r\n flags: %d"
+ "\r\n", len, bufSz, flags) );
if (!descP->isReadable)
return enif_make_badarg(env);
@@ -2997,7 +3063,7 @@ ERL_NIF_TERM nrecvfrom(ErlNifEnv* env,
* Either as much as we want to read or (if zero (0)) use the "default"
* size (what has been configured).
*/
- if (!ALLOC_BIN((bufSz ? bufSz : descP->rBufSz), &buf))
+ if (!ALLOC_BIN(bufSz, &buf))
return esock_make_error(env, atom_exalloc);
/* We ignore the wrap for the moment.
@@ -3010,9 +3076,14 @@ ERL_NIF_TERM nrecvfrom(ErlNifEnv* env,
read = sock_recvfrom(descP->sock, buf.data, buf.size, flags,
&fromAddr.sa, &addrLen);
+ if (IS_SOCKET_ERROR(read))
+ save_errno = sock_errno();
+ else
+ save_errno = -1; // The value does not actually matter in this case
return recvfrom_check_result(env, descP,
read,
+ save_errno,
&buf,
&fromAddr, addrLen,
recvRef);
@@ -4683,6 +4754,12 @@ ERL_NIF_TERM ngetopt_lvl_socket(ErlNifEnv* env,
break;
#endif
+#if defined(SO_TYPE)
+ case SOCKET_OPT_SOCK_TYPE:
+ result = ngetopt_lvl_sock_type(env, descP);
+ break;
+#endif
+
default:
result = esock_make_error(env, esock_atom_einval);
break;
@@ -4792,6 +4869,51 @@ ERL_NIF_TERM ngetopt_lvl_sock_sndbuf(ErlNifEnv* env,
#endif
+#if defined(SO_TYPE)
+static
+ERL_NIF_TERM ngetopt_lvl_sock_type(ErlNifEnv* env,
+ SocketDescriptor* descP)
+{
+ ERL_NIF_TERM result;
+ int val;
+ SOCKOPTLEN_T valSz = sizeof(val);
+ int res;
+
+ res = sock_getopt(descP->sock, SOL_SOCKET, SO_TYPE, &val, &valSz);
+
+ if (res != 0) {
+ result = esock_make_error_errno(env, res);
+ } else {
+ switch (val) {
+ case SOCK_STREAM:
+ result = esock_make_ok2(env, esock_atom_stream);
+ break;
+ case SOCK_DGRAM:
+ result = esock_make_ok2(env, esock_atom_dgram);
+ break;
+#ifdef HAVE_SCTP
+ case SOCK_SEQPACKET:
+ result = esock_make_ok2(env, esock_atom_seqpacket);
+ break;
+#endif
+ case SOCK_RAW:
+ result = esock_make_ok2(env, esock_atom_raw);
+ break;
+ case SOCK_RDM:
+ result = esock_make_ok2(env, esock_atom_rdm);
+ break;
+ default:
+ result = esock_make_error(env,
+ MKT2(env, esock_atom_unknown, MKI(env, val)));
+ break;
+ }
+ }
+
+ return result;
+}
+#endif
+
+
/* ngetopt_lvl_ip - Level *IP* option(s)
*/
static
@@ -5217,6 +5339,137 @@ ERL_NIF_TERM ngetopt_int_opt(ErlNifEnv* env,
+
+/* ----------------------------------------------------------------------
+ * nif_sockname - get socket name
+ *
+ * Description:
+ * Returns the current address to which the socket is bound.
+ *
+ * Arguments:
+ * Socket (ref) - Points to the socket descriptor.
+ */
+
+static
+ERL_NIF_TERM nif_sockname(ErlNifEnv* env,
+ int argc,
+ const ERL_NIF_TERM argv[])
+{
+ SocketDescriptor* descP;
+ ERL_NIF_TERM res;
+
+ SGDBG( ("SOCKET", "nif_sockname -> entry with argc: %d\r\n", argc) );
+
+ /* Extract arguments and perform preliminary validation */
+
+ if ((argc != 1) ||
+ !enif_get_resource(env, argv[0], sockets, (void**) &descP)) {
+ return enif_make_badarg(env);
+ }
+
+ SSDBG( descP,
+ ("SOCKET", "nif_sockname -> args when sock = %d:"
+ "\r\n Socket: %T"
+ "\r\n", descP->sock, argv[0]) );
+
+ res = nsockname(env, descP);
+
+ SSDBG( descP, ("SOCKET", "nif_sockname -> done with res = %T\r\n", res) );
+
+ return res;
+}
+
+
+
+static
+ERL_NIF_TERM nsockname(ErlNifEnv* env,
+ SocketDescriptor* descP)
+{
+ SocketAddress sa;
+ SocketAddress* saP = &sa;
+ unsigned int sz = sizeof(SocketAddress);
+
+ sys_memzero((char*) saP, sz);
+ if (IS_SOCKET_ERROR(sock_name(descP->sock, (struct sockaddr*) saP, &sz))) {
+ return esock_make_error_errno(env, sock_errno());
+ } else {
+ ERL_NIF_TERM esa;
+ char* xres;
+
+ if ((xres = esock_encode_sockaddr(env, saP, sz, &esa)) != NULL)
+ return esock_make_error_str(env, xres);
+ else
+ return esock_make_ok2(env, esa);
+ }
+}
+
+
+
+/* ----------------------------------------------------------------------
+ * nif_peername - get name of the connected peer socket
+ *
+ * Description:
+ * Returns the address of the peer connected to the socket.
+ *
+ * Arguments:
+ * Socket (ref) - Points to the socket descriptor.
+ */
+
+static
+ERL_NIF_TERM nif_peername(ErlNifEnv* env,
+ int argc,
+ const ERL_NIF_TERM argv[])
+{
+ SocketDescriptor* descP;
+ ERL_NIF_TERM res;
+
+ SGDBG( ("SOCKET", "nif_peername -> entry with argc: %d\r\n", argc) );
+
+ /* Extract arguments and perform preliminary validation */
+
+ if ((argc != 1) ||
+ !enif_get_resource(env, argv[0], sockets, (void**) &descP)) {
+ return enif_make_badarg(env);
+ }
+
+ SSDBG( descP,
+ ("SOCKET", "nif_peername -> args when sock = %d:"
+ "\r\n Socket: %T"
+ "\r\n", descP->sock, argv[0]) );
+
+ res = npeername(env, descP);
+
+ SSDBG( descP, ("SOCKET", "nif_peername -> done with res = %T\r\n", res) );
+
+ return res;
+}
+
+
+
+static
+ERL_NIF_TERM npeername(ErlNifEnv* env,
+ SocketDescriptor* descP)
+{
+ SocketAddress sa;
+ SocketAddress* saP = &sa;
+ unsigned int sz = sizeof(SocketAddress);
+
+ sys_memzero((char*) saP, sz);
+ if (IS_SOCKET_ERROR(sock_peer(descP->sock, (struct sockaddr*) saP, &sz))) {
+ return esock_make_error_errno(env, sock_errno());
+ } else {
+ ERL_NIF_TERM esa;
+ char* xres;
+
+ if ((xres = esock_encode_sockaddr(env, saP, sz, &esa)) != NULL)
+ return esock_make_error_str(env, xres);
+ else
+ return esock_make_ok2(env, esa);
+ }
+}
+
+
+
/* ----------------------------------------------------------------------
* U t i l i t y F u n c t i o n s
* ----------------------------------------------------------------------
@@ -5495,6 +5748,7 @@ static
ERL_NIF_TERM recvfrom_check_result(ErlNifEnv* env,
SocketDescriptor* descP,
int read,
+ int saveErrno,
ErlNifBinary* bufP,
SocketAddress* fromAddrP,
unsigned int fromAddrLen,
@@ -5502,6 +5756,14 @@ ERL_NIF_TERM recvfrom_check_result(ErlNifEnv* env,
{
ERL_NIF_TERM data;
+ SSDBG( descP,
+ ("SOCKET", "recvfrom_check_result -> entry with"
+ "\r\n read: %d"
+ "\r\n saveErrno: %d"
+ "\r\n recvRef: %T"
+ "\r\n", read, saveErrno, recvRef) );
+
+
/* There is a special case: If the provided 'to read' value is
* zero (0). That means that we reads as much as we can, using
* the default read buffer size.
@@ -5511,12 +5773,12 @@ ERL_NIF_TERM recvfrom_check_result(ErlNifEnv* env,
/* +++ Error handling +++ */
- int save_errno = sock_errno();
-
- if (save_errno == ECONNRESET) {
+ if (saveErrno == ECONNRESET) {
/* +++ Oups - closed +++ */
+ SSDBG( descP, ("SOCKET", "recvfrom_check_result -> closed\r\n") );
+
/* <KOLLA>
* IF THE CURRENT PROCESS IS *NOT* THE CONTROLLING
* PROCESS, WE NEED TO INFORM IT!!!
@@ -5536,11 +5798,22 @@ ERL_NIF_TERM recvfrom_check_result(ErlNifEnv* env,
return esock_make_error(env, atom_closed);
- } else if ((save_errno == ERRNO_BLOCK) ||
- (save_errno == EAGAIN)) {
+ } else if ((saveErrno == ERRNO_BLOCK) ||
+ (saveErrno == EAGAIN)) {
+
+ SSDBG( descP, ("SOCKET", "recvfrom_check_result -> eagain\r\n") );
+
+ SELECT(env, descP->sock, (ERL_NIF_SELECT_READ),
+ descP, NULL, recvRef);
+
return esock_make_error(env, esock_atom_eagain);
} else {
- return esock_make_error_errno(env, save_errno);
+
+ SSDBG( descP,
+ ("SOCKET",
+ "recvfrom_check_result -> errno: %d\r\n", saveErrno) );
+
+ return esock_make_error_errno(env, saveErrno);
}
} else {
@@ -6598,8 +6871,10 @@ void dec_socket(int domain, int type, int protocol)
cnt_dec(&data.numTypeStreams, 1);
else if (type == SOCK_DGRAM)
cnt_dec(&data.numTypeDGrams, 1);
+#ifdef HAVE_SCTP
else if (type == SOCK_SEQPACKET)
cnt_dec(&data.numTypeSeqPkgs, 1);
+#endif
if (protocol == IPPROTO_IP)
cnt_dec(&data.numProtoIP, 1);
@@ -6639,8 +6914,10 @@ void inc_socket(int domain, int type, int protocol)
cnt_inc(&data.numTypeStreams, 1);
else if (type == SOCK_DGRAM)
cnt_inc(&data.numTypeDGrams, 1);
+#ifdef HAVE_SCTP
else if (type == SOCK_SEQPACKET)
cnt_inc(&data.numTypeSeqPkgs, 1);
+#endif
if (protocol == IPPROTO_IP)
cnt_inc(&data.numProtoIP, 1);
@@ -6729,9 +7006,11 @@ BOOLEAN_T etype2type(int etype, int* type)
*type = SOCK_RAW;
break;
+#ifdef HAVE_SCTP
case SOCKET_TYPE_SEQPACKET:
*type = SOCK_SEQPACKET;
break;
+#endif
default:
return FALSE;
@@ -7718,6 +7997,15 @@ void socket_down(ErlNifEnv* env,
const ErlNifPid* pid,
const ErlNifMonitor* mon)
{
+ SocketDescriptor* descP = (SocketDescriptor*) obj;
+
+ SSDBG( descP,
+ ("SOCKET", "socket_down -> entry when"
+ "\r\n sock: %d"
+ "\r\n pid: %T"
+ "\r\n mon: %T"
+ "\r\n", descP->sock, *pid, *mon) );
+
}
@@ -7751,6 +8039,8 @@ ErlNifFunc socket_funcs[] =
{"nif_shutdown", 2, nif_shutdown, 0},
{"nif_setopt", 5, nif_setopt, 0},
{"nif_getopt", 4, nif_getopt, 0},
+ {"nif_sockname", 1, nif_sockname, 0},
+ {"nif_peername", 1, nif_peername, 0},
/* Misc utility functions */
@@ -7927,6 +8217,7 @@ int on_load(ErlNifEnv* env, void** priv_data, ERL_NIF_TERM load_info)
esock_atom_true = MKA(env, "true");
esock_atom_udp = MKA(env, "udp");
esock_atom_undefined = MKA(env, "undefined");
+ esock_atom_unknown = MKA(env, "unknown");
/* Global error codes */
esock_atom_eafnosupport = MKA(env, ESOCK_STR_EAFNOSUPPORT);
diff --git a/erts/preloaded/ebin/socket.beam b/erts/preloaded/ebin/socket.beam
index 4924c43a5c..99a9f0f749 100644
--- a/erts/preloaded/ebin/socket.beam
+++ b/erts/preloaded/ebin/socket.beam
Binary files differ
diff --git a/erts/preloaded/src/socket.erl b/erts/preloaded/src/socket.erl
index bf94271073..79b8471e08 100644
--- a/erts/preloaded/src/socket.erl
+++ b/erts/preloaded/src/socket.erl
@@ -37,7 +37,7 @@
accept/1, accept/2,
send/2, send/3, send/4,
- sendto/4, sendto/5,
+ sendto/3, sendto/4, sendto/5,
%% sendmsg/4,
%% writev/4, OR SENDV? It will be strange for recv then: recvv (instead of readv)
@@ -50,7 +50,10 @@
shutdown/2,
setopt/4,
- getopt/3
+ getopt/3,
+
+ sockname/1,
+ peername/1
]).
-export_type([
@@ -204,11 +207,11 @@
reuseaddr |
reuseport |
rxq_ovfl |
- sndlowat |
- sndtimeo |
setfib |
sndbuf |
sndbufforce |
+ sndlowat |
+ sndtimeo |
timestamp |
type.
@@ -431,6 +434,7 @@
-define(SOCKET_SEND_FLAGS_DEFAULT, []).
-define(SOCKET_SEND_TIMEOUT_DEFAULT, infinity).
+-define(SOCKET_SENDTO_FLAGS_DEFAULT, []).
-define(SOCKET_SENDTO_TIMEOUT_DEFAULT, ?SOCKET_SEND_TIMEOUT_DEFAULT).
-define(SOCKET_RECV_FLAG_CMSG_CLOEXEC, 0).
@@ -462,6 +466,7 @@
-define(SOCKET_OPT_SOCK_RCVBUF, 17).
-define(SOCKET_OPT_SOCK_REUSEADDR, 21).
-define(SOCKET_OPT_SOCK_SNDBUF, 27).
+-define(SOCKET_OPT_SOCK_TYPE, 32).
-define(SOCKET_OPT_IP_RECVTOS, 25).
-define(SOCKET_OPT_IP_ROUTER_ALERT, 28).
@@ -927,40 +932,43 @@ do_send(SockRef, Data, EFlags, Timeout) ->
%% ---------------------------------------------------------------------------
%%
-sendto(Socket, Data, Flags, Dest) ->
- sendto(Socket, Data, Flags, Dest, ?SOCKET_SENDTO_TIMEOUT_DEFAULT).
+sendto(Socket, Data, Dest) ->
+ sendto(Socket, Data, Dest, ?SOCKET_SENDTO_FLAGS_DEFAULT).
+
+sendto(Socket, Data, Dest, Flags) ->
+ sendto(Socket, Data, Dest, Flags, ?SOCKET_SENDTO_TIMEOUT_DEFAULT).
--spec sendto(Socket, Data, Flags, Dest, Timeout) ->
+-spec sendto(Socket, Data, Dest, Flags, Timeout) ->
ok | {error, Reason} when
Socket :: socket(),
Data :: binary(),
- Flags :: send_flags(),
Dest :: null | sockaddr(),
+ Flags :: send_flags(),
Timeout :: timeout(),
Reason :: term().
-sendto(Socket, Data, Flags, Dest, Timeout) when is_list(Data) ->
+sendto(Socket, Data, Dest, Flags, Timeout) when is_list(Data) ->
Bin = erlang:list_to_binary(Data),
- sendto(Socket, Bin, Flags, Dest, Timeout);
-sendto(#socket{ref = SockRef}, Data, Flags, Dest, Timeout)
+ sendto(Socket, Bin, Dest, Flags, Timeout);
+sendto(#socket{ref = SockRef}, Data, Dest, Flags, Timeout)
when is_binary(Data) andalso
- is_list(Flags) andalso
(Dest =:= null) andalso
+ is_list(Flags) andalso
(is_integer(Timeout) orelse (Timeout =:= infinity)) ->
EFlags = enc_send_flags(Flags),
- do_sendto(SockRef, Data, EFlags, Dest, Timeout);
-sendto(#socket{ref = SockRef}, Data, Flags, #{family := Fam} = Dest, Timeout)
+ do_sendto(SockRef, Data, Dest, EFlags, Timeout);
+sendto(#socket{ref = SockRef}, Data, #{family := Fam} = Dest, Flags, Timeout)
when is_binary(Data) andalso
- is_list(Flags) andalso
((Fam =:= inet) orelse (Fam =:= inet6) orelse (Fam =:= local)) andalso
+ is_list(Flags) andalso
(is_integer(Timeout) orelse (Timeout =:= infinity)) ->
EFlags = enc_send_flags(Flags),
- do_sendto(SockRef, Data, EFlags, Dest, Timeout).
+ do_sendto(SockRef, Data, Dest, EFlags, Timeout).
-do_sendto(SockRef, Data, EFlags, Dest, Timeout) ->
+do_sendto(SockRef, Data, Dest, EFlags, Timeout) ->
TS = timestamp(Timeout),
SendRef = make_ref(),
- case nif_sendto(SockRef, SendRef, Data, EFlags, Dest) of
+ case nif_sendto(SockRef, SendRef, Data, Dest, EFlags) of
ok ->
%% We are done
ok;
@@ -970,10 +978,10 @@ do_sendto(SockRef, Data, EFlags, Dest, Timeout) ->
receive
{select, SockRef, SendRef, ready_output} when (Written > 0) ->
<<_:Written/binary, Rest/binary>> = Data,
- do_sendto(SockRef, Rest, EFlags, Dest,
+ do_sendto(SockRef, Rest, Dest, EFlags,
next_timeout(TS, Timeout));
{select, SockRef, SendRef, ready_output} ->
- do_sendto(SockRef, Data, EFlags, Dest,
+ do_sendto(SockRef, Data, Dest, EFlags,
next_timeout(TS, Timeout));
{nif_abort, SendRef, Reason} ->
@@ -988,7 +996,7 @@ do_sendto(SockRef, Data, EFlags, Dest, Timeout) ->
{error, eagain} ->
receive
{select, SockRef, SendRef, ready_output} ->
- do_sendto(SockRef, Data, EFlags, Dest,
+ do_sendto(SockRef, Data, Dest, EFlags,
next_timeout(TS, Timeout))
after Timeout ->
nif_cancel(SockRef, sendto, SendRef),
@@ -1135,21 +1143,10 @@ do_recv(SockRef, _OldRef, Length, EFlags, Acc, Timeout)
(is_integer(Timeout) andalso (Timeout > 0)) ->
TS = timestamp(Timeout),
RecvRef = make_ref(),
- p("do_recv -> try read with"
- "~n SockRef: ~p"
- "~n RecvRef: ~p"
- "~n Length: ~p"
- "~n EFlags: ~p"
- "~nwhen"
- "~n Timeout: ~p (~p)", [SockRef, RecvRef, Length, EFlags, Timeout, TS]),
case nif_recv(SockRef, RecvRef, Length, EFlags) of
{ok, true = _Complete, Bin} when (size(Acc) =:= 0) ->
- p("do_recv -> ok: complete (size(Acc) =:= 0)"
- "~n size(Bin): ~p", [size(Bin)]),
{ok, Bin};
{ok, true = _Complete, Bin} ->
- p("do_recv -> ok: complete"
- "~n size(Bin): ~p", [size(Bin)]),
{ok, <<Acc/binary, Bin/binary>>};
%% It depends on the amount of bytes we tried to read:
@@ -1158,16 +1155,12 @@ do_recv(SockRef, _OldRef, Length, EFlags, Acc, Timeout)
%% > 0 - We got a part of the message and we will be notified
%% when there is more to read (a select message)
{ok, false = _Complete, Bin} when (Length =:= 0) ->
- p("do_recv -> ok: not-complete (Length =:= 0)"
- "~n size(Bin): ~p", [size(Bin)]),
do_recv(SockRef, RecvRef,
Length, EFlags,
<<Acc/binary, Bin/binary>>,
next_timeout(TS, Timeout));
{ok, false = _Completed, Bin} when (size(Acc) =:= 0) ->
- p("do_recv -> ok: not-complete (size(Acc) =:= 0)"
- "~n size(Bin): ~p", [size(Bin)]),
%% We got the first chunk of it.
%% We will be notified (select message) when there
%% is more to read.
@@ -1190,8 +1183,6 @@ do_recv(SockRef, _OldRef, Length, EFlags, Acc, Timeout)
end;
{ok, false = _Completed, Bin} ->
- p("do_recv -> ok: not-complete"
- "~n size(Bin): ~p", [size(Bin)]),
%% We got a chunk of it!
NewTimeout = next_timeout(TS, Timeout),
receive
@@ -1213,17 +1204,14 @@ do_recv(SockRef, _OldRef, Length, EFlags, Acc, Timeout)
%% We return with the accumulated binary (if its non-empty)
{error, eagain} when (Length =:= 0) andalso (size(Acc) > 0) ->
- p("do_recv -> eagain (Length =:= 0)", []),
{ok, Acc};
{error, eagain} ->
- p("do_recv -> eagain", []),
%% There is nothing just now, but we will be notified when there
%% is something to read (a select message).
NewTimeout = next_timeout(TS, Timeout),
receive
{select, SockRef, RecvRef, ready_input} ->
- p("do_recv -> received select ready-input message", []),
do_recv(SockRef, RecvRef,
Length, EFlags,
Acc,
@@ -1317,16 +1305,21 @@ recvfrom(#socket{ref = SockRef}, BufSz, Flags, Timeout)
do_recvfrom(SockRef, BufSz, EFlags, Timeout) ->
TS = timestamp(Timeout),
RecvRef = make_ref(),
+ p("recvfrom -> try recvfrom"),
case nif_recvfrom(SockRef, RecvRef, BufSz, EFlags) of
{ok, {_Source, _NewData}} = OK ->
+ p("recvfrom -> ok: "
+ "~n Source: ~p", [_Source]),
OK;
{error, eagain} ->
+ p("recvfrom -> eagain - wait for select ready-input"),
%% There is nothing just now, but we will be notified when there
%% is something to read (a select message).
NewTimeout = next_timeout(TS, Timeout),
receive
{select, SockRef, RecvRef, ready_input} ->
+ p("recvfrom -> eagain - got select ready-input"),
do_recvfrom(SockRef, BufSz, EFlags,
next_timeout(TS, Timeout));
@@ -1339,7 +1332,8 @@ do_recvfrom(SockRef, BufSz, EFlags, Timeout) ->
{error, timeout}
end;
- {error, _} = ERROR ->
+ {error, _Reason} = ERROR ->
+ p("recvfrom -> error: ~p", [_Reason]),
ERROR
end.
@@ -1572,6 +1566,37 @@ getopt(#socket{info = Info, ref = SockRef}, Level, Key) ->
+%% ===========================================================================
+%%
+%% sockname - return the current address of the socket.
+%%
+%%
+
+-spec sockname(Socket) -> {ok, SockAddr} | {error, Reason} when
+ Socket :: socket(),
+ SockAddr :: sockaddr(),
+ Reason :: term().
+
+sockname(#socket{ref = SockRef}) ->
+ nif_sockname(SockRef).
+
+
+
+%% ===========================================================================
+%%
+%% peername - return the address of the peer *connected* to the socket.
+%%
+%%
+
+-spec peername(Socket) -> {ok, SockAddr} | {error, Reason} when
+ Socket :: socket(),
+ SockAddr :: sockaddr(),
+ Reason :: term().
+
+peername(#socket{ref = SockRef}) ->
+ nif_peername(SockRef).
+
+
%% ===========================================================================
%%
@@ -1970,6 +1995,8 @@ enc_sockopt_key(socket = L, sndtimeo = Opt, _Dir, _D, _T, _P) ->
not_supported({L, Opt});
enc_sockopt_key(socket = L, timestamp = Opt, _Dir, _D, _T, _P) ->
not_supported({L, Opt});
+enc_sockopt_key(socket = _L, type = _Opt, _Dir, _D, _T, _P) ->
+ ?SOCKET_OPT_SOCK_TYPE;
enc_sockopt_key(socket = L, UnknownOpt, _Dir, _D, _T, _P) ->
unknown({L, UnknownOpt});
@@ -2200,6 +2227,9 @@ tdiff(T1, T2) ->
+p(F) ->
+ p(F, []).
+
p(F, A) ->
p(get(sname), F, A).
@@ -2261,7 +2291,7 @@ nif_accept(_SRef, _Ref) ->
nif_send(_SockRef, _SendRef, _Data, _Flags) ->
erlang:error(badarg).
-nif_sendto(_SRef, _SendRef, _Data, _Flags, _DestSockAddr) ->
+nif_sendto(_SRef, _SendRef, _Data, _Dest, _Flags) ->
erlang:error(badarg).
nif_recv(_SRef, _RecvRef, _Length, _Flags) ->
@@ -2288,3 +2318,9 @@ nif_setopt(_Ref, _IsEnc, _Lev, _Key, _Val) ->
nif_getopt(_Ref, _IsEnc, _Lev, _Key) ->
erlang:error(badarg).
+nif_sockname(_Ref) ->
+ erlang:error(badarg).
+
+nif_peername(_Ref) ->
+ erlang:error(badarg).
+
diff --git a/lib/kernel/test/Makefile b/lib/kernel/test/Makefile
index 07e7922d3d..051fac25af 100644
--- a/lib/kernel/test/Makefile
+++ b/lib/kernel/test/Makefile
@@ -25,6 +25,7 @@ include $(ERL_TOP)/make/$(TARGET)/otp.mk
# ----------------------------------------------------
SOCKET_MODULES = \
+ socket_lib \
socket_server \
socket_client
diff --git a/lib/kernel/test/socket_client.erl b/lib/kernel/test/socket_client.erl
index a284777046..0b570e1f71 100644
--- a/lib/kernel/test/socket_client.erl
+++ b/lib/kernel/test/socket_client.erl
@@ -1,17 +1,34 @@
-%%%-------------------------------------------------------------------
-%%% @author Micael Karlberg <[email protected]>
-%%% @copyright (C) 2018, Micael Karlberg
-%%% @doc
-%%%
-%%% @end
-%%% Created : 27 Jun 2018 by Micael Karlberg <[email protected]>
-%%%-------------------------------------------------------------------
+%%
+%% %CopyrightBegin%
+%%
+%% Copyright Ericsson AB 2018-2018. All Rights Reserved.
+%%
+%% 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.
+%%
+%% %CopyrightEnd%
+%%
+
-module(socket_client).
--export([start/1]).
+-export([
+ start/1,
+ start_tcp/1, start_tcp/2,
+ start_udp/1, start_udp/2
+ ]).
+
+-define(LIB, socket_lib).
--define(REQ, 0).
--define(REP, 1).
+-record(client, {socket, type, dest, msg_id = 1}).
start(Port) ->
start_tcp(Port).
@@ -19,22 +36,77 @@ start(Port) ->
start_tcp(Port) ->
start(inet, stream, tcp, Port).
+start_tcp(Addr, Port) when (size(Addr) =:= 4) ->
+ start(inet, stream, tcp, Addr, Port);
+start_tcp(Addr, Port) when (size(Addr) =:= 8) ->
+ start(inet6, stream, tcp, Addr, Port).
+
+
+start_udp(Port) ->
+ start(inet, dgram, udp, Port).
+
+start_udp(Addr, Port) when (size(Addr) =:= 4) ->
+ start(inet, dgram, udp, Addr, Port);
+start_udp(Addr, Port) when (size(Addr) =:= 8) ->
+ start(inet6, dgram, udp, Addr, Port).
+
+
start(Domain, Type, Proto, Port) ->
+ start(Domain, Type, Proto, which_addr(Domain), Port).
+
+start(Domain, Type, Proto, Addr, Port) ->
+ put(sname, "starter"),
+ SA = #{family => Domain,
+ addr => Addr,
+ port => Port},
+ do_start(Domain, Type, Proto, SA).
+
+do_start(Domain, stream = Type, Proto, SA) ->
try do_init(Domain, Type, Proto) of
Sock ->
- connect(Sock, Domain, Port),
+ connect(Sock, SA),
+ i("connected: "
+ "~n From: ~p"
+ "~n To: ~p",
+ [
+ case socket:sockname(Sock) of
+ {ok, Name} -> Name;
+ {error, _} = NE -> NE
+ end,
+ case socket:peername(Sock) of
+ {ok, Name} -> Name;
+ {error, _} = PE -> PE
+ end
+ ]),
%% Give the server some time...
- p("wait some", []),
- %% sleep(5000),
+ i("wait some", []),
+ ?LIB:sleep(5000),
%% ok = socket:close(Sock),
- send_loop(Sock)
+ send_loop(#client{socket = Sock,
+ type = Type})
+ catch
+ throw:E ->
+ e("Failed initiate: "
+ "~n Error: ~p", [E])
+ end;
+do_start(Domain, dgram = Type, Proto, SA) ->
+ try do_init(Domain, Type, Proto) of
+ Sock ->
+ %% Give the server some time...
+ i("wait some", []),
+ ?LIB:sleep(5000),
+ %% ok = socket:close(Sock),
+ send_loop(#client{socket = Sock,
+ type = Type,
+ dest = SA})
catch
throw:E ->
e("Failed initiate: "
"~n Error: ~p", [E])
end.
-do_init(Domain, Type, Proto) ->
+
+do_init(Domain, stream = Type, Proto) ->
i("try (socket) open"),
Sock = case socket:open(Domain, Type, Proto) of
{ok, S} ->
@@ -48,8 +120,23 @@ do_init(Domain, Type, Proto) ->
Sock;
{error, BReason} ->
throw({bind, BReason})
+ end;
+do_init(Domain, dgram = Type, Proto) ->
+ i("try (socket) open"),
+ Sock = case socket:open(Domain, Type, Proto) of
+ {ok, S} ->
+ S;
+ {error, OReason} ->
+ throw({open, OReason})
+ end,
+ case socket:bind(Sock, any) of
+ {ok, _} ->
+ Sock;
+ {error, BReason} ->
+ throw({bind, BReason})
end.
+
which_addr(Domain) ->
Iflist = case inet:getifaddrs() of
{ok, IFL} ->
@@ -60,16 +147,11 @@ which_addr(Domain) ->
which_addr(Domain, Iflist).
-connect(Sock, Domain, Port) ->
- Addr = which_addr(Domain),
- SA = #{family => Domain,
- addr => Addr,
- port => Port},
+connect(Sock, SA) ->
i("try (socket) connect to:"
"~n ~p", [SA]),
case socket:connect(Sock, SA) of
ok ->
- i("connected"),
ok;
{error, Reason} ->
e("connect failure: "
@@ -78,22 +160,23 @@ connect(Sock, Domain, Port) ->
end.
-send_loop(Sock) ->
- send_loop(Sock, 1).
-
-send_loop(Sock, N) when (N =< 10) ->
+send_loop(#client{msg_id = N} = C) when (N =< 10) ->
i("try send request ~w", [N]),
- Req = enc_req_msg(N, "hejsan"),
- case socket:send(Sock, Req) of
+ Req = ?LIB:enc_req_msg(N, "hejsan"),
+ case send(C, Req) of
ok ->
i("request ~w sent - now try read answer", [N]),
- case socket:recv(Sock, 0) of
- {ok, Msg} ->
- i("received ~w bytes of data", [size(Msg)]),
- case dec_msg(Msg) of
+ case recv(C) of
+ {ok, {Source, Msg}} ->
+ i("received ~w bytes of data~s",
+ [size(Msg), case Source of
+ undefined -> "";
+ _ -> ?LIB:f(" from:~n ~p", [Source])
+ end]),
+ case ?LIB:dec_msg(Msg) of
{reply, N, Reply} ->
i("received reply ~w: ~p", [N, Reply]),
- send_loop(Sock, N+1)
+ send_loop(C#client{msg_id = N+1})
end;
{error, RReason} ->
e("Failed recv response for request ~w: "
@@ -105,7 +188,7 @@ send_loop(Sock, N) when (N =< 10) ->
"~n ~p", [SReason]),
exit({failed_send, SReason})
end;
-send_loop(Sock, _N) ->
+send_loop(#client{socket = Sock}) ->
i("we are done - close the socket when: "
"~n ~p", [socket:info()]),
ok = socket:close(Sock),
@@ -113,6 +196,25 @@ send_loop(Sock, _N) ->
"~n ~p", [socket:info()]).
+send(#client{socket = Sock, type = stream}, Msg) ->
+ socket:send(Sock, Msg);
+send(#client{socket = Sock, type = dgram, dest = Dest}, Msg) ->
+ %% i("try send to: "
+ %% "~n ~p", [Dest]),
+ %% ok = socket:setopt(Sock, otp, debug, true),
+ socket:sendto(Sock, Msg, Dest).
+
+recv(#client{socket = Sock, type = stream}) ->
+ case socket:recv(Sock) of
+ {ok, Msg} ->
+ {ok, {undefined, Msg}};
+ {error, _} = ERROR ->
+ ERROR
+ end;
+recv(#client{socket = Sock, type = dgram}) ->
+ socket:recvfrom(Sock).
+
+
which_addr(_Domain, []) ->
throw(no_address);
which_addr(Domain, [{Name, IFO}|_IFL]) when (Name =/= "lo") ->
@@ -130,68 +232,66 @@ which_addr2(Domain, [_|IFO]) ->
%% ---
-enc_req_msg(N, Data) ->
- enc_msg(?REQ, N, Data).
+%% enc_req_msg(N, Data) ->
+%% enc_msg(?REQ, N, Data).
-enc_rep_msg(N, Data) ->
- enc_msg(?REP, N, Data).
+%% enc_rep_msg(N, Data) ->
+%% enc_msg(?REP, N, Data).
-enc_msg(Type, N, Data) when is_list(Data) ->
- enc_msg(Type, N, list_to_binary(Data));
-enc_msg(Type, N, Data)
- when is_integer(Type) andalso is_integer(N) andalso is_binary(Data) ->
- <<Type:32/integer, N:32/integer, Data/binary>>.
+%% enc_msg(Type, N, Data) when is_list(Data) ->
+%% enc_msg(Type, N, list_to_binary(Data));
+%% enc_msg(Type, N, Data)
+%% when is_integer(Type) andalso is_integer(N) andalso is_binary(Data) ->
+%% <<Type:32/integer, N:32/integer, Data/binary>>.
-dec_msg(<<?REQ:32/integer, N:32/integer, Data/binary>>) ->
- {request, N, Data};
-dec_msg(<<?REP:32/integer, N:32/integer, Data/binary>>) ->
- {reply, N, Data}.
+%% dec_msg(<<?REQ:32/integer, N:32/integer, Data/binary>>) ->
+%% {request, N, Data};
+%% dec_msg(<<?REP:32/integer, N:32/integer, Data/binary>>) ->
+%% {reply, N, Data}.
%% ---
-sleep(T) ->
- receive after T -> ok end.
+%% sleep(T) ->
+%% receive after T -> ok end.
%% ---
-formated_timestamp() ->
- format_timestamp(os:timestamp()).
+%% formated_timestamp() ->
+%% format_timestamp(os:timestamp()).
-format_timestamp(Now) ->
- N2T = fun(N) -> calendar:now_to_local_time(N) end,
- format_timestamp(Now, N2T, true).
+%% format_timestamp(Now) ->
+%% N2T = fun(N) -> calendar:now_to_local_time(N) end,
+%% format_timestamp(Now, N2T, true).
-format_timestamp({_N1, _N2, N3} = N, N2T, true) ->
- FormatExtra = ".~.2.0w",
- ArgsExtra = [N3 div 10000],
- format_timestamp(N, N2T, FormatExtra, ArgsExtra);
-format_timestamp({_N1, _N2, _N3} = N, N2T, false) ->
- FormatExtra = "",
- ArgsExtra = [],
- format_timestamp(N, N2T, FormatExtra, ArgsExtra).
+%% format_timestamp({_N1, _N2, N3} = N, N2T, true) ->
+%% FormatExtra = ".~.2.0w",
+%% ArgsExtra = [N3 div 10000],
+%% format_timestamp(N, N2T, FormatExtra, ArgsExtra);
+%% format_timestamp({_N1, _N2, _N3} = N, N2T, false) ->
+%% FormatExtra = "",
+%% ArgsExtra = [],
+%% format_timestamp(N, N2T, FormatExtra, ArgsExtra).
-format_timestamp(N, N2T, FormatExtra, ArgsExtra) ->
- {Date, Time} = N2T(N),
- {YYYY,MM,DD} = Date,
- {Hour,Min,Sec} = Time,
- FormatDate =
- io_lib:format("~.4w-~.2.0w-~.2.0w ~.2.0w:~.2.0w:~.2.0w" ++ FormatExtra,
- [YYYY, MM, DD, Hour, Min, Sec] ++ ArgsExtra),
- lists:flatten(FormatDate).
+%% format_timestamp(N, N2T, FormatExtra, ArgsExtra) ->
+%% {Date, Time} = N2T(N),
+%% {YYYY,MM,DD} = Date,
+%% {Hour,Min,Sec} = Time,
+%% FormatDate =
+%% io_lib:format("~.4w-~.2.0w-~.2.0w ~.2.0w:~.2.0w:~.2.0w" ++ FormatExtra,
+%% [YYYY, MM, DD, Hour, Min, Sec] ++ ArgsExtra),
+%% lists:flatten(FormatDate).
%% ---
e(F, A) ->
- p("<ERROR> " ++ F, A).
+ ?LIB:e(F, A).
i(F) ->
- i(F, []).
+ ?LIB:i(F).
+
i(F, A) ->
- p("*** " ++ F, A).
-
-p(F, A) ->
- io:format("[client,~p][~s] " ++ F ++ "~n", [self(),formated_timestamp()|A]).
+ ?LIB:i(F, A).
diff --git a/lib/kernel/test/socket_lib.erl b/lib/kernel/test/socket_lib.erl
new file mode 100644
index 0000000000..0eed81d61a
--- /dev/null
+++ b/lib/kernel/test/socket_lib.erl
@@ -0,0 +1,129 @@
+%%
+%% %CopyrightBegin%
+%%
+%% Copyright Ericsson AB 2018-2018. All Rights Reserved.
+%%
+%% 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.
+%%
+%% %CopyrightEnd%
+%%
+
+-module(socket_lib).
+
+-export([
+ sleep/1,
+ enc_req_msg/2, enc_rep_msg/2,
+ enc_msg/3, dec_msg/1,
+ request/3, reply/4,
+ f/2,
+ i/1, i/2,
+ e/2
+ ]).
+
+
+-define(REQ, 0).
+-define(REP, 1).
+
+
+%% ---
+
+sleep(T) ->
+ receive after T -> ok end.
+
+
+%% ---
+
+enc_req_msg(N, Data) ->
+ enc_msg(?REQ, N, Data).
+
+enc_rep_msg(N, Data) ->
+ enc_msg(?REP, N, Data).
+
+enc_msg(Type, N, Data) when is_list(Data) ->
+ enc_msg(Type, N, list_to_binary(Data));
+enc_msg(Type, N, Data)
+ when is_integer(Type) andalso is_integer(N) andalso is_binary(Data) ->
+ <<Type:32/integer, N:32/integer, Data/binary>>.
+
+dec_msg(<<?REQ:32/integer, N:32/integer, Data/binary>>) ->
+ {request, N, Data};
+dec_msg(<<?REP:32/integer, N:32/integer, Data/binary>>) ->
+ {reply, N, Data}.
+
+
+%% ---
+
+request(Tag, Pid, Request) ->
+ Ref = make_ref(),
+ Pid ! {Tag, self(), Ref, Request},
+ receive
+ {Tag, Pid, Ref, Reply} ->
+ Reply
+ end.
+
+reply(Tag, Pid, Ref, Reply) ->
+ Pid ! {Tag, self(), Ref, Reply}.
+
+
+%% ---
+
+f(F, A) ->
+ lists:flatten(io_lib:format(F, A)).
+
+
+%% ---
+
+e(F, A) ->
+ p("<ERROR> " ++ F, A).
+
+i(F) ->
+ i(F, []).
+i(F, A) ->
+ p("*** " ++ F, A).
+
+p(F, A) ->
+ p(get(sname), F, A).
+
+p(SName, F, A) ->
+ io:format("[~s,~p][~s] " ++ F ++ "~n",
+ [SName,self(),formated_timestamp()|A]).
+
+
+%% ---
+
+formated_timestamp() ->
+ format_timestamp(os:timestamp()).
+
+format_timestamp(Now) ->
+ N2T = fun(N) -> calendar:now_to_local_time(N) end,
+ format_timestamp(Now, N2T, true).
+
+format_timestamp({_N1, _N2, N3} = N, N2T, true) ->
+ FormatExtra = ".~.2.0w",
+ ArgsExtra = [N3 div 10000],
+ format_timestamp(N, N2T, FormatExtra, ArgsExtra);
+format_timestamp({_N1, _N2, _N3} = N, N2T, false) ->
+ FormatExtra = "",
+ ArgsExtra = [],
+ format_timestamp(N, N2T, FormatExtra, ArgsExtra).
+
+format_timestamp(N, N2T, FormatExtra, ArgsExtra) ->
+ {Date, Time} = N2T(N),
+ {YYYY,MM,DD} = Date,
+ {Hour,Min,Sec} = Time,
+ FormatDate =
+ io_lib:format("~.4w-~.2.0w-~.2.0w ~.2.0w:~.2.0w:~.2.0w" ++ FormatExtra,
+ [YYYY, MM, DD, Hour, Min, Sec] ++ ArgsExtra),
+ lists:flatten(FormatDate).
+
+
diff --git a/lib/kernel/test/socket_server.erl b/lib/kernel/test/socket_server.erl
index dde605b624..702f040434 100644
--- a/lib/kernel/test/socket_server.erl
+++ b/lib/kernel/test/socket_server.erl
@@ -1,21 +1,34 @@
-%%%-------------------------------------------------------------------
-%%% @author Micael Karlberg <[email protected]>
-%%% @copyright (C) 2018, Micael Karlberg
-%%% @doc
-%%%
-%%% @end
-%%% Created : 27 Jun 2018 by Micael Karlberg <[email protected]>
-%%%-------------------------------------------------------------------
+%%
+%% %CopyrightBegin%
+%%
+%% Copyright Ericsson AB 2018-2018. All Rights Reserved.
+%%
+%% 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.
+%%
+%% %CopyrightEnd%
+%%
+
-module(socket_server).
--export([start/0]).
+-export([start/0,
+ start_tcp/0,
+ start_udp/0]).
--define(REQ, 0).
--define(REP, 1).
+-define(LIB, socket_lib).
--record(manager, {acceptor, handler_id, handlers}).
+-record(manager, {acceptor, handler_id, handlers}).
-record(acceptor, {socket, manager}).
--record(handler, {socket, manager}).
+-record(handler, {socket, type, manager}).
start() ->
start_tcp().
@@ -23,6 +36,9 @@ start() ->
start_tcp() ->
start(inet, stream, tcp).
+start_udp() ->
+ start(inet, dgram, udp).
+
start(Domain, Type, Proto) ->
put(sname, "starter"),
i("try start manager"),
@@ -51,13 +67,13 @@ manager_stop(Pid, Reason) ->
manager_request(Pid, {stop, Reason}).
manager_request(Pid, Request) ->
- request(manager, Pid, Request).
+ ?LIB:request(manager, Pid, Request).
manager_reply(Pid, Ref, Reply) ->
- reply(manager, Pid, Ref, Reply).
+ ?LIB:reply(manager, Pid, Ref, Reply).
-manager_init(Domain, Type, Proto) ->
+manager_init(Domain, stream = Type, Proto) ->
put(sname, "manager"),
i("try start acceptor"),
case acceptor_start(Domain, Type, Proto) of
@@ -68,8 +84,44 @@ manager_init(Domain, Type, Proto) ->
handlers = []});
{error, Reason} ->
exit({failed_starting_acceptor, Reason})
+ end;
+manager_init(Domain, dgram = Type, Proto) ->
+ put(sname, "manager"),
+ i("try open socket"),
+ case socket:open(Domain, Type, Proto) of
+ {ok, Sock} ->
+ Addr = which_addr(Domain),
+ SA = #{family => Domain,
+ addr => Addr},
+ case socket:bind(Sock, SA) of
+ {ok, _P} ->
+ ok;
+ {error, BReason} ->
+ throw({bind, BReason})
+ end,
+ i("try start handler for"
+ "~n ~p", [case socket:sockname(Sock) of
+ {ok, Name} -> Name;
+ {error, _} = E -> E
+ end]),
+ case handler_start(1, Sock) of
+ {ok, {Pid, MRef}} ->
+ i("handler (~p) started", [Pid]),
+ handler_continue(Pid),
+ manager_loop(#manager{handler_id = 2, % Just in case
+ handlers = [{Pid, MRef, 1}]});
+ {error, SReason} ->
+ e("Failed starting handler: "
+ "~n ~p", [SReason]),
+ exit({failed_start_handler, SReason})
+ end;
+ {error, OReason} ->
+ e("Failed open socket: "
+ "~n ~p", [OReason]),
+ exit({failed_open_socket, OReason})
end.
+
manager_loop(M) ->
receive
{'DOWN', MRef, process, Pid, Reason} ->
@@ -262,7 +314,11 @@ acceptor_loop(#acceptor{socket = LSock} = A) ->
end.
acceptor_handle_accept_success(#acceptor{manager = Manager}, Sock) ->
- i("try start handler"),
+ i("try start handler for peer"
+ "~n ~p", [case socket:peername(Sock) of
+ {ok, Peer} -> Peer;
+ {error, _} = E -> E
+ end]),
case manager_start_handler(Manager, Sock) of
{ok, Pid} ->
i("handler (~p) started - now change 'ownership'", [Pid]),
@@ -309,10 +365,10 @@ handler_continue(Pid) ->
handler_request(Pid, continue).
handler_request(Pid, Request) ->
- request(handler, Pid, Request).
+ ?LIB:request(handler, Pid, Request).
handler_reply(Pid, Ref, Reply) ->
- reply(handler, Pid, Ref, Reply).
+ ?LIB:reply(handler, Pid, Ref, Reply).
handler_init(Manager, ID, Sock) ->
@@ -321,23 +377,30 @@ handler_init(Manager, ID, Sock) ->
Manager ! {handler, self(), ok},
receive
{handler, Pid, Ref, continue} ->
- i("continue"),
+ i("got continue"),
handler_reply(Pid, Ref, ok),
+ {ok, Type} = socket:getopt(Sock, socket, type),
%% socket:setopt(Socket, otp, debug, true),
handler_loop(#handler{manager = Manager,
+ type = Type,
socket = Sock})
end.
-handler_loop(#handler{socket = Socket} = H) ->
- case socket:recv(Socket) of
- {ok, Msg} ->
- i("received ~w bytes of data", [size(Msg)]),
- case dec_msg(Msg) of
+handler_loop(H) ->
+ i("try read message"),
+ case recv(H) of
+ {ok, {Source, Msg}} ->
+ i("received ~w bytes of data~s",
+ [size(Msg), case Source of
+ undefined -> "";
+ _ -> f(" from:~n ~p", [Source])
+ end]),
+ case ?LIB:dec_msg(Msg) of
{request, N, Req} ->
i("received request ~w: "
"~n ~p", [N, Req]),
- Reply = enc_rep_msg(N, "hoppsan"),
- case socket:send(Socket, Reply) of
+ Reply = ?LIB:enc_rep_msg(N, "hoppsan"),
+ case send(H, Reply, Source) of
ok ->
i("successfully sent reply ~w", [N]),
handler_loop(H);
@@ -360,87 +423,99 @@ handler_loop(#handler{socket = Socket} = H) ->
end.
+recv(#handler{socket = Sock, type = stream}) ->
+ case socket:recv(Sock) of
+ {ok, Msg} ->
+ {ok, {undefined, Msg}};
+ {error, _} = ERROR ->
+ ERROR
+ end;
+recv(#handler{socket = Sock, type = dgram}) ->
+ %% ok = socket:setopt(Sock, otp, debug, true),
+ socket:recvfrom(Sock).
+
+
+send(#handler{socket = Sock, type = stream}, Msg, _) ->
+ socket:send(Sock, Msg);
+send(#handler{socket = Sock, type = dgram}, Msg, Dest) ->
+ socket:sendto(Sock, Msg, Dest).
+
+
%% =========================================================================
-enc_req_msg(N, Data) ->
- enc_msg(?REQ, N, Data).
+%% enc_req_msg(N, Data) ->
+%% enc_msg(?REQ, N, Data).
-enc_rep_msg(N, Data) ->
- enc_msg(?REP, N, Data).
+%% enc_rep_msg(N, Data) ->
+%% enc_msg(?REP, N, Data).
-enc_msg(Type, N, Data) when is_list(Data) ->
- enc_msg(Type, N, list_to_binary(Data));
-enc_msg(Type, N, Data)
- when is_integer(Type) andalso is_integer(N) andalso is_binary(Data) ->
- <<Type:32/integer, N:32/integer, Data/binary>>.
+%% enc_msg(Type, N, Data) when is_list(Data) ->
+%% enc_msg(Type, N, list_to_binary(Data));
+%% enc_msg(Type, N, Data)
+%% when is_integer(Type) andalso is_integer(N) andalso is_binary(Data) ->
+%% <<Type:32/integer, N:32/integer, Data/binary>>.
-dec_msg(<<?REQ:32/integer, N:32/integer, Data/binary>>) ->
- {request, N, Data};
-dec_msg(<<?REP:32/integer, N:32/integer, Data/binary>>) ->
- {reply, N, Data}.
+%% dec_msg(<<?REQ:32/integer, N:32/integer, Data/binary>>) ->
+%% {request, N, Data};
+%% dec_msg(<<?REP:32/integer, N:32/integer, Data/binary>>) ->
+%% {reply, N, Data}.
%% ---
-request(Tag, Pid, Request) ->
- Ref = make_ref(),
- Pid ! {Tag, self(), Ref, Request},
- receive
- {Tag, Pid, Ref, Reply} ->
- Reply
- end.
+%% request(Tag, Pid, Request) ->
+%% Ref = make_ref(),
+%% Pid ! {Tag, self(), Ref, Request},
+%% receive
+%% {Tag, Pid, Ref, Reply} ->
+%% Reply
+%% end.
-reply(Tag, Pid, Ref, Reply) ->
- Pid ! {Tag, self(), Ref, Reply}.
+%% reply(Tag, Pid, Ref, Reply) ->
+%% Pid ! {Tag, self(), Ref, Reply}.
%% ---
-formated_timestamp() ->
- format_timestamp(os:timestamp()).
+%% formated_timestamp() ->
+%% format_timestamp(os:timestamp()).
-format_timestamp(Now) ->
- N2T = fun(N) -> calendar:now_to_local_time(N) end,
- format_timestamp(Now, N2T, true).
+%% format_timestamp(Now) ->
+%% N2T = fun(N) -> calendar:now_to_local_time(N) end,
+%% format_timestamp(Now, N2T, true).
-format_timestamp({_N1, _N2, N3} = N, N2T, true) ->
- FormatExtra = ".~.2.0w",
- ArgsExtra = [N3 div 10000],
- format_timestamp(N, N2T, FormatExtra, ArgsExtra);
-format_timestamp({_N1, _N2, _N3} = N, N2T, false) ->
- FormatExtra = "",
- ArgsExtra = [],
- format_timestamp(N, N2T, FormatExtra, ArgsExtra).
+%% format_timestamp({_N1, _N2, N3} = N, N2T, true) ->
+%% FormatExtra = ".~.2.0w",
+%% ArgsExtra = [N3 div 10000],
+%% format_timestamp(N, N2T, FormatExtra, ArgsExtra);
+%% format_timestamp({_N1, _N2, _N3} = N, N2T, false) ->
+%% FormatExtra = "",
+%% ArgsExtra = [],
+%% format_timestamp(N, N2T, FormatExtra, ArgsExtra).
-format_timestamp(N, N2T, FormatExtra, ArgsExtra) ->
- {Date, Time} = N2T(N),
- {YYYY,MM,DD} = Date,
- {Hour,Min,Sec} = Time,
- FormatDate =
- io_lib:format("~.4w-~.2.0w-~.2.0w ~.2.0w:~.2.0w:~.2.0w" ++ FormatExtra,
- [YYYY, MM, DD, Hour, Min, Sec] ++ ArgsExtra),
- lists:flatten(FormatDate).
+%% format_timestamp(N, N2T, FormatExtra, ArgsExtra) ->
+%% {Date, Time} = N2T(N),
+%% {YYYY,MM,DD} = Date,
+%% {Hour,Min,Sec} = Time,
+%% FormatDate =
+%% io_lib:format("~.4w-~.2.0w-~.2.0w ~.2.0w:~.2.0w:~.2.0w" ++ FormatExtra,
+%% [YYYY, MM, DD, Hour, Min, Sec] ++ ArgsExtra),
+%% lists:flatten(FormatDate).
%% ---
f(F, A) ->
- lists:flatten(io_lib:format(F, A)).
+ ?LIB:f(F, A).
e(F, A) ->
- p("<ERROR> " ++ F, A).
+ ?LIB:e(F, A).
i(F) ->
- i(F, []).
-i(F, A) ->
- p("*** " ++ F, A).
+ ?LIB:i(F).
-p(F, A) ->
- p(get(sname), F, A).
+i(F, A) ->
+ ?LIB:i(F, A).
-p(SName, F, A) ->
- io:format("[~s,~p][~s] " ++ F ++ "~n",
- [SName,self(),formated_timestamp()|A]).
-