diff --git a/examples/unix/sock-client.py b/examples/unix/sock-client.py
index d5d5021475b20f62726f9469311595898795b935..08a39b196a1f961f2e9c96e257ce33b09fc590b6 100644
--- a/examples/unix/sock-client.py
+++ b/examples/unix/sock-client.py
@@ -1,22 +1,32 @@
-mod = rawsocket
-s = mod.socket()
+try:
+    import rawsocket as _socket
+except:
+    import _socket
+
+
+s = _socket.socket()
 
 if 1:
-    ai = mod.getaddrinfo("google.com", 80)
+    ai = _socket.getaddrinfo("google.com", 80)
     print("Address infos:", ai)
     addr = ai[0][4]
 else:
-    # Deprecated way to construct connection address
-    addr = mod.sockaddr_in()
+    # Deprecated ways to construct connection address
+    addr = _socket.sockaddr_in()
     addr.sin_family = 2
     #addr.sin_addr = (0x0100 << 16) + 0x007f
     #addr.sin_addr = (0x7f00 << 16) + 0x0001
-    #addr.sin_addr = mod.inet_aton("127.0.0.1")
-    addr.sin_addr = mod.gethostbyname("google.com")
-    addr.sin_port = mod.htons(80)
+    #addr.sin_addr = _socket.inet_aton("127.0.0.1")
+    addr.sin_addr = _socket.gethostbyname("google.com")
+    addr.sin_port = _socket.htons(80)
 
 print("Connect address:", addr)
 s.connect(addr)
 
-s.write("GET / HTTP/1.0\n\n")
-print(s.readall())
+if 0:
+    # MicroPython rawsocket module supports file interface directly
+    s.write("GET / HTTP/1.0\n\n")
+    print(s.readall())
+else:
+    s.send(b"GET / HTTP/1.0\n\n")
+    print(s.recv(4096))
diff --git a/examples/unix/sock-server.py b/examples/unix/sock-server.py
index c39a0af21cab1a7cd0dafe82aeb2a56e05560664..ed14b7f31eef2740430286af23e23d2dd236a961 100644
--- a/examples/unix/sock-server.py
+++ b/examples/unix/sock-server.py
@@ -1,7 +1,18 @@
-mod = rawsocket
-s = mod.socket()
+try:
+    import rawsocket as socket
+except:
+    import socket
 
-ai = mod.getaddrinfo("127.0.0.1", 8080)
+
+CONTENT = """\
+HTTP/1.0 200 OK
+
+Hello #{} from MicroPython!
+"""
+
+s = socket.socket()
+
+ai = socket.getaddrinfo("127.0.0.1", 8080)
 print("Bind address info:", ai)
 addr = ai[0][4]
 
@@ -17,12 +28,13 @@ while True:
     print("Client address:", client_addr)
     print("Client socket:", client_s)
     print("Request:")
-    print(client_s.read(4096))
-    #print(client_s.readall())
-    client_s.write("""\
-HTTP/1.0 200 OK
-
-Hello #{} from MicroPython!
-""".format(counter))
+    if 0:
+        # MicroPython rawsocket module supports file interface directly
+        print(client_s.read(4096))
+        #print(client_s.readall())
+        client_s.write(CONTENT.format(counter))
+    else:
+        print(client_s.recv(4096))
+        client_s.send(bytes(CONTENT.format(counter), "ascii"))
     client_s.close()
     counter += 1
diff --git a/py/builtin.c b/py/builtin.c
index 8340ad3045881e19fdb8eb41452a8434d9dcc0e8..9cbc037677ee7319e532c6a099b050ac111d1b05 100644
--- a/py/builtin.c
+++ b/py/builtin.c
@@ -350,3 +350,15 @@ static mp_obj_t mp_builtin_str(mp_obj_t o_in) {
 }
 
 MP_DEFINE_CONST_FUN_OBJ_1(mp_builtin_str_obj, mp_builtin_str);
