lib/socket: intro of the socket module and examples
authorMatthieu Lucas <lucasmatthieu@gmail.com>
Wed, 15 May 2013 19:29:10 +0000 (15:29 -0400)
committerMatthieu Lucas <lucasmatthieu@gmail.com>
Wed, 15 May 2013 19:48:08 +0000 (15:48 -0400)
Signed-off-by: Matthieu Lucas <lucasmatthieu@gmail.com>

examples/socket_client.nit [new file with mode: 0644]
examples/socket_server.nit [new file with mode: 0644]
lib/socket/socket.nit [new file with mode: 0644]
lib/socket/socket_c.nit [new file with mode: 0644]
tests/sav/socket_client.sav [new file with mode: 0644]
tests/sav/socket_server.sav [new file with mode: 0644]

diff --git a/examples/socket_client.nit b/examples/socket_client.nit
new file mode 100644 (file)
index 0000000..adc69ff
--- /dev/null
@@ -0,0 +1,35 @@
+# This file is part of NIT ( http://www.nitlanguage.org ).
+#
+# Copyright 2013 Matthieu Lucas <lucasmatthieu@gmail.com>
+#
+# 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.
+
+# Client sample using the Socket module which connect to the server sample.
+module socket_client
+
+import socket
+
+if args.length < 2 then
+       print "Usage : {program_name} <host> <port>"
+       return
+end
+
+var s = new Socket.stream_with_host(args[0], args[1].to_i)
+print "[HOST ADDRESS] : {s.address}"
+print "[HOST] : {s.host.as(not null)}"
+print "[PORT] : {s.port.to_s}"
+print "Connecting ... {s.connect.to_s}"
+print "Writing ... {s.write("Hello server !").to_s}"
+print "[Response from server] : {s.read.to_s}"
+print "Closing ... {s.close.to_s}"
+
diff --git a/examples/socket_server.nit b/examples/socket_server.nit
new file mode 100644 (file)
index 0000000..73ae782
--- /dev/null
@@ -0,0 +1,53 @@
+# This file is part of NIT ( http://www.nitlanguage.org ).
+#
+# Copyright 2013 Matthieu Lucas <lucasmatthieu@gmail.com>
+#
+# 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.
+
+# Server sample using the Socket module which allow client to connect
+module socket_server
+
+import socket
+
+if args.is_empty then
+       print "Usage : {program_name} <port>"
+       return
+end
+
+var socket = new Socket.stream_with_port(args[0].to_i)
+print "[PORT] : {socket.port.to_s}"
+print "Binding ... {socket.bind.to_s}"
+print "Listening ... {socket.listen(3).to_s}"
+
+var clients = new Array[Socket]
+var max = socket
+loop
+       var fs = new SocketObserver(true, true, true)
+       fs.readset.set(socket)
+
+       for c in clients do fs.readset.set(c)
+
+       if fs.select(max, 4, 0) == 0 then
+               print "Error occured in select {socket.errno.to_s}"
+               break
+       end
+
+       if fs.readset.is_set(socket) then
+               var ns = socket.accept
+               print "Accepting {ns.address} ... "
+               print "[Message from {ns.address}] : {ns.read}"
+               ns.write("Goodbye client.")
+               print "Closing {ns.address} ... {ns.close.to_s}"
+       end
+end
+
diff --git a/lib/socket/socket.nit b/lib/socket/socket.nit
new file mode 100644 (file)
index 0000000..d5f203a
--- /dev/null
@@ -0,0 +1,94 @@
+# This file is part of NIT ( http://www.nitlanguage.org ).
+#
+# Copyright 2013 Matthieu Lucas <lucasmatthieu@gmail.com>
+#
+# 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.
+
+# Socket services
+module socket
+
+import socket_c
+
+class Socket
+       var address: String
+       var host: nullable String
+       var port: Int
+       private var socket: FFSocket
+       private var addrin: FFSocketAddrIn
+
+       init stream_with_host(thost: String, tport: Int)
+       do
+               socket = new FFSocket.socket( new FFSocketAddressFamilies.af_inet, new FFSocketTypes.sock_stream, new FFSocketProtocolFamilies.pf_null )
+               var hostname = socket.gethostbyname(thost)
+               addrin = new FFSocketAddrIn.with_hostent(hostname, tport)
+               address = addrin.address
+               host = hostname.h_name
+               port = addrin.port
+       end
+
+       init stream_with_port(tport: Int)
+       do
+               socket = new FFSocket.socket( new FFSocketAddressFamilies.af_inet, new FFSocketTypes.sock_stream, new FFSocketProtocolFamilies.pf_null )
+               addrin = new FFSocketAddrIn.with(tport, new FFSocketAddressFamilies.af_inet)
+               address = addrin.address
+               port = addrin.port
+               host = null
+       end
+
+       init primitive_init(h: FFSocketAcceptResult)
+       do
+               socket = h.socket
+               addrin = h.addrIn
+               address = addrin.address
+               port = addrin.port
+               host = null
+       end
+
+       fun connect: Bool do return socket.connect(addrin) >= 0
+       fun write(msg: String): Bool do return socket.write(msg) >= 0
+       fun read: String do return socket.read
+       fun close: Bool do return socket.close >= 0
+       fun bind: Bool do return socket.bind(addrin) >= 0
+       fun listen(size: Int): Bool do return socket.listen(size) >= 0
+       fun accept: Socket do return new Socket.primitive_init(socket.accept)
+       fun errno: Int do return socket.errno
+end
+
+class SocketSet
+       var sset: FFSocketSet
+       init do sset = new FFSocketSet end
+       fun set(s: Socket) do sset.set(s.socket) end
+       fun is_set(s: Socket): Bool do return sset.is_set(s.socket) end
+       fun zero do sset.zero end
+       fun clear(s: Socket) do sset.clear(s.socket) end
+end
+
+class SocketObserver
+       private var observer: FFSocketObserver
+       var readset: nullable SocketSet = null
+       var writeset: nullable SocketSet = null
+       var exceptset: nullable SocketSet = null
+       init(read :Bool, write :Bool, except: Bool)
+       do
+               if read then readset = new SocketSet
+               if write then writeset = new SocketSet
+               if except then exceptset = new SocketSet
+               observer = new FFSocketObserver
+       end     
+       fun select(max: Socket,seconds: Int, microseconds: Int): Bool
+       do
+               var timeval = new FFTimeval(seconds, microseconds)
+               return observer.select(max.socket, readset.sset, writeset.sset, readset.sset, timeval) > 0
+       end
+end
+
diff --git a/lib/socket/socket_c.nit b/lib/socket/socket_c.nit
new file mode 100644 (file)
index 0000000..559a844
--- /dev/null
@@ -0,0 +1,220 @@
+# This file is part of NIT ( http://www.nitlanguage.org ).
+#
+# Copyright 2013 Matthieu Lucas <lucasmatthieu@gmail.com>
+#
+# 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.
+
+# Low-level socket functionalities
+module socket_c
+
+in "C Header" `{
+       #include <stdio.h>
+       #include <stdlib.h>
+       #include <unistd.h>
+       #include <string.h>
+       #include <sys/socket.h>
+       #include <sys/types.h>
+       #include <netinet/in.h>
+       #include <arpa/inet.h>
+       #include <netdb.h>
+       #include <errno.h>
+
+       typedef int S_DESCRIPTOR;
+       typedef struct sockaddr_in S_ADDR_IN;
+       typedef struct sockaddr S_ADDR;
+       typedef struct in_addr S_IN_ADDR;
+       typedef struct hostent S_HOSTENT;
+       typedef struct timeval S_TIMEVAL;
+       typedef struct sockaccept_result { S_ADDR_IN addr_in; S_DESCRIPTOR s_desc; } S_ACCEPT_RESULT;
+       typedef fd_set S_FD_SET;
+       typedef socklen_t S_LEN;
+`}
+
+extern FFSocket `{ S_DESCRIPTOR* `}
+       new socket(domain: FFSocketAddressFamilies, socketType: FFSocketTypes, protocol: FFSocketProtocolFamilies) `{
+               S_DESCRIPTOR *d = NULL; d = (S_DESCRIPTOR*) malloc( sizeof(S_DESCRIPTOR) );
+               int ds = socket(domain, socketType, protocol);
+               memcpy(d, &ds, sizeof(ds));
+               return d;
+       `}
+       fun destroy `{ free(recv); `}
+       fun close: Int `{ return close( *recv ); `}
+       fun descriptor: Int `{ return *recv; `}
+       fun errno: Int `{ return errno; `}
+
+       fun gethostbyname(n: String): FFSocketHostent import String::to_cstring `{ return gethostbyname(String_to_cstring(n)); `}
+       fun connect(addrIn: FFSocketAddrIn): Int `{ return connect( *recv, (S_ADDR*)addrIn, sizeof(*addrIn) ); `}
+       fun write(buffer: String): Int import String::to_cstring, String::length `{ return write(*recv, (char*)String_to_cstring(buffer), String_length(buffer)); `}
+
+       fun read: String `{
+               char c[1024];
+               int n = read(*recv, c, (sizeof(c)-1));
+               if(n < 0) exit(-1);
+               c[n] = '\0';
+               return new_String_from_cstring(c);
+       `}
+
+       fun bind(addrIn: FFSocketAddrIn): Int `{ return bind(*recv, (S_ADDR*)addrIn, sizeof(*addrIn)); `}
+       fun listen(size: Int): Int `{ return listen(*recv, size); `}
+
+       private fun i_accept(addrIn: FFSocketAddrIn): FFSocket `{
+               S_LEN s = sizeof(S_ADDR);
+               S_DESCRIPTOR *d = NULL;
+               d = malloc(sizeof(S_DESCRIPTOR*));
+               *d = accept(*recv,(S_ADDR*)addrIn, &s);
+               return d;
+       `}
+       fun accept: FFSocketAcceptResult
+       do
+               var addrIn = new FFSocketAddrIn
+               var s = i_accept(addrIn)
+               return new FFSocketAcceptResult(s, addrIn)
+       end
+end
+
+extern FFSocketAcceptResult `{ S_ACCEPT_RESULT* `}
+       new (socket: FFSocket, addrIn: FFSocketAddrIn) `{
+               S_ACCEPT_RESULT *sar = NULL;
+               sar = malloc( sizeof(S_ACCEPT_RESULT*) );
+               sar->s_desc = *socket;
+               sar->addr_in = *addrIn;
+               return sar;
+       `}
+       fun socket: FFSocket `{ return &recv->s_desc; `}
+       fun addrIn: FFSocketAddrIn `{ return &recv->addr_in; `}
+       fun destroy `{ free(recv); `}
+end
+
+extern FFSocketAddrIn `{ S_ADDR_IN* `}
+       new `{
+               S_ADDR_IN *sai = NULL;
+               sai = malloc( sizeof(S_ADDR_IN*) );
+               return sai;
+       `}
+       new with(port: Int, family: FFSocketAddressFamilies) `{
+               S_ADDR_IN *sai = NULL;
+               sai = malloc( sizeof(S_ADDR_IN*) );
+               sai->sin_family = family;
+               sai->sin_port = htons(port);
+               sai->sin_addr.s_addr = INADDR_ANY;
+               return sai;
+       `}
+       new with_hostent(hostent: FFSocketHostent, port: Int) `{
+               S_ADDR_IN *sai = NULL;
+               sai = malloc( sizeof(S_ADDR_IN*) );
+               sai->sin_family = hostent->h_addrtype;
+               sai->sin_port = htons(port);
+               memcpy( (char*)&sai->sin_addr.s_addr, (char*)hostent->h_addr, hostent->h_length );
+               return sai;
+       `}
+       fun address: String `{ return new_String_from_cstring( (char*)inet_ntoa(recv->sin_addr) ); `}
+       fun family: FFSocketAddressFamilies `{ return recv->sin_family; `}
+       fun port: Int `{ return ntohs(recv->sin_port); `}
+       fun destroy `{ free(recv); `}
+end
+
+extern FFSocketHostent `{ S_HOSTENT* `}
+       private fun i_h_aliases(i: Int): String `{ return new_String_from_cstring(recv->h_aliases[i]); `}
+       private fun i_h_aliases_reachable(i: Int): Bool `{ return (recv->h_aliases[i] != NULL); `}
+       fun h_aliases: Array[String]
+       do
+               var i=0
+               var d=new Array[String]
+               loop
+                       d.add(i_h_aliases(i))
+                       if i_h_aliases_reachable(i+1) == false then break
+                       i += 1
+               end
+               return d
+       end
+       fun h_addr: String `{ return new_String_from_cstring( (char*)inet_ntoa(*(S_IN_ADDR*)recv->h_addr) ); `}
+       fun h_addrtype: Int `{ return recv->h_addrtype; `}
+       fun h_length: Int `{ return recv->h_length; `}
+       fun h_name: String `{ return new_String_from_cstring(recv->h_name); `}
+end
+
+extern FFTimeval `{ S_TIMEVAL* `}
+       new (seconds: Int, microseconds: Int) `{
+               S_TIMEVAL* tv = NULL;
+               tv = malloc( sizeof(S_TIMEVAL*) );
+               tv->tv_sec = seconds;
+               tv->tv_usec = microseconds;
+               return tv;
+       `}
+       fun seconds: Int `{ return recv->tv_sec; `}
+       fun microseconds: Int `{ return recv->tv_usec; `}
+       fun destroy `{ free( recv ); `}
+end
+
+extern FFSocketSet `{ S_FD_SET* `}
+       new `{
+               S_FD_SET *f = NULL;
+               f = malloc( sizeof(S_FD_SET*) );
+               return f;
+       `}
+       fun set(s: FFSocket) `{ FD_SET( *s, recv ); `}
+       fun is_set(s: FFSocket): Bool `{ return FD_ISSET( *s, recv ); `}
+       fun zero `{ FD_ZERO( recv ); `}
+       fun clear(s: FFSocket) `{ FD_CLR( *s, recv ); `}
+       fun destroy `{ free( recv ); `}
+end
+
+class FFSocketObserver
+       fun select(max: FFSocket, reads: nullable FFSocketSet, write: nullable FFSocketSet,
+             except: nullable FFSocketSet, timeout: FFTimeval): Int `{
+               S_FD_SET *rds, *wts, *exs = NULL;
+               S_TIMEVAL *tm = NULL;
+               if(reads != NULL) rds = (S_FD_SET*)reads;
+               if(write != NULL) wts = (S_FD_SET*)write;
+               if(except != NULL) exs = (S_FD_SET*)except;
+               if(timeout != NULL) tm = (S_TIMEVAL*)timeout;
+               return select(*max, rds, wts, exs, tm);
+       `}
+end
+
+extern FFSocketTypes `{ int `}
+       new sock_stream `{ return SOCK_STREAM; `}
+       new sock_dgram `{ return SOCK_DGRAM; `}
+       new sock_raw `{ return SOCK_RAW; `}
+       new sock_seqpacket `{ return SOCK_SEQPACKET; `}
+end
+extern FFSocketAddressFamilies `{ int `}
+       new af_null `{ return 0; `}
+       new af_unspec `{ return  AF_UNSPEC; `}          # unspecified
+       new af_unix `{ return  AF_UNIX; `}              # local to host (pipes)
+       new af_local `{ return  AF_LOCAL; `}            # backward compatibility
+       new af_inet `{ return  AF_INET; `}              # internetwork: UDP, TCP, etc.
+       new af_sna `{ return  AF_SNA; `}                # IBM SNA
+       new af_decnet `{ return  AF_DECnet; `}          # DECnet
+       new af_route `{ return  AF_ROUTE; `}            # Internal Routing Protocol
+       new af_ipx `{ return  AF_IPX; `}                # Novell Internet Protocol
+       new af_isdn `{ return  AF_ISDN; `}              # Integrated Services Digital Network
+       new af_inet6 `{ return  AF_INET6; `}            # IPv6
+       new af_max `{ return  AF_MAX; `}
+end
+extern FFSocketProtocolFamilies `{ int `}
+       new pf_null `{ return 0; `}
+       new pf_unspec `{ return PF_UNSPEC; `}
+       new pf_local `{ return PF_LOCAL; `}
+       new pf_unix `{ return PF_UNIX; `}
+       new pf_inet `{ return PF_INET; `}
+       new pf_sna `{ return PF_SNA; `}
+       new pf_decnet `{ return PF_DECnet; `}
+       new pf_route `{ return PF_ROUTE; `}
+       new pf_ipx `{ return PF_IPX; `}
+       new pf_isdn `{ return PF_ISDN; `}
+       new pf_key `{ return PF_KEY; `}
+       new pf_inet6 `{ return PF_INET6; `}
+       new pf_max `{ return PF_MAX; `}
+end
+
diff --git a/tests/sav/socket_client.sav b/tests/sav/socket_client.sav
new file mode 100644 (file)
index 0000000..3ca92d1
--- /dev/null
@@ -0,0 +1 @@
+Usage : ./out/socket_client.bin <host> <port>
diff --git a/tests/sav/socket_server.sav b/tests/sav/socket_server.sav
new file mode 100644 (file)
index 0000000..1b95d8d
--- /dev/null
@@ -0,0 +1 @@
+Usage : ./out/socket_server.bin <port>