v/vlib/net/socket.v

319 lines
6.9 KiB
V
Raw Normal View History

2019-06-30 16:11:55 +02:00
module net
2019-07-01 14:55:45 +02:00
struct Socket {
pub:
2019-06-30 16:11:55 +02:00
sockfd int
2019-07-01 14:55:45 +02:00
family int
_type int
proto int
2019-06-30 16:11:55 +02:00
}
2019-07-13 23:29:00 +02:00
struct C.WSAData {
mut:
wVersion u16
wHighVersion u16
szDescription [257]byte
szSystemStatus [129]byte
iMaxSockets u16
iMaxUdpDg u16
lpVendorInfo byteptr
}
2019-07-20 05:51:45 +02:00
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)
)
2019-07-01 14:55:45 +02:00
struct C.in_addr {
mut:
s_addr int
2019-06-30 16:11:55 +02:00
}
2019-07-01 14:55:45 +02:00
struct C.sockaddr_in {
mut:
sin_family int
sin_port int
sin_addr C.in_addr
}
2019-06-30 16:11:55 +02:00
struct C.addrinfo {
2019-07-01 14:55:45 +02:00
mut:
ai_family int
ai_socktype int
ai_flags int
ai_protocol int
ai_addrlen int
2019-07-01 14:55:45 +02:00
ai_addr voidptr
ai_canonname voidptr
ai_next voidptr
2019-07-01 14:55:45 +02:00
}
2019-06-30 16:11:55 +02:00
2019-07-01 14:55:45 +02:00
struct C.sockaddr_storage {}
2019-06-30 20:57:25 +02:00
2019-07-01 14:55:45 +02:00
// create socket
2019-07-20 05:51:45 +02:00
pub fn socket(family int, _type int, proto int) ?Socket {
2019-07-13 23:29:00 +02:00
$if windows {
mut wsadata := C.WSAData{}
2019-07-20 05:51:45 +02:00
res := C.WSAStartup(WSA_V22, &wsadata)
if res != 0 {
return error('socket: WSAStartup failed')
}
2019-07-13 23:29:00 +02:00
}
2019-07-01 14:55:45 +02:00
sockfd := C.socket(family, _type, proto)
2019-08-22 23:00:31 +02:00
one:=1
// This is needed so that there are no problems with reusing the
// same port after the application exits.
C.setsockopt(sockfd, C.SOL_SOCKET, C.SO_REUSEADDR, &one, sizeof(int))
2019-07-20 05:51:45 +02:00
if sockfd == 0 {
return error('socket: init failed')
}
2019-07-01 14:55:45 +02:00
s := Socket {
sockfd: sockfd
family: family
_type: _type
proto: proto
2019-06-30 16:11:55 +02:00
}
2019-07-01 14:55:45 +02:00
return s
}
pub fn socket_udp() ?Socket {
2019-08-22 23:00:31 +02:00
return socket(C.AF_INET, C.SOCK_DGRAM, C.IPPROTO_UDP)
}
2019-07-01 14:55:45 +02:00
// set socket options
2019-09-03 18:10:56 +02:00
pub fn (s Socket) setsockopt(level int, optname int, optvalue &int) ?int {
2019-07-01 14:55:45 +02:00
res := C.setsockopt(s.sockfd, level, optname, optvalue, C.sizeof(optvalue))
2019-07-20 05:51:45 +02:00
if res < 0 {
return error('socket: setsockopt failed')
}
return int(res)
2019-07-01 14:55:45 +02:00
}
// bind socket to port
2019-07-20 05:51:45 +02:00
pub fn (s Socket) bind(port int) ?int {
2019-07-01 14:55:45 +02:00
mut addr := C.sockaddr_in{}
addr.sin_family = s.family
addr.sin_port = C.htons(port)
2019-08-22 23:00:31 +02:00
addr.sin_addr.s_addr = C.htonl(C.INADDR_ANY)
2019-07-01 14:55:45 +02:00
size := 16 // sizeof(C.sockaddr_in)
res := int(C.bind(s.sockfd, &addr, size))
2019-07-20 05:51:45 +02:00
if res < 0 {
return error('socket: bind failed')
}
return res
2019-07-01 14:55:45 +02:00
}
// put socket into passive mode and wait to receive
2019-07-20 05:51:45 +02:00
pub fn (s Socket) listen() ?int {
2019-07-01 14:55:45 +02:00
backlog := 128
2019-08-22 23:00:31 +02:00
res := int(C.listen(s.sockfd, backlog))
2019-07-20 05:51:45 +02:00
if res < 0 {
return error('socket: listen failed')
}
2019-08-20 10:18:12 +02:00
$if debug {
println('listen res = $res')
}
2019-08-22 23:00:31 +02:00
return res
2019-07-01 14:55:45 +02:00
}
// put socket into passive mode with user specified backlog and wait to receive
2019-07-20 05:51:45 +02:00
pub fn (s Socket) listen_backlog(backlog int) ?int {
mut n := 0
if backlog > 0 {
n = backlog
}
res := C.listen(s.sockfd, n)
2019-07-20 05:51:45 +02:00
if res < 0 {
return error('socket: listen_backlog failed')
}
return int(res)
}
2019-07-01 14:55:45 +02:00
// helper method to create, bind, and listen given port number
2019-07-20 05:51:45 +02:00
pub fn listen(port int) ?Socket {
2019-08-20 10:18:12 +02:00
$if debug {
println('net.listen($port)')
}
2019-08-22 23:00:31 +02:00
s := socket(C.AF_INET, C.SOCK_STREAM, 0) or {
2019-07-20 05:51:45 +02:00
return error(err)
}
bind_res := s.bind(port) or {
return error(err)
}
listen_res := s.listen() or {
return error(err)
2019-06-30 16:11:55 +02:00
}
2019-07-01 14:55:45 +02:00
return s
2019-06-30 16:11:55 +02:00
}
2019-07-01 14:55:45 +02:00
// accept first connection request from socket queue
2019-07-20 05:51:45 +02:00
pub fn (s Socket) accept() ?Socket {
2019-08-20 10:18:12 +02:00
$if debug {
println('accept()')
}
2019-07-01 14:55:45 +02:00
addr := C.sockaddr_storage{}
size := 128 // sizeof(sockaddr_storage)
sockfd := C.accept(s.sockfd, &addr, &size)
if sockfd < 0 {
2019-07-20 05:51:45 +02:00
return error('socket: accept failed')
2019-06-30 21:00:22 +02:00
}
2019-07-01 14:55:45 +02:00
c := Socket {
sockfd: sockfd
family: s.family
_type: s._type
proto: s.proto
2019-06-30 21:00:22 +02:00
}
2019-07-01 14:55:45 +02:00
return c
}
// connect to given addrress and port
2019-07-20 05:51:45 +02:00
pub fn (s Socket) connect(address string, port int) ?int {
2019-07-01 14:55:45 +02:00
mut hints := C.addrinfo{}
2019-08-22 23:00:31 +02:00
hints.ai_family = C.AF_UNSPEC
hints.ai_socktype = C.SOCK_STREAM
hints.ai_flags = C.AI_PASSIVE
hints.ai_addrlen = 0
hints.ai_canonname = C.NULL
hints.ai_addr = C.NULL
2019-07-01 14:55:45 +02:00
info := &C.addrinfo{!}
sport := '$port'
info_res := C.getaddrinfo(address.str, sport.str, &hints, &info)
2019-07-01 14:55:45 +02:00
if info_res != 0 {
2019-07-20 05:51:45 +02:00
return error('socket: connect failed')
2019-06-30 21:00:22 +02:00
}
2019-08-04 00:56:12 +02:00
res := int(C.connect(s.sockfd, info.ai_addr, info.ai_addrlen))
2019-07-20 05:51:45 +02:00
if res < 0 {
return error('socket: connect failed')
}
return int(res)
2019-06-30 20:57:25 +02:00
}
2019-07-01 14:55:45 +02:00
// helper method to create socket and connect
2019-07-20 05:51:45 +02:00
pub fn dial(address string, port int) ?Socket {
2019-08-22 23:00:31 +02:00
s := socket(C.AF_INET, C.SOCK_STREAM, 0) or {
2019-07-20 05:51:45 +02:00
return error(err)
}
res := s.connect(address, port) or {
return error(err)
2019-07-01 14:55:45 +02:00
}
return s
2019-06-30 20:57:25 +02:00
}
2019-07-02 17:25:21 +02:00
// send string data to socket
pub fn (s Socket) send(buf byteptr, len int) ?int {
res := int( C.send(s.sockfd, buf, len, 0) )
if res < 0 {
return error('socket: send failed')
}
2019-07-02 17:25:21 +02:00
return res
}
// receive string data from socket
pub fn (s Socket) recv(bufsize int) (byteptr, int) {
2019-07-02 17:25:21 +02:00
buf := malloc(bufsize)
res := int( C.recv(s.sockfd, buf, bufsize, 0) )
return buf, res
2019-07-02 17:25:21 +02:00
}
2019-08-21 19:04:06 +02:00
// TODO: remove cread/2 and crecv/2 when the Go net interface is done
pub fn (s Socket) cread( buffer byteptr, buffersize int ) int {
return int( C.read(s.sockfd, buffer, buffersize) )
}
// Receive a message from the socket, and place it in a preallocated buffer buf,
// with maximum message size bufsize. Returns the length of the received message.
2019-08-21 19:04:06 +02:00
pub fn (s Socket) crecv( buffer byteptr, buffersize int ) int {
return int( C.recv(s.sockfd, buffer, buffersize, 0) )
}
2019-07-01 14:55:45 +02:00
// shutdown and close socket
2019-07-20 05:51:45 +02:00
pub fn (s Socket) close() ?int {
mut shutdown_res := 0
2019-07-13 23:29:00 +02:00
$if windows {
2019-08-22 23:00:31 +02:00
shutdown_res = C.shutdown(s.sockfd, C.SD_BOTH)
2019-07-13 23:29:00 +02:00
}
$else {
2019-08-22 23:00:31 +02:00
shutdown_res = C.shutdown(s.sockfd, C.SHUT_RDWR)
2019-07-13 23:29:00 +02:00
}
2019-07-20 05:51:45 +02:00
// TODO: should shutdown throw an error? close will
// continue even if shutdown failed
// if shutdown_res < 0 {
// return error('socket: shutdown failed')
// }
2019-07-13 23:29:00 +02:00
2019-07-20 05:51:45 +02:00
mut res := 0
2019-07-13 23:29:00 +02:00
$if windows {
2019-07-20 05:51:45 +02:00
res = C.closesocket(s.sockfd)
2019-07-01 14:55:45 +02:00
}
2019-07-13 23:29:00 +02:00
$else {
2019-07-20 05:51:45 +02:00
res = C.close(s.sockfd)
}
if res < 0 {
return error('socket: close failed')
2019-07-13 23:29:00 +02:00
}
2019-07-01 14:55:45 +02:00
return 0
}
2019-07-29 18:21:36 +02:00
2019-08-22 23:00:31 +02:00
const (
2019-07-29 18:21:36 +02:00
MAX_READ = 400
2019-08-22 23:00:31 +02:00
)
2019-07-29 18:21:36 +02:00
pub fn (s Socket) write(str string) {
line := '$str\r\n'
C.send(s.sockfd, line.str, line.len, 0)
2019-07-29 18:21:36 +02:00
}
2019-08-22 23:00:31 +02:00
2019-07-29 18:21:36 +02:00
pub fn (s Socket) read_line() string {
mut res := ''
for {
2019-08-20 10:18:12 +02:00
$if debug {
println('.')
}
2019-07-29 18:21:36 +02:00
mut buf := malloc(MAX_READ)
n := int(C.recv(s.sockfd, buf, MAX_READ-1, 0))
2019-08-20 10:18:12 +02:00
$if debug {
println('numbytes=$n')
}
2019-07-29 18:21:36 +02:00
if n == -1 {
2019-08-20 10:18:12 +02:00
$if debug {
println('recv failed')
}
2019-07-29 18:21:36 +02:00
// TODO
return ''
}
if n == 0 {
break
}
// println('resp len=$numbytes')
buf[n] = `\0`
2019-08-22 23:00:31 +02:00
// C.printf('!!buf= "%s" n=%d\n', buf,n)
2019-07-29 18:21:36 +02:00
line := string(buf)
res += line
// Reached a newline. That's an end of an IRC message
// TODO dont need ends_with check ?
if line.ends_with('\n') || n < MAX_READ - 1 {
// println('NL')
break
}
if line.ends_with('\r\n') {
// println('RNL')
break
}
}
return res
}
pub fn (s Socket) get_port() int {
mut addr := C.sockaddr_in {}
size := 16 // sizeof(sockaddr_in)
sockname_res := C.getsockname(s.sockfd, &addr, &size)
return int(C.ntohs(addr.sin_port))
}
2019-08-22 23:00:31 +02:00