+
+// TODO: This should be type, this is just quick CPython compat hack
+static mp_obj_t mp_builtin_bytes(uint n_args, const mp_obj_t *args) {
+    if (!MP_OBJ_IS_QSTR(args[0]) && !MP_OBJ_IS_TYPE(args[0], &str_type)) {
+        assert(0);
+    }
+    // Currently, MicroPython strings are mix between CPython byte and unicode
+    // strings. So, conversion is null so far.
+    return args[0];
+}
+
+MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(mp_builtin_bytes_obj, 1, 3, mp_builtin_bytes);
diff --git a/py/builtin.h b/py/builtin.h
index 050a2161c1c01cd62334c35a93c0bc55dd9afc53..4257de5bdbab87b3447e68b6137d2ee8bc0836a5 100644
--- a/py/builtin.h
+++ b/py/builtin.h
@@ -5,6 +5,7 @@ MP_DECLARE_CONST_FUN_OBJ(mp_builtin___repl_print___obj);
 MP_DECLARE_CONST_FUN_OBJ(mp_builtin_abs_obj);
 MP_DECLARE_CONST_FUN_OBJ(mp_builtin_all_obj);
 MP_DECLARE_CONST_FUN_OBJ(mp_builtin_any_obj);
+MP_DECLARE_CONST_FUN_OBJ(mp_builtin_bytes_obj); // Temporary hack
 MP_DECLARE_CONST_FUN_OBJ(mp_builtin_callable_obj);
 MP_DECLARE_CONST_FUN_OBJ(mp_builtin_chr_obj);
 MP_DECLARE_CONST_FUN_OBJ(mp_builtin_divmod_obj);
