v/vlib/net/socket.v

236 lines
4.3 KiB
Go

module net
struct Socket {
pub:
sockfd int
family int
_type int
proto int
}
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
SD_BOTH
)
// used for WinSock init
struct C.WSAData {
mut:
wVersion u16
wHighVersion u16
szDescription [257]byte
szSystemStatus [129]byte
iMaxSockets u16
iMaxUdpDg u16
lpVendorInfo byteptr
}
struct C.in_addr {
mut:
s_addr int
}
struct C.sockaddr_in {
mut:
sin_family int
sin_port int
sin_addr C.in_addr
}
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
}
struct C.sockaddr_storage {}
// create socket
pub fn socket(family int, _type int, proto int) Socket {
$if windows {
mut wsadata := C.WSAData{}
res := C.WSAStartup(0x202, &wsadata)
// TODO: throw error if WSAStartup fails
}
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
}
// put socket into passive mode with user specified backlog and wait to receive
pub fn (s Socket) listen_backlog(backlog int) int {
mut n := 0
if backlog > 0 {
n = backlog
}
res := C.listen(s.sockfd, n)
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.str, sport.str, &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
}
// send string data to socket
pub fn (s Socket) send(buf byteptr, len int) int {
res := C.send(s.sockfd, buf, len, 0)
if res < 0 {
println('socket: send failed')
}
return res
}
// receive string data from socket
pub fn (s Socket) recv(bufsize int) byteptr {
buf := malloc(bufsize)
res := C.recv(s.sockfd, buf, bufsize, 0)
if res < 0 {
println('socket: recv failed')
}
return buf
}
// shutdown and close socket
pub fn (s Socket) close() int {
// WinSock
$if windows {
C.WSACleanup()
}
$if windows {
shutdown_res := C.shutdown(s.sockfd, SD_BOTH)
if shutdown_res < 0 {
println('socket: shutdown failed')
}
}
$else {
shutdown_res := C.shutdown(s.sockfd, SHUT_RDWR)
if shutdown_res < 0 {
println('socket: shutdown failed')
}
}
$if windows {
res := C.closesocket(s.sockfd)
if res < 0 {
println('socket: close failed')
}
}
$else {
res := C.close(s.sockfd)
if res < 0 {
println('socket: close failed')
}
}
return 0
}