net: socket error handling

pull/1289/head
Justice Suh 2019-07-19 23:51:45 -04:00 committed by Alexander Medvednikov
parent 2291e9fcfe
commit d72c859bf9
3 changed files with 93 additions and 86 deletions

View File

@ -24,7 +24,6 @@ import const (
SD_BOTH SD_BOTH
) )
// used for WinSock init
struct C.WSAData { struct C.WSAData {
mut: mut:
wVersion u16 wVersion u16
@ -36,6 +35,14 @@ mut:
lpVendorInfo byteptr lpVendorInfo byteptr
} }
const (
WSA_V1 = 0x100 // C.MAKEWORD(1, 0)
WSA_V11 = 0x101 // C.MAKEWORD(1, 1)
WSA_V2 = 0x200 // C.MAKEWORD(2, 0)
WSA_V21 = 0x201 // C.MAKEWORD(2, 1)
WSA_V22 = 0x202 // C.MAKEWORD(2, 2)
)
struct C.in_addr { struct C.in_addr {
mut: mut:
s_addr int s_addr int
@ -62,14 +69,19 @@ mut:
struct C.sockaddr_storage {} struct C.sockaddr_storage {}
// create socket // create socket
pub fn socket(family int, _type int, proto int) Socket { pub fn socket(family int, _type int, proto int) ?Socket {
$if windows { $if windows {
mut wsadata := C.WSAData{} mut wsadata := C.WSAData{}
res := C.WSAStartup(0x202, &wsadata) res := C.WSAStartup(WSA_V22, &wsadata)
// TODO: throw error if WSAStartup fails if res != 0 {
return error('socket: WSAStartup failed')
}
} }
sockfd := C.socket(family, _type, proto) sockfd := C.socket(family, _type, proto)
if sockfd == 0 {
return error('socket: init failed')
}
s := Socket { s := Socket {
sockfd: sockfd sockfd: sockfd
family: family family: family
@ -80,66 +92,72 @@ pub fn socket(family int, _type int, proto int) Socket {
} }
// set socket options // set socket options
pub fn (s Socket) setsockopt(level int, optname int, optvalue *int) int { pub fn (s Socket) setsockopt(level int, optname int, optvalue *int) ?int {
res := C.setsockopt(s.sockfd, level, optname, optvalue, C.sizeof(optvalue)) res := C.setsockopt(s.sockfd, level, optname, optvalue, C.sizeof(optvalue))
return res if res < 0 {
return error('socket: setsockopt failed')
}
return int(res)
} }
// bind socket to port // bind socket to port
pub fn (s Socket) bind(port int) int { pub fn (s Socket) bind(port int) ?int {
mut addr := C.sockaddr_in{} mut addr := C.sockaddr_in{}
addr.sin_family = s.family addr.sin_family = s.family
addr.sin_port = C.htons(port) addr.sin_port = C.htons(port)
addr.sin_addr.s_addr = C.htonl(INADDR_ANY) addr.sin_addr.s_addr = C.htonl(INADDR_ANY)
size := 16 // sizeof(C.sockaddr_in) size := 16 // sizeof(C.sockaddr_in)
res := C.bind(s.sockfd, &addr, size) res := C.bind(s.sockfd, &addr, size)
return res if res < 0 {
return error('socket: bind failed')
}
return int(res)
} }
// put socket into passive mode and wait to receive // put socket into passive mode and wait to receive
pub fn (s Socket) listen() int { pub fn (s Socket) listen() ?int {
backlog := 128 backlog := 128
res := C.listen(s.sockfd, backlog) res := C.listen(s.sockfd, backlog)
return res if res < 0 {
return error('socket: listen failed')
}
return int(res)
} }
// put socket into passive mode with user specified backlog and wait to receive // put socket into passive mode with user specified backlog and wait to receive
pub fn (s Socket) listen_backlog(backlog int) int { pub fn (s Socket) listen_backlog(backlog int) ?int {
mut n := 0 mut n := 0
if backlog > 0 { if backlog > 0 {
n = backlog n = backlog
} }
res := C.listen(s.sockfd, n) res := C.listen(s.sockfd, n)
return res if res < 0 {
return error('socket: listen_backlog failed')
}
return int(res)
} }
// helper method to create, bind, and listen given port number // helper method to create, bind, and listen given port number
pub fn listen(port int) Socket { pub fn listen(port int) ?Socket {
s := socket(AF_INET, SOCK_STREAM, 0) s := socket(AF_INET, SOCK_STREAM, 0) or {
if s.sockfd == 0 { return error(err)
println('socket: init socket failed')
return s
} }
bind_res := s.bind(port) bind_res := s.bind(port) or {
if bind_res < 0 { return error(err)
println('socket: bind failed')
return s
} }
listen_res := s.listen() listen_res := s.listen() or {
if listen_res < 0 { return error(err)
println('socket: listen failed')
return s
} }
return s return s
} }
// accept first connection request from socket queue // accept first connection request from socket queue
pub fn (s Socket) accept() Socket { pub fn (s Socket) accept() ?Socket {
addr := C.sockaddr_storage{} addr := C.sockaddr_storage{}
size := 128 // sizeof(sockaddr_storage) size := 128 // sizeof(sockaddr_storage)
sockfd := C.accept(s.sockfd, &addr, &size) sockfd := C.accept(s.sockfd, &addr, &size)
if sockfd < 0 { if sockfd < 0 {
println('socket: accept failed') return error('socket: accept failed')
} }
c := Socket { c := Socket {
sockfd: sockfd sockfd: sockfd
@ -151,7 +169,7 @@ pub fn (s Socket) accept() Socket {
} }
// connect to given addrress and port // connect to given addrress and port
pub fn (s Socket) connect(address string, port int) int { pub fn (s Socket) connect(address string, port int) ?int {
mut hints := C.addrinfo{} mut hints := C.addrinfo{}
hints.ai_family = AF_UNSPEC hints.ai_family = AF_UNSPEC
hints.ai_socktype = SOCK_STREAM hints.ai_socktype = SOCK_STREAM
@ -161,20 +179,22 @@ pub fn (s Socket) connect(address string, port int) int {
sport := '$port' sport := '$port'
info_res := C.getaddrinfo(address.str, sport.str, &hints, &info) info_res := C.getaddrinfo(address.str, sport.str, &hints, &info)
if info_res != 0 { if info_res != 0 {
println('socket: getaddrinfo failed') return error('socket: connect failed')
return info_res
} }
res := C.connect(s.sockfd, info.ai_addr, info.ai_addrlen) res := C.connect(s.sockfd, info.ai_addr, info.ai_addrlen)
return res if res < 0 {
return error('socket: connect failed')
}
return int(res)
} }
// helper method to create socket and connect // helper method to create socket and connect
pub fn dial(address string, port int) Socket { pub fn dial(address string, port int) ?Socket {
s := socket(AF_INET, SOCK_STREAM, 0) s := socket(AF_INET, SOCK_STREAM, 0) or {
res := s.connect(address, port) return error(err)
if res < 0 { }
println('socket: failed to connect') res := s.connect(address, port) or {
return error(err)
} }
return s return s
} }
@ -182,9 +202,9 @@ pub fn dial(address string, port int) Socket {
// send string data to socket // send string data to socket
pub fn (s Socket) send(buf byteptr, len int) int { pub fn (s Socket) send(buf byteptr, len int) int {
res := C.send(s.sockfd, buf, len, 0) res := C.send(s.sockfd, buf, len, 0)
if res < 0 { // if res < 0 {
println('socket: send failed') // return error('socket: send failed')
} // }
return res return res
} }
@ -192,43 +212,41 @@ pub fn (s Socket) send(buf byteptr, len int) int {
pub fn (s Socket) recv(bufsize int) byteptr { pub fn (s Socket) recv(bufsize int) byteptr {
buf := malloc(bufsize) buf := malloc(bufsize)
res := C.recv(s.sockfd, buf, bufsize, 0) res := C.recv(s.sockfd, buf, bufsize, 0)
if res < 0 { // if res < 0 {
println('socket: recv failed') // return error('socket: recv failed')
} // }
return buf return buf
} }
// shutdown and close socket // shutdown and close socket
pub fn (s Socket) close() int { pub fn (s Socket) close() ?int {
// WinSock // WinSock
$if windows { $if windows {
C.WSACleanup() C.WSACleanup()
} }
mut shutdown_res := 0
$if windows { $if windows {
shutdown_res := C.shutdown(s.sockfd, SD_BOTH) shutdown_res = C.shutdown(s.sockfd, SD_BOTH)
if shutdown_res < 0 {
println('socket: shutdown failed')
}
} }
$else { $else {
shutdown_res := C.shutdown(s.sockfd, SHUT_RDWR) shutdown_res = C.shutdown(s.sockfd, SHUT_RDWR)
if shutdown_res < 0 {
println('socket: shutdown failed')
}
} }
// TODO: should shutdown throw an error? close will
// continue even if shutdown failed
// if shutdown_res < 0 {
// return error('socket: shutdown failed')
// }
mut res := 0
$if windows { $if windows {
res := C.closesocket(s.sockfd) res = C.closesocket(s.sockfd)
if res < 0 {
println('socket: close failed')
}
} }
$else { $else {
res := C.close(s.sockfd) res = C.close(s.sockfd)
if res < 0 { }
println('socket: close failed') if res < 0 {
} return error('socket: close failed')
} }
return 0 return 0

View File

@ -1,20 +1,29 @@
import net import net
fn test_socket() { fn test_socket() {
// server := net.listen(8080) // server := net.listen(8080) or {
// println(err)
// return
// }
// println(server) // println(server)
// client := net.dial('127.0.0.1', 8080) // client := net.dial('127.0.0.1', 8080) or {
// println(err)
// return
// }
// println(client) // println(client)
// socket := server.accept() // socket := server.accept() or {
// println(err)
// return
// }
// println(socket) // println(socket)
//
// message := 'Hello World' // message := 'Hello World'
// socket.send(message.str, message.len) // socket.send(message.str, message.len)
// println('Sent: ' + message) // println('Sent: ' + message)
//
// bytes := client.recv(1024) // bytes := client.recv(1024)
// println('Received: ' + tos(bytes, message.len)) // println('Received: ' + tos(bytes, message.len))
//
// server.close() // server.close()
// client.close() // client.close()
// socket.close() // socket.close()

View File

@ -3,23 +3,3 @@ module net
#flag -lws2_32 #flag -lws2_32
#include <winsock2.h> #include <winsock2.h>
#include <Ws2tcpip.h> #include <Ws2tcpip.h>
// Ref - https://docs.microsoft.com/en-us/windows/win32/api/winsock/nf-winsock-wsastartup
const (
WSA_V1 = 0x100 // C.MAKEWORD(1, 0)
WSA_V11 = 0x101 // C.MAKEWORD(1, 1)
WSA_V2 = 0x200 // C.MAKEWORD(2, 0)
WSA_V21 = 0x201 // C.MAKEWORD(2, 1)
WSA_V22 = 0x202 // C.MAKEWORD(2, 2)
)
pub fn socket(family int, stype int, prototype int) Socket {
sockfd := C.socket(family, _type, proto)
s := Socket {
sockfd: sockfd
family: family
_type: _type
proto: proto
}
return s
}