diff --git a/py/obj.c b/py/obj.c
index 5a6c08332a417371d80049c029cc54f8be933eb0..8a073f4710dc65db668bef6304deadc1db64f5bb 100644
--- a/py/obj.c
+++ b/py/obj.c
@@ -28,6 +28,8 @@ mp_obj_t mp_obj_get_type(mp_obj_t o_in) {
 const char *mp_obj_get_type_str(mp_obj_t o_in) {
     if (MP_OBJ_IS_SMALL_INT(o_in)) {
         return "int";
+    } else if (MP_OBJ_IS_QSTR(o_in)) {
+        return "str";
     } else {
         mp_obj_base_t *o = o_in;
         return o->type->name;
@@ -222,7 +224,9 @@ void mp_obj_get_complex(mp_obj_t arg, mp_float_t *real, mp_float_t *imag) {
 #endif
 
 qstr mp_obj_get_qstr(mp_obj_t arg) {
-    if (MP_OBJ_IS_TYPE(arg, &str_type)) {
+    if (MP_OBJ_IS_QSTR(arg)) {
+        return MP_OBJ_QSTR_VALUE(arg);
+    } else if (MP_OBJ_IS_TYPE(arg, &str_type)) {
         return mp_obj_str_get(arg);
     } else {
         assert(0);
@@ -286,3 +290,9 @@ mp_obj_t mp_obj_len_maybe(mp_obj_t o_in) {
     }
     return MP_OBJ_NEW_SMALL_INT(len);
 }
+
+// Return input argument. Useful as .getiter for objects which are
+// their own iterators, etc.
+mp_obj_t mp_identity(mp_obj_t self) {
+    return self;
+}
diff --git a/py/obj.h b/py/obj.h
index faf02314749b9366e7abd1c287ae14ec10e4d6c0..c2ce32588a5a8e56dbbec8d8f882df8981b31063 100644
--- a/py/obj.h
+++ b/py/obj.h
@@ -351,6 +351,8 @@ extern const mp_obj_type_t fun_native_type;
 extern const mp_obj_type_t fun_bc_type;
 void mp_obj_fun_bc_get(mp_obj_t self_in, int *n_args, uint *n_state, const byte **code);
 
+mp_obj_t mp_identity(mp_obj_t self);
+
 // generator
 extern const mp_obj_type_t gen_instance_type;
 
@@ -374,3 +376,6 @@ typedef struct _mp_obj_classmethod_t {
     mp_obj_base_t base;
     mp_obj_t fun;
 } mp_obj_classmethod_t;
+
+// sequence helpers
+void mp_seq_multiply(const void *items, uint item_sz, uint len, uint times, void *dest);
diff --git a/py/objlist.c b/py/objlist.c
index bc363d38fdb5cc21597ed031d1f90d47c44993d5..2e9a8705ff13c0167ff2e6416a7a3e4d1b83cd1a 100644
--- a/py/objlist.c
+++ b/py/objlist.c
@@ -153,13 +153,8 @@ static mp_obj_t list_binary_op(int op, mp_obj_t lhs, mp_obj_t rhs) {
                 return NULL;
             }
             int n = MP_OBJ_SMALL_INT_VALUE(rhs);
-            int len = o->len;
-            mp_obj_list_t *s = list_new(len * n);
-            mp_obj_t *dest = s->items;
-            for (int i = 0; i < n; i++) {
-                memcpy(dest, o->items, sizeof(mp_obj_t) * len);
-                dest += len;
-            }
+            mp_obj_list_t *s = list_new(o->len * n);
+            mp_seq_multiply(o->items, sizeof(*o->items), o->len, n, s->items);
             return s;
         }
         case RT_COMPARE_OP_EQUAL:
diff --git a/py/objstr.c b/py/objstr.c
index 3552058430533fd8bc2812a86ac88b0bf79ea040..f4dc8573982e21a9cc5785d9dc7899b39fa00bea 100644
--- a/py/objstr.c
+++ b/py/objstr.c
@@ -65,9 +65,8 @@ mp_obj_t str_binary_op(int op, mp_obj_t lhs_in, mp_obj_t rhs_in) {
             // TODO: need predicate to check for int-like type (bools are such for example)
             // ["no", "yes"][1 == 2] is common idiom
             if (MP_OBJ_IS_SMALL_INT(rhs_in)) {
-                // TODO: This implements byte string access for single index so far
-                // TODO: Handle negative indexes.
-                return mp_obj_new_int(lhs_data[mp_obj_get_int(rhs_in)]);
+                uint index = mp_get_index(lhs->base.type, lhs_len, rhs_in);
+                return mp_obj_new_str(qstr_from_strn((const char*)lhs_data + index, 1));
 #if MICROPY_ENABLE_SLICE
             } else if (MP_OBJ_IS_TYPE(rhs_in, &slice_type)) {
                 machine_int_t start, stop, step;
@@ -122,6 +121,16 @@ mp_obj_t str_binary_op(int op, mp_obj_t lhs_in, mp_obj_t rhs_in) {
                 return mp_const_false;
             }
             break;
+        case RT_BINARY_OP_MULTIPLY:
+        {
+            if (!MP_OBJ_IS_SMALL_INT(rhs_in)) {
+                return NULL;
+            }
+            int n = MP_OBJ_SMALL_INT_VALUE(rhs_in);
+            char *s = m_new(char, lhs_len * n);
+            mp_seq_multiply(lhs_data, sizeof(*lhs_data), lhs_len, n, s);
+            return MP_OBJ_NEW_QSTR(qstr_from_strn_take(s, lhs_len * n, lhs_len * n));
+        }
     }
 
     return MP_OBJ_NULL; // op not supported
@@ -184,6 +193,45 @@ bad_arg:
     nlr_jump(mp_obj_new_exception_msg(MP_QSTR_TypeError, "?str.join expecting a list of str's"));
 }
 
+#define is_ws(c) ((c) == ' ' || (c) == '\t')
+
+static mp_obj_t str_split(uint n_args, const mp_obj_t *args) {
+    int splits = -1;
+    mp_obj_t sep = mp_const_none;
+    if (n_args > 1) {
+        sep = args[1];
+        if (n_args > 2) {
+            splits = MP_OBJ_SMALL_INT_VALUE(args[2]);
+        }
+    }
+    assert(sep == mp_const_none);
+    (void)sep; // unused; to hush compiler warning
+    mp_obj_t res = mp_obj_new_list(0, NULL);
+    const char *s = qstr_str(mp_obj_str_get(args[0]));
+    const char *start;
+
+    // Initial whitespace is not counted as split, so we pre-do it
+    while (is_ws(*s)) s++;
+    while (*s && splits != 0) {
+        start = s;
+        while (*s != 0 && !is_ws(*s)) s++;
+        rt_list_append(res, MP_OBJ_NEW_QSTR(qstr_from_strn(start, s - start)));
+        if (*s == 0) {
+            break;
+        }
+        while (is_ws(*s)) s++;
+        if (splits > 0) {
+            splits--;
+        }
+    }
+
+    if (*s != 0) {
+        rt_list_append(res, MP_OBJ_NEW_QSTR(qstr_from_str(s)));
+    }
+
+    return res;
+}
+
 static bool chr_in_str(const char* const str, const size_t str_len, const char c) {
     for (size_t i = 0; i < str_len; i++) {
         if (str[i] == c) {
@@ -195,16 +243,8 @@ static bool chr_in_str(const char* const str, const size_t str_len, const char c
 
 static mp_obj_t str_find(uint n_args, const mp_obj_t *args) {
     assert(2 <= n_args && n_args <= 4);
-    assert(MP_OBJ_IS_TYPE(args[0], &str_type));
-    if (!MP_OBJ_IS_TYPE(args[1], &str_type)) {
-        nlr_jump(mp_obj_new_exception_msg_1_arg(
-                     MP_QSTR_TypeError,
-                     "Can't convert '%s' object to str implicitly",
-                     mp_obj_get_type_str(args[1])));
-    }
-
-    const char* haystack = qstr_str(((mp_obj_str_t*)args[0])->qstr);
-    const char* needle = qstr_str(((mp_obj_str_t*)args[1])->qstr);
+    const char* haystack = qstr_str(mp_obj_str_get(args[0]));
+    const char* needle = qstr_str(mp_obj_str_get(args[1]));
 
     size_t haystack_len = strlen(haystack);
     size_t needle_len = strlen(needle);
@@ -242,14 +282,11 @@ mp_obj_t str_strip(uint n_args, const mp_obj_t *args) {
     if (n_args == 1) {
         chars_to_del = whitespace;
     } else {
-        assert(MP_OBJ_IS_TYPE(args[1], &str_type));
-        mp_obj_str_t *chars_to_del_obj = args[1];
-        chars_to_del = qstr_str(chars_to_del_obj->qstr);
+        chars_to_del = qstr_str(mp_obj_str_get(args[1]));
     }
 
     const size_t chars_to_del_len = strlen(chars_to_del);
-    mp_obj_str_t *self = args[0];
-    const char *orig_str = qstr_str(self->qstr);
+    const char *orig_str = qstr_str(mp_obj_str_get(args[0]));
     const size_t orig_str_len = strlen(orig_str);
 
     size_t first_good_char_pos = 0;
@@ -307,12 +344,14 @@ mp_obj_t str_format(uint n_args, const mp_obj_t *args) {
 
 static MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(str_find_obj, 2, 4, str_find);
 static MP_DEFINE_CONST_FUN_OBJ_2(str_join_obj, str_join);
+static MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(str_split_obj, 1, 3, str_split);
 static MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(str_strip_obj, 1, 2, str_strip);
 static MP_DEFINE_CONST_FUN_OBJ_VAR(str_format_obj, 1, str_format);
 
 static const mp_method_t str_type_methods[] = {
     { "find", &str_find_obj },
     { "join", &str_join_obj },
+    { "split", &str_split_obj },
     { "strip", &str_strip_obj },
     { "format", &str_format_obj },
     { NULL, NULL }, // end-of-list sentinel
@@ -335,9 +374,15 @@ mp_obj_t mp_obj_new_str(qstr qstr) {
 }
 
 qstr mp_obj_str_get(mp_obj_t self_in) {
-    assert(MP_OBJ_IS_TYPE(self_in, &str_type));
-    mp_obj_str_t *self = self_in;
-    return self->qstr;
+    if (MP_OBJ_IS_QSTR(self_in)) {
+        return MP_OBJ_QSTR_VALUE(self_in);
+    }
+    if (MP_OBJ_IS_TYPE(self_in, &str_type)) {
+        mp_obj_str_t *self = self_in;
+        return self->qstr;
+    }
+    nlr_jump(mp_obj_new_exception_msg_varg(MP_QSTR_TypeError, "Can't convert '%s' object to str implicitly",
+             mp_obj_get_type_str(self_in)));
 }
 
 /******************************************************************************/
diff --git a/py/py.mk b/py/py.mk
index 72013ef9994ad85c2af5b936197754ece34c6af0..75394b361593c7407b266eeb61da52856564ce9b 100644
--- a/py/py.mk
+++ b/py/py.mk
@@ -97,6 +97,7 @@ PY_O_BASENAME = \
 	objtuple.o \
 	objtype.o \
 	objzip.o \
+	sequence.o \
 	stream.o \
 	builtin.o \
 	builtinimport.o \
diff --git a/py/qstrdefs.h b/py/qstrdefs.h
index 10b1fc0d395019bc471cdccd506f92966d694249..9bc01c585118f3d6d58022a73b0522b0e05a9a67 100644
--- a/py/qstrdefs.h
+++ b/py/qstrdefs.h
@@ -40,6 +40,7 @@ Q(any)
 Q(array)
 Q(bool)
 Q(bytearray)
+Q(bytes)
 Q(callable)
 Q(chr)
 Q(complex)
diff --git a/py/runtime.c b/py/runtime.c
index 210047ac0a81b1e3c4828dd3a1e398d77630d43a..ccd3d7d0afeb2432da9a242a8b0b75f64dee3dc0 100644
--- a/py/runtime.c
+++ b/py/runtime.c
@@ -130,6 +130,7 @@ void rt_init(void) {
     mp_map_add_qstr(&map_builtins, MP_QSTR_abs, (mp_obj_t)&mp_builtin_abs_obj);
     mp_map_add_qstr(&map_builtins, MP_QSTR_all, (mp_obj_t)&mp_builtin_all_obj);
     mp_map_add_qstr(&map_builtins, MP_QSTR_any, (mp_obj_t)&mp_builtin_any_obj);
+    mp_map_add_qstr(&map_builtins, MP_QSTR_bytes, (mp_obj_t)&mp_builtin_bytes_obj);
     mp_map_add_qstr(&map_builtins, MP_QSTR_callable, (mp_obj_t)&mp_builtin_callable_obj);
     mp_map_add_qstr(&map_builtins, MP_QSTR_chr, (mp_obj_t)&mp_builtin_chr_obj);
     mp_map_add_qstr(&map_builtins, MP_QSTR_divmod, (mp_obj_t)&mp_builtin_divmod_obj);
diff --git a/py/sequence.c b/py/sequence.c
new file mode 100644
index 0000000000000000000000000000000000000000..56718c6f859a6f24ed7bc938117148694689de20
--- /dev/null
+++ b/py/sequence.c
@@ -0,0 +1,25 @@
+#include <stdlib.h>
+#include <stdint.h>
+#include <string.h>
+#include <assert.h>
+
+#include "nlr.h"
+#include "misc.h"
+#include "mpconfig.h"
+#include "qstr.h"
+#include "obj.h"
+#include "map.h"
+#include "runtime0.h"
+#include "runtime.h"
+
+// Helpers for sequence types
+
+// Implements backend of sequence * integer operation. Assumes elements are
+// memory-adjacent in sequence.
+void mp_seq_multiply(const void *items, uint item_sz, uint len, uint times, void *dest) {
+    for (int i = 0; i < times; i++) {
+        uint copy_sz = item_sz * len;
+        memcpy(dest, items, copy_sz);
+        dest = (char*)dest + copy_sz;
+    }
+}
diff --git a/py/stream.c b/py/stream.c
index 88ddc5e6c9a35aad8cc12159e72c8d286e641d64..be560d3c22e2da7ec4282e9e7544ca75226e4287 100644
--- a/py/stream.c
+++ b/py/stream.c
@@ -30,7 +30,8 @@ static mp_obj_t stream_read(uint n_args, const mp_obj_t *args) {
         nlr_jump(mp_obj_new_exception_msg_varg(MP_QSTR_OSError, "[Errno %d]", error));
     } else {
         // TODO don't intern this string
-        return mp_obj_new_str(qstr_from_strn_take(buf, sz, out_sz));
+        buf = m_realloc(buf, sz, out_sz);
+        return mp_obj_new_str(qstr_from_strn_take(buf, out_sz, out_sz));
     }
 }
 
@@ -134,9 +135,18 @@ static mp_obj_t stream_unbuffered_readline(uint n_args, const mp_obj_t *args) {
     }
     // TODO don't intern this string
     vstr_shrink(vstr);
-    return mp_obj_new_str(qstr_from_strn_take(vstr_str(vstr), vstr->alloc, vstr_len(vstr)));
+    return MP_OBJ_NEW_QSTR(qstr_from_strn_take(vstr_str(vstr), vstr->alloc, vstr_len(vstr)));
 }
 
+mp_obj_t mp_stream_unbuffered_iter(mp_obj_t self) {
+    mp_obj_t l_in = stream_unbuffered_readline(1, &self);
+    const char *l = qstr_str(MP_OBJ_QSTR_VALUE(l_in));
+    // TODO: \0
+    if (*l != 0) {
+        return l_in;
+    }
+    return mp_const_stop_iteration;
+}
 
 MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(mp_stream_read_obj, 1, 2, stream_read);
 MP_DEFINE_CONST_FUN_OBJ_1(mp_stream_readall_obj, stream_readall);
diff --git a/py/stream.h b/py/stream.h
index 58e80725497c0ffce755ecd66653dd9c109fa78f..a0cc34797bcc7a7ebc767aae37b95ad654d7c31f 100644
--- a/py/stream.h
+++ b/py/stream.h
@@ -2,3 +2,6 @@ extern const mp_obj_fun_native_t mp_stream_read_obj;
 extern const mp_obj_fun_native_t mp_stream_readall_obj;
 extern const mp_obj_fun_native_t mp_stream_unbuffered_readline_obj;
 extern const mp_obj_fun_native_t mp_stream_write_obj;
+
+// Iterator which uses mp_stream_unbuffered_readline_obj
+mp_obj_t mp_stream_unbuffered_iter(mp_obj_t self);
diff --git a/tests/basics/string1.py b/tests/basics/string1.py
index 3fecf799beb9ad867c36019e30c8922a5b150a64..074b04ef8f5ecbece6a98e69e54dd8b1a543f6c9 100644
--- a/tests/basics/string1.py
+++ b/tests/basics/string1.py
@@ -8,6 +8,19 @@ print(x)
 
 print('123' + "456")
 
+print('123' * 5)
+
+print('abc'[1])
+print('abc'[-1])
+try:
+    'abc'[100]
+except IndexError:
+    print('caught')
+try:
+    'abc'[-4]
+except IndexError:
+    print('caught2')
+
 # iter
 print(list('str'))
 
diff --git a/tests/basics/string_split.py b/tests/basics/string_split.py
new file mode 100644
index 0000000000000000000000000000000000000000..f73cb4291efff6e508979f434ec2c7c7247f7463
--- /dev/null
+++ b/tests/basics/string_split.py
@@ -0,0 +1,7 @@
+print("a b".split())
+print("   a   b    ".split(None))
+print("   a   b    ".split(None, 1))
+print("   a   b    ".split(None, 2))
+print("   a   b  c  ".split(None, 1))
+print("   a   b  c  ".split(None, 0))
+print("   a   b  c  ".split(None, -1))
diff --git a/tests/io/file-iter.py b/tests/io/file-iter.py
new file mode 100644
index 0000000000000000000000000000000000000000..48e8739966179bd79c5cefd9802c3028acf7ab43
--- /dev/null
+++ b/tests/io/file-iter.py
@@ -0,0 +1,3 @@
+f = open("io/data/file1")
+for l in f:
+    print(l)
diff --git a/tests/run-tests b/tests/run-tests
index ef1368725f2588d6d39a271fc024c934c5f13226..752138ccc67f5e2bf28179e0989178c4b65a89f8 100755
--- a/tests/run-tests
+++ b/tests/run-tests
@@ -10,7 +10,14 @@ numpassed=0
 numfailed=0
 namefailed=
 
-for infile in basics/*.py io/*.py
+if [ $# -eq 0 ]
+then
+    tests="basics/*.py io/*.py"
+else
+    tests="$@"
+fi
+
+for infile in $tests
 do
     basename=`basename $infile .py`
     outfile=${basename}.out
diff --git a/unix/file.c b/unix/file.c
index 72c56ca100c59c45ac5f16c56326f7a8401b792c..3181c08b69746afe0fd98a0f5828a685fed1be59 100644
--- a/unix/file.c
+++ b/unix/file.c
@@ -8,6 +8,7 @@
 #include "mpconfig.h"
 #include "qstr.h"
 #include "obj.h"
+#include "runtime.h"
 #include "stream.h"
 
 typedef struct _mp_obj_fdfile_t {
@@ -15,6 +16,8 @@ typedef struct _mp_obj_fdfile_t {
     int fd;
 } mp_obj_fdfile_t;
 
+static const mp_obj_type_t rawfile_type;
+
 static void fdfile_print(void (*print)(void *env, const char *fmt, ...), void *env, mp_obj_t self_in, mp_print_kind_t kind) {
     mp_obj_fdfile_t *self = self_in;
     print(env, "<io.FileIO %d>", self->fd);
@@ -45,6 +48,13 @@ static mp_obj_t fdfile_close(mp_obj_t self_in) {
 }
 static MP_DEFINE_CONST_FUN_OBJ_1(fdfile_close_obj, fdfile_close);
 
+static mp_obj_fdfile_t *fdfile_new(int fd) {
+    mp_obj_fdfile_t *o = m_new_obj(mp_obj_fdfile_t);
+    o->base.type = &rawfile_type;
+    o->fd = fd;
+    return o;
+}
+
 static mp_obj_t fdfile_make_new(mp_obj_t type_in, uint n_args, uint n_kw, const mp_obj_t *args) {
     mp_obj_fdfile_t *o = m_new_obj(mp_obj_fdfile_t);
     o->base.type = type_in;
@@ -81,11 +91,11 @@ static mp_obj_t fdfile_make_new(mp_obj_t type_in, uint n_args, uint n_kw, const
         }
     }
 
-    o->fd = open(fname, mode, 0644);
-    if (o->fd == -1) {
-        nlr_jump(mp_obj_new_exception_msg_1_arg(MP_QSTR_OSError, "[Errno %d]", (const char *)(machine_int_t)errno));
+    int fd = open(fname, mode, 0644);
+    if (fd == -1) {
+        nlr_jump(mp_obj_new_exception_msg_varg(MP_QSTR_OSError, "[Errno %d]", errno));
     }
-    return o;
+    return fdfile_new(fd);
 }
 
 static const mp_method_t rawfile_type_methods[] = {
@@ -102,8 +112,8 @@ static const mp_obj_type_t rawfile_type = {
     "io.FileIO",
     .print = fdfile_print,
     .make_new = fdfile_make_new,
-    .getiter = NULL,
-    .iternext = NULL,
+    .getiter = mp_identity,
+    .iternext = mp_stream_unbuffered_iter,
     .stream_p = {
         .read = fdfile_read,
         .write = fdfile_write,
@@ -117,3 +127,12 @@ mp_obj_t mp_builtin_open(uint n_args, const mp_obj_t *args) {
     return fdfile_make_new((mp_obj_t)&rawfile_type, n_args, 0, args);
 }
 MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(mp_builtin_open_obj, 1, 2, mp_builtin_open);
+
+void file_init() {
+    rt_store_name(MP_QSTR_open, (mp_obj_t)&mp_builtin_open_obj);
+
+    mp_obj_t m_sys = mp_obj_new_module(MP_QSTR_sys);
+    rt_store_attr(m_sys, MP_QSTR_stdin, fdfile_new(STDIN_FILENO));
+    rt_store_attr(m_sys, MP_QSTR_stdout, fdfile_new(STDOUT_FILENO));
+    rt_store_attr(m_sys, MP_QSTR_stderr, fdfile_new(STDERR_FILENO));
+}
diff --git a/unix/main.c b/unix/main.c
index 74e903c2efba2bc2b8297dc65c6e66015ae3d386..facc250a7844a709bae6aefcce0cbef86104a98a 100644
--- a/unix/main.c
+++ b/unix/main.c
@@ -22,6 +22,7 @@
 #endif
 
 extern const mp_obj_fun_native_t mp_builtin_open_obj;
+void file_init();
 void rawsocket_init();
 
 static void execute_from_lexer(mp_lexer_t *lex, mp_parse_input_kind_t input_kind, bool is_repl) {
@@ -222,7 +223,8 @@ int main(int argc, char **argv) {
     rt_store_attr(m_sys, MP_QSTR_argv, py_argv);
 
     rt_store_name(qstr_from_str("test"), test_obj_new(42));
-    rt_store_name(MP_QSTR_open, (mp_obj_t)&mp_builtin_open_obj);
+
+    file_init();
     rawsocket_init();
 
     // Here is some example code to create a class and instance of that class.
diff --git a/unix/qstrdefsport.h b/unix/qstrdefsport.h
index 3178f0a826efa9dab888ea54d8adb6eda059cfea..a470e5dab01376206189032eb128b8879404d6d6 100644
--- a/unix/qstrdefsport.h
+++ b/unix/qstrdefsport.h
@@ -3,6 +3,9 @@
 Q(sys)
 Q(argv)
 Q(open)
+Q(stdin)
+Q(stdout)
+Q(stderr)
 Q(rawsocket)
 Q(socket)
 Q(sockaddr_in)
diff --git a/unix/socket.c b/unix/socket.c
index e43695c6b6a27463f0100d9d44279bcb7da4468d..ae87ba46569f4bc08bdfba071bb748ec238514e7 100644
--- a/unix/socket.c
+++ b/unix/socket.c
@@ -127,6 +127,41 @@ static mp_obj_t socket_accept(mp_obj_t self_in) {
 }
 static MP_DEFINE_CONST_FUN_OBJ_1(socket_accept_obj, socket_accept);
 
+static mp_obj_t socket_recv(uint n_args, const mp_obj_t *args) {
+    mp_obj_socket_t *self = args[0];
+    int sz = MP_OBJ_SMALL_INT_VALUE(args[1]);
+    int flags = 0;
+
+    if (n_args > 2) {
+        flags = MP_OBJ_SMALL_INT_VALUE(args[2]);
+    }
+
+    char *buf = m_new(char, sz);
+    int out_sz = recv(self->fd, buf, sz, flags);
+    RAISE_ERRNO(out_sz, errno);
+
+    buf = m_realloc(buf, sz, out_sz);
+    return MP_OBJ_NEW_QSTR(qstr_from_strn_take(buf, out_sz, out_sz));
+}
+static MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(socket_recv_obj, 2, 3, socket_recv);
+
+static mp_obj_t socket_send(uint n_args, const mp_obj_t *args) {
+    mp_obj_socket_t *self = args[0];
+    int flags = 0;
+
+    if (n_args > 2) {
+        flags = MP_OBJ_SMALL_INT_VALUE(args[2]);
+    }
+
+    const char *buf = qstr_str(mp_obj_str_get(args[1]));
+    int sz = strlen(buf);
+    int out_sz = send(self->fd, buf, sz, flags);
+    RAISE_ERRNO(out_sz, errno);
+
+    return MP_OBJ_NEW_SMALL_INT((machine_int_t)out_sz);
+}
+static MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(socket_send_obj, 2, 3, socket_send);
+
 static mp_obj_t socket_make_new(mp_obj_t type_in, uint n_args, uint n_kw, const mp_obj_t *args) {
     int family = AF_INET;
     int type = SOCK_STREAM;
@@ -159,6 +194,8 @@ static const mp_method_t rawsocket_type_methods[] = {
         { "bind", &socket_bind_obj },
         { "listen", &socket_listen_obj },
         { "accept", &socket_accept_obj },
+        { "recv", &socket_recv_obj },
+        { "send", &socket_send_obj },
         { "close", &socket_close_obj },
 #if MICROPY_SOCKET_EXTRA
         { "recv", &mp_stream_read_obj },
@@ -280,5 +317,4 @@ void rawsocket_init() {
     STORE_INT_CONST(m, SOCK_STREAM);
     STORE_INT_CONST(m, SOCK_DGRAM);
     STORE_INT_CONST(m, SOCK_RAW);
-    rt_store_name(MP_QSTR_rawsocket, m);
 }