diff options
-rw-r--r-- | erts/emulator/nifs/common/socket_nif.c | 323 | ||||
-rw-r--r-- | erts/preloaded/ebin/socket.beam | bin | 42488 -> 42660 bytes | |||
-rw-r--r-- | erts/preloaded/src/socket.erl | 122 | ||||
-rw-r--r-- | lib/kernel/test/Makefile | 1 | ||||
-rw-r--r-- | lib/kernel/test/socket_client.erl | 252 | ||||
-rw-r--r-- | lib/kernel/test/socket_lib.erl | 129 | ||||
-rw-r--r-- | lib/kernel/test/socket_server.erl | 237 |
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 Binary files differindex 4924c43a5c..99a9f0f749 100644 --- a/erts/preloaded/ebin/socket.beam +++ b/erts/preloaded/ebin/socket.beam 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]). - |