diff --git a/vlib/net/socket.v b/vlib/net/socket.v index 2c7afa9bfa..86860dc20c 100644 --- a/vlib/net/socket.v +++ b/vlib/net/socket.v @@ -1,150 +1,169 @@ module net -#include -#include -#include #include -#include +#include +#include -struct Conn { -pub: +struct Socket { +pub: sockfd int + family int + _type int + proto int } -struct Listener { - listener int - their_addr voidptr -} - -import const ( - AF_UNSPEC - AF_INET - SOCK_STREAM - AI_PASSIVE - PF_INET - SOL_SOCKET - SO_REUSEADDR - INADDR_ANY -) - -struct C.addrinfo { -mut: - ai_family int - ai_socktype int - ai_flags int - ai_protocol int - ai_addrlen int - ai_next voidptr - ai_addr voidptr -} +import const ( + AF_INET + AF_INET6 + AF_UNSPEC + SOCK_STREAM + SOL_SOCKET + SO_REUSEADDR + SO_REUSEPORT + INADDR_ANY + AI_PASSIVE + SHUT_RD + SHUT_WR + SHUT_RDWR +) struct C.in_addr { -mut: - s_addr int - -} +mut: + s_addr int +} struct C.sockaddr_in { -mut: - sin_family int - sin_port int - sin_addr C.in_addr - -} - -struct C.sockaddr_storage { - -} - -fn dial(addr string, port int) Conn { - println('net.dial("$addr":$port)') - mut hints := C.addrinfo{} - servinfo := &C.addrinfo{!} - mut rp := &C.addrinfo{!} - mut sockfd := -1 - // allow IPv4 or IPv6 - hints.ai_family = AF_UNSPEC - // avoid name lookup for port - // hints.ai_flags = AI_NUMERICSERV - hints.ai_socktype = SOCK_STREAM - hints.ai_flags = AI_PASSIVE - strport := '$port' - connbad := Conn{} - c_addr := addr.cstr() - rv := C.getaddrinfo(c_addr, strport.cstr(), &hints, &servinfo) - if rv != 0 { - println('Getaddrinfo failed ') - return connbad - } - // Loop through all the results and connect to the first we can - for rp = servinfo; !isnil(rp); rp = rp.ai_next { - sockfd = C.socket(rp.ai_family, rp.ai_socktype, rp.ai_protocol) - if sockfd == -1 { - continue - } - if C.connect(sockfd, rp.ai_addr, rp.ai_addrlen) { - C.close(sockfd) - continue - } - break - } - if sockfd == -1 { - println('socket: Cannot connect to host') - return connbad - } - C.freeaddrinfo(servinfo) - conn := Conn { - sockfd: sockfd, - } - return conn +mut: + sin_family int + sin_port int + sin_addr C.in_addr } -fn listen(addr string, port int) Listener { - mut hints := C.addrinfo{} - res := &C.addrinfo{!} - new_fd := 0 - BACKLOG := 10 - strport := port.str() - option := 1 - listener := C.socket(PF_INET, SOCK_STREAM, 0) - C.setsockopt(listener, SOL_SOCKET, SO_REUSEADDR, &option, sizeof(int)) - bad_listener := Listener{their_addr: 0} - if listener < 0 { - println('couldnt create listen scoket') - return bad_listener - } - // socket address used for the server - mut server_address := C.sockaddr_in{} - server_address.sin_family = AF_INET - // htons: host to network short: transforms a value in host byte - // ordering format to a short value in network byte ordering format - server_address.sin_port = C.htons(port) - // htonl: host to network long: same as htons but to long - server_address.sin_addr.s_addr = C.htonl(INADDR_ANY) - size := 16 // sizeof(C.sockaddr_in) - if C.bind(listener, &server_address, size) < 0 { - println('cant bind') - return bad_listener - } - if C.listen(listener, BACKLOG) < 0 { - println('cant listen') - return bad_listener - } - l := Listener { - listener: listener, - their_addr: 0 - } - return l +struct C.addrinfo { +mut: + ai_family int + ai_socktype int + ai_flags int + ai_protocol int + ai_addrlen int + ai_next voidptr + ai_addr voidptr } -// accept an incoming connection -fn (l Listener) accept() Conn { - their_addr := C.sockaddr_storage{} - addr_size := 128 // sizeof (l.their_addr) - return Conn { - sockfd: C.accept(l.listener, &their_addr, &addr_size) - } +struct C.sockaddr_storage {} + +// create socket +pub fn socket(family int, _type int, proto int) Socket { + sockfd := C.socket(family, _type, proto) + s := Socket { + sockfd: sockfd + family: family + _type: _type + proto: proto + } + return s } - +// set socket options +pub fn (s Socket) setsockopt(level int, optname int, optvalue int) int { + res := C.setsockopt(s.sockfd, level, optname, optvalue, C.sizeof(optvalue)) + return res +} + +// bind socket to port +pub fn (s Socket) bind(port int) int { + mut addr := C.sockaddr_in{} + addr.sin_family = s.family + addr.sin_port = C.htons(port) + addr.sin_addr.s_addr = C.htonl(INADDR_ANY) + size := 16 // sizeof(C.sockaddr_in) + res := C.bind(s.sockfd, &addr, size) + return res +} + +// put socket into passive mode and wait to receive +pub fn (s Socket) listen() int { + backlog := 128 + res := C.listen(s.sockfd, backlog) + return res +} + +// helper method to create, bind, and listen given port number +pub fn listen(port int) Socket { + s := socket(AF_INET, SOCK_STREAM, 0) + if s.sockfd == 0 { + println('socket: init socket failed') + return s + } + bind_res := s.bind(port) + if bind_res < 0 { + println('socket: bind failed') + return s + } + listen_res := s.listen() + if listen_res < 0 { + println('socket: listen failed') + return s + } + return s +} + +// accept first connection request from socket queue +pub fn (s Socket) accept() Socket { + addr := C.sockaddr_storage{} + size := 128 // sizeof(sockaddr_storage) + sockfd := C.accept(s.sockfd, &addr, &size) + if sockfd < 0 { + println('socket: accept failed') + } + c := Socket { + sockfd: sockfd + family: s.family + _type: s._type + proto: s.proto + } + return c +} + +// connect to given addrress and port +pub fn (s Socket) connect(address string, port int) int { + mut hints := C.addrinfo{} + hints.ai_family = AF_UNSPEC + hints.ai_socktype = SOCK_STREAM + hints.ai_flags = AI_PASSIVE + + info := &C.addrinfo{!} + sport := '$port' + info_res := C.getaddrinfo(address.cstr(), sport.cstr(), &hints, &info) + if info_res != 0 { + println('socket: getaddrinfo failed') + return info_res + } + + res := C.connect(s.sockfd, info.ai_addr, info.ai_addrlen) + return res +} + +// helper method to create socket and connect +pub fn dial(address string, port int) Socket { + s := socket(AF_INET, SOCK_STREAM, 0) + res := s.connect(address, port) + if res < 0 { + println('socket: failed to connect') + } + return s +} + +// shutdown and close socket +pub fn (s Socket) close() int { + shutdown_res := C.shutdown(s.sockfd, SHUT_RDWR) + if shutdown_res < 0 { + println('socket: shutdown failed') + } + res := C.close(s.sockfd) + if res < 0 { + println('socket: close failed') + } + return 0 +} diff --git a/vlib/net/socket_test.v b/vlib/net/socket_test.v index b8c9625f94..052be66058 100644 --- a/vlib/net/socket_test.v +++ b/vlib/net/socket_test.v @@ -1,6 +1,14 @@ import net -fn test_dial() { -//conn := net.dial('irc.freenode.org', 6667) -//println(conn.sockfd) +fn test_socket() { +// server := net.listen(8080) +// println(server) +// client := net.dial('127.0.0.1', 8080) +// println(client) +// socket := server.accept() +// println(socket) +// +// server.close() +// client.close() +// socket.close() }