1
0
mirror of https://github.com/vlang/v.git synced 2023-08-10 21:13:21 +03:00
v/vlib/net/socket.v

400 lines
8.7 KiB
V
Raw Normal View History

2019-06-30 17:11:55 +03:00
module net
2019-10-10 20:24:36 +03:00
import os
2019-10-24 19:44:49 +03:00
pub struct Socket {
2019-07-01 15:55:45 +03:00
pub:
2019-06-30 17:11:55 +03:00
sockfd int
2019-07-01 15:55:45 +03:00
family int
2020-05-16 17:12:23 +03:00
typ int
2019-12-22 01:41:42 +03:00
proto int
pub mut:
max_single_send_size int = 64000
2019-06-30 17:11:55 +03:00
}
2019-07-01 15:55:45 +03:00
struct C.in_addr {
mut:
s_addr int
2019-06-30 17:11:55 +03:00
}
2020-05-18 22:38:06 +03:00
struct C.sockaddr {
}
2019-07-01 15:55:45 +03:00
struct C.sockaddr_in {
mut:
sin_family int
2019-12-22 01:41:42 +03:00
sin_port int
sin_addr C.in_addr
2019-07-01 15:55:45 +03:00
}
2019-06-30 17:11:55 +03:00
struct C.addrinfo {
2019-07-01 15:55:45 +03:00
mut:
2019-12-22 01:41:42 +03:00
ai_family int
ai_socktype int
ai_flags int
ai_protocol int
ai_addrlen int
ai_addr voidptr
ai_canonname voidptr
2019-12-22 01:41:42 +03:00
ai_next voidptr
}
struct C.sockaddr_storage {
2019-07-01 15:55:45 +03:00
}
2019-06-30 17:11:55 +03:00
2019-11-24 06:27:02 +03:00
fn C.socket() int
2019-12-22 01:41:42 +03:00
2019-11-24 06:27:02 +03:00
fn C.setsockopt() int
2019-12-22 01:41:42 +03:00
2019-11-24 06:27:02 +03:00
fn C.htonl() int
2019-12-22 01:41:42 +03:00
2019-11-24 06:27:02 +03:00
fn C.htons() int
2019-12-22 01:41:42 +03:00
2019-11-24 06:27:02 +03:00
fn C.bind() int
2019-12-22 01:41:42 +03:00
2019-11-24 06:27:02 +03:00
fn C.listen() int
2019-12-22 01:41:42 +03:00
2019-11-24 06:27:02 +03:00
fn C.accept() int
2019-12-22 01:41:42 +03:00
2019-11-24 06:27:02 +03:00
fn C.getaddrinfo() int
2019-12-22 01:41:42 +03:00
2019-11-24 06:27:02 +03:00
fn C.connect() int
2019-12-22 01:41:42 +03:00
2019-11-24 06:27:02 +03:00
fn C.send() int
2019-12-22 01:41:42 +03:00
2019-11-24 06:27:02 +03:00
fn C.recv() int
2019-12-22 01:41:42 +03:00
2019-11-24 06:27:02 +03:00
fn C.shutdown() int
2019-12-22 01:41:42 +03:00
2019-11-24 06:27:02 +03:00
fn C.ntohs() int
2019-06-30 21:57:25 +03:00
2019-12-22 01:41:42 +03:00
fn C.getsockname() int
2020-05-16 17:12:23 +03:00
2020-07-02 22:27:36 +03:00
fn C.inet_ntop(af int, src voidptr, dst charptr, dst_size int) charptr
fn C.getpeername(sockfd int, addr &C.sockaddr_in, addrsize &int) int
2019-07-01 15:55:45 +03:00
// create socket
2020-05-16 17:12:23 +03:00
pub fn new_socket(family, typ, proto int) ?Socket {
sockfd := C.socket(family, typ, proto)
2019-12-22 01:41:42 +03:00
one := 1
2019-08-23 00:00:31 +03:00
// This is needed so that there are no problems with reusing the
// same port after the application exits.
2019-11-25 06:23:09 +03:00
C.setsockopt(sockfd, C.SOL_SOCKET, C.SO_REUSEADDR, &one, sizeof(int))
if sockfd == -1 {
return error('net.socket: failed')
2019-07-20 06:51:45 +03:00
}
2019-12-22 01:41:42 +03:00
s := Socket{
2019-07-01 15:55:45 +03:00
sockfd: sockfd
family: family
2020-05-16 17:12:23 +03:00
typ: typ
2019-07-01 15:55:45 +03:00
proto: proto
2019-06-30 17:11:55 +03:00
}
2019-07-01 15:55:45 +03:00
return s
}
pub fn socket_udp() ?Socket {
2019-11-24 06:27:02 +03:00
return new_socket(C.AF_INET, C.SOCK_DGRAM, C.IPPROTO_UDP)
}
2019-07-01 15:55:45 +03:00
// set socket options
2020-05-16 17:12:23 +03:00
pub fn (s Socket) setsockopt(level, optname int, optvalue &int) ?int {
2019-11-25 06:23:09 +03:00
res := C.setsockopt(s.sockfd, level, optname, optvalue, sizeof(&int))
2019-07-20 06:51:45 +03:00
if res < 0 {
return error('net.setsocketopt: failed with $res')
2019-07-20 06:51:45 +03:00
}
2019-11-24 06:27:02 +03:00
return res
2019-07-01 15:55:45 +03:00
}
// bind socket to port
2019-07-20 06:51:45 +03:00
pub fn (s Socket) bind(port int) ?int {
2020-05-16 17:12:23 +03:00
mut addr := C.sockaddr_in{}
2019-07-01 15:55:45 +03:00
addr.sin_family = s.family
addr.sin_port = C.htons(port)
2019-08-23 00:00:31 +03:00
addr.sin_addr.s_addr = C.htonl(C.INADDR_ANY)
2019-07-01 15:55:45 +03:00
size := 16 // sizeof(C.sockaddr_in)
2020-05-18 22:38:06 +03:00
tmp := voidptr(&addr)
skaddr := &C.sockaddr(tmp)
res := C.bind(s.sockfd, skaddr, size)
2019-07-20 06:51:45 +03:00
if res < 0 {
return error('net.bind: failed with $res')
2019-07-20 06:51:45 +03:00
}
return res
2019-07-01 15:55:45 +03:00
}
// put socket into passive mode and wait to receive
2019-07-20 06:51:45 +03:00
pub fn (s Socket) listen() ?int {
2019-07-01 15:55:45 +03:00
backlog := 128
2019-11-24 06:27:02 +03:00
res := C.listen(s.sockfd, backlog)
2019-07-20 06:51:45 +03:00
if res < 0 {
return error('net.listen: failed with $res')
2019-07-20 06:51:45 +03:00
}
2019-08-20 11:18:12 +03:00
$if debug {
println('listen res = $res')
}
2019-08-23 00:00:31 +03:00
return res
2019-07-01 15:55:45 +03:00
}
// put socket into passive mode with user specified backlog and wait to receive
2019-07-20 06:51:45 +03: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 06:51:45 +03:00
if res < 0 {
return error('net.listen_backlog: failed with $res')
2019-07-20 06:51:45 +03:00
}
2019-11-24 06:27:02 +03:00
return res
}
2019-07-01 15:55:45 +03:00
// helper method to create, bind, and listen given port number
2019-07-20 06:51:45 +03:00
pub fn listen(port int) ?Socket {
2019-08-20 11:18:12 +03:00
$if debug {
println('net.listen($port)')
}
2019-11-24 06:27:02 +03:00
s := new_socket(C.AF_INET, C.SOCK_STREAM, 0) or {
2019-07-20 06:51:45 +03:00
return error(err)
}
s.bind(port) or {
2019-07-20 06:51:45 +03:00
return error(err)
}
s.listen() or {
2019-07-20 06:51:45 +03:00
return error(err)
2019-06-30 17:11:55 +03:00
}
2019-07-01 15:55:45 +03:00
return s
2019-06-30 17:11:55 +03:00
}
2019-07-01 15:55:45 +03:00
// accept first connection request from socket queue
2019-07-20 06:51:45 +03:00
pub fn (s Socket) accept() ?Socket {
2019-08-20 11:18:12 +03:00
$if debug {
println('accept()')
}
2020-07-02 22:27:36 +03:00
addr := C.sockaddr{}
size := sizeof(addr)
sockfd := C.accept(s.sockfd, &addr, &size)
2019-07-01 15:55:45 +03:00
if sockfd < 0 {
return error('net.accept: failed with $sockfd')
2019-06-30 22:00:22 +03:00
}
2019-12-22 01:41:42 +03:00
c := Socket{
2019-07-01 15:55:45 +03:00
sockfd: sockfd
family: s.family
2020-05-16 17:12:23 +03:00
typ: s.typ
2019-07-01 15:55:45 +03:00
proto: s.proto
2019-06-30 22:00:22 +03:00
}
2019-07-01 15:55:45 +03:00
return c
}
2020-07-02 22:27:36 +03:00
pub fn (s Socket) peer_ip() ?string {
buf := [44]byte{}
2020-07-02 22:27:36 +03:00
peeraddr := C.sockaddr_in{}
speeraddr := sizeof(peeraddr)
ok := C.getpeername(s.sockfd, &C.sockaddr(&peeraddr), &speeraddr)
if ok == -1 {
return error('net.peer_ip: getpeername failed')
}
cstr := C.inet_ntop(C.AF_INET, &peeraddr.sin_addr, buf, sizeof(buf))
if cstr == 0 {
return error('net.peer_ip: inet_ntop failed')
}
return cstring_to_vstring(cstr)
}
2019-07-01 15:55:45 +03:00
// connect to given addrress and port
2019-07-20 06:51:45 +03:00
pub fn (s Socket) connect(address string, port int) ?int {
2020-05-16 17:12:23 +03:00
mut hints := C.addrinfo{}
hints.ai_family = s.family
2020-05-16 17:12:23 +03:00
hints.ai_socktype = s.typ
2019-08-23 00:00:31 +03:00
hints.ai_flags = C.AI_PASSIVE
hints.ai_protocol = s.proto
hints.ai_addrlen = 0
hints.ai_canonname = C.NULL
hints.ai_addr = C.NULL
hints.ai_next = C.NULL
2019-12-04 13:08:28 +03:00
info := &C.addrinfo(0)
2019-07-01 15:55:45 +03:00
sport := '$port'
info_res := C.getaddrinfo(address.str, sport.str, &hints, &info)
2019-07-01 15:55:45 +03:00
if info_res != 0 {
2020-05-16 17:12:23 +03:00
error_message := os.get_error_msg(error_code())
return error('net.connect: getaddrinfo failed "$error_message"')
2019-06-30 22:00:22 +03:00
}
2019-11-24 06:27:02 +03:00
res := C.connect(s.sockfd, info.ai_addr, info.ai_addrlen)
2019-07-20 06:51:45 +03:00
if res < 0 {
2020-05-16 17:12:23 +03:00
error_message := os.get_error_msg(error_code())
return error('net.connect: connect failed "$error_message"')
2019-07-20 06:51:45 +03:00
}
2019-11-24 06:27:02 +03:00
return res
2019-06-30 21:57:25 +03:00
}
2019-07-01 15:55:45 +03:00
// helper method to create socket and connect
2019-07-20 06:51:45 +03:00
pub fn dial(address string, port int) ?Socket {
2019-11-24 06:27:02 +03:00
s := new_socket(C.AF_INET, C.SOCK_STREAM, 0) or {
2019-07-20 06:51:45 +03:00
return error(err)
}
s.connect(address, port) or {
2019-07-20 06:51:45 +03:00
return error(err)
2019-07-01 15:55:45 +03:00
}
return s
2019-06-30 21:57:25 +03:00
}
// send data to socket (when you have a memory buffer)
pub fn (s Socket) send(buf byteptr, len int) ?int {
mut dptr := buf
mut dlen := len
for {
send_size := if dlen > s.max_single_send_size { s.max_single_send_size } else { dlen }
sbytes := C.send(s.sockfd, dptr, send_size, msg_nosignal)
2019-12-22 01:41:42 +03:00
if sbytes < 0 {
return error('net.send: failed with $sbytes')
}
dlen -= sbytes
2019-12-22 01:41:42 +03:00
if dlen <= 0 {
break
}
unsafe {
dptr += sbytes
}
}
return len
}
// send string data to socket (when you have a v string)
pub fn (s Socket) send_string(sdata string) ?int {
2019-12-22 01:41:42 +03:00
return s.send(sdata.str, sdata.len)
2019-07-02 18:25:21 +03:00
}
// receive string data from socket. NB: you are responsible for freeing the returned byteptr
2020-05-16 17:12:23 +03:00
pub fn (s Socket) recv(bufsize int) (byteptr, int) {
mut buf := byteptr(0)
2020-05-16 17:12:23 +03:00
unsafe {
buf = malloc(bufsize)
}
2019-11-24 06:27:02 +03:00
res := C.recv(s.sockfd, buf, bufsize, 0)
2020-05-16 17:12:23 +03:00
return buf, res
2019-07-02 18:25:21 +03:00
}
2019-08-21 20:04:06 +03:00
// TODO: remove cread/2 and crecv/2 when the Go net interface is done
2019-12-22 01:41:42 +03:00
pub fn (s Socket) cread(buffer byteptr, buffersize int) int {
2019-11-24 06:27:02 +03:00
return C.read(s.sockfd, buffer, buffersize)
2019-08-21 20:04:06 +03:00
}
2019-12-22 01:41:42 +03:00
// 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.
2020-05-18 22:38:06 +03:00
pub fn (s Socket) crecv(buffer voidptr, buffersize int) int {
return C.recv(s.sockfd, byteptr(buffer), buffersize, 0)
2019-08-21 20:04:06 +03:00
}
2019-07-01 15:55:45 +03:00
// shutdown and close socket
2019-07-20 06:51:45 +03:00
pub fn (s Socket) close() ?int {
mut shutdown_res := 0
2019-07-14 00:29:00 +03:00
$if windows {
2019-08-23 00:00:31 +03:00
shutdown_res = C.shutdown(s.sockfd, C.SD_BOTH)
2019-12-22 01:41:42 +03:00
} $else {
2019-08-23 00:00:31 +03:00
shutdown_res = C.shutdown(s.sockfd, C.SHUT_RDWR)
2019-07-14 00:29:00 +03:00
}
2019-07-20 06:51:45 +03:00
// TODO: should shutdown throw an error? close will
// continue even if shutdown failed
2019-12-22 01:41:42 +03:00
// if shutdown_res < 0 {
// return error('net.close: shutdown failed with $shutdown_res')
// }
2019-07-20 06:51:45 +03:00
mut res := 0
2019-07-14 00:29:00 +03:00
$if windows {
2019-07-20 06:51:45 +03:00
res = C.closesocket(s.sockfd)
2019-12-22 01:41:42 +03:00
} $else {
2019-07-20 06:51:45 +03:00
res = C.close(s.sockfd)
}
if res < 0 {
return error('net.close: failed with $res')
2019-07-14 00:29:00 +03:00
}
2019-07-01 15:55:45 +03:00
return 0
}
2019-07-29 19:21:36 +03:00
pub const (
crlf = '\r\n'
max_read = 400
msg_peek = 0x02
2019-08-23 00:00:31 +03:00
)
2020-05-16 17:12:23 +03:00
// write - write a string with CRLF after it over the socket s
pub fn (s Socket) write(str string) ?int {
line := '$str$crlf'
2020-05-22 18:36:09 +03:00
res := C.send(s.sockfd, line.str, line.len, msg_nosignal)
2019-12-22 01:41:42 +03:00
if res < 0 {
return error('net.write: failed with $res')
}
return res
2019-07-29 19:21:36 +03:00
}
2019-08-23 00:00:31 +03:00
// read_line - retrieves a line from the socket s (i.e. a string ended with \n)
2019-07-29 19:21:36 +03:00
pub fn (s Socket) read_line() string {
mut buf := [max_read]byte{} // where C.recv will store the network data
mut res := '' // The final result, including the ending \n.
for {
mut line := '' // The current line. Can be a partial without \n in it.
n := C.recv(s.sockfd, buf, max_read - 1, msg_peek)
2019-12-22 01:41:42 +03:00
if n == -1 {
return res
}
if n == 0 {
return res
}
buf[n] = `\0`
mut eol_idx := -1
2020-05-16 17:12:23 +03:00
for i in 0 .. n {
if int(buf[i]) == `\n` {
eol_idx = i
// Ensure that tos_clone(buf) later,
// will return *only* the first line (including \n),
// and ignore the rest
2019-12-22 01:41:42 +03:00
buf[i + 1] = `\0`
break
2019-08-20 11:18:12 +03:00
}
}
2020-05-18 22:38:06 +03:00
bufbp := byteptr(buf)
line = tos_clone(bufbp)
if eol_idx > 0 {
// At this point, we are sure that recv returned valid data,
// that contains *at least* one line.
// Ensure that the block till the first \n (including it)
// is removed from the socket's receive queue, so that it does
// not get read again.
2019-12-22 01:41:42 +03:00
C.recv(s.sockfd, buf, eol_idx + 1, 0)
res += line
break
}
// recv returned a buffer without \n in it .
C.recv(s.sockfd, buf, n, 0)
res += line
res += crlf
break
}
return res
2019-07-29 19:21:36 +03:00
}
2019-12-07 00:44:22 +03:00
// TODO
pub fn (s Socket) read_all() string {
mut buf := [max_read]byte{} // where C.recv will store the network data
2019-12-07 00:44:22 +03:00
mut res := '' // The final result, including the ending \n.
for {
n := C.recv(s.sockfd, buf, max_read - 1, 0)
2019-12-22 01:41:42 +03:00
if n == -1 {
return res
}
if n == 0 {
return res
}
2020-05-18 22:38:06 +03:00
bufbp := byteptr(buf)
res += tos_clone(bufbp)
2019-12-07 00:44:22 +03:00
}
return res
}
pub fn (s Socket) get_port() int {
2020-05-16 17:12:23 +03:00
mut addr := C.sockaddr_in{}
size := 16 // sizeof(sockaddr_in)
2020-05-18 22:38:06 +03:00
tmp := voidptr(&addr)
skaddr := &C.sockaddr(tmp)
C.getsockname(s.sockfd, skaddr, &size)
2019-11-24 06:27:02 +03:00
return C.ntohs(addr.sin_port)
}