net: socket error handling
parent
2291e9fcfe
commit
d72c859bf9
|
@ -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
|
||||||
|
|
|
@ -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()
|
||||||
|
|
|
@ -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
|
|
||||||
}
|
|
||||||
|
|
Loading…
Reference in New Issue