/
Socket.ooc
182 lines (140 loc) · 4.83 KB
/
Socket.ooc
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
import net/[berkeley, Exceptions]
/**
Common base for all socket types.
*/
Socket: abstract class {
descriptor: Int
family, type, protocol: Int
connected? := false
listening? := false
hasData? := false
init: func ~sock(=family, =type, =protocol) {
descriptor = socket(family, type, protocol)
if (descriptor == -1) {
SocketError new("Failed to create socket") throw()
}
}
init: func ~descriptor(=family, =type, =protocol, =descriptor) {}
close: func {
result : Int
version(windows) {
result = closesocket(descriptor)
}
version (unix || apple) {
result = close(descriptor)
}
if (result == -1) {
SocketError new("Failed to close socket") throw()
}
connected? = false
}
ioctl: func(request: Int, arg: Pointer) {
//TODO: abstract this into version blocks to support windows
rt := ioctl(descriptor, request, arg)
if(rt != 0) {
SocketError new() throw()
}
}
/**
Returns the number of bytes currently waiting to be consumed. This function is
NON BLOCKING and therefore don't rely on it for halting execution while data is
recieved. You may want to look at 'wait()'
*/
available: func -> Int {
result: Int
ioctl(FIONREAD, result&)
return result;
}
/**
Waits on the socket for data to be recieved or a timeout. If sucessful the return value
is the number of bytes waiting to be consumed.
:param timeoutSec: The timeout in seconds before wait is returned without data available.
:param timeoutuSec: The timeout in micro seconds before wait is returned without data available.
:throws: A TimeoutError if the wait times out before data becomes available
*/
wait: func(timeoutSec, timeoutuSec: Int) -> Int {
timeout : TimeVal
timeout tv_sec = timeoutSec
timeout tv_usec = timeoutuSec
descriptors : FdSet
descriptors zero()
descriptors set(descriptor)
select(descriptor+1, descriptors&, null, null, timeout&)
if (!descriptors set?(descriptor))
TimeoutError new("Wait on socket timedout.") throw()
return available()
}
/**
Waits on the socket for data to be recieved or a timeout. If sucessful the return value
is the number of bytes waiting to be consumed.
:param timeoutSec: The timeout in seconds before wait is returned without data available.
:throws: A TimeoutError if the wait times out before data becomes available
*/
wait: func ~justSeconds(timeoutSec: Int) -> Int {
return wait(timeoutSec, 0)
}
/**
Sets the socket to non-blocking mode
*/
setNonBlocking: func -> Int {
flags := currentFlags()
result := fcntl(descriptor, SocketControls SET_SOCKET_FLAGS, flags | SocketControls NON_BLOCKING)
if (result < 0)
SocketError new() throw()
return result
}
/**
Sets the socket to blocking mode
*/
setBlocking: func -> Int {
flags := currentFlags()
result := fcntl(descriptor, SocketControls SET_SOCKET_FLAGS, flags & ~(SocketControls NON_BLOCKING))
if (result < 0)
SocketError new() throw()
return result
}
/**
Retrieves the current socket flags from the underlying socket
*/
currentFlags: func -> Int {
flags := fcntl(descriptor, SocketControls GET_SOCKET_FLAGS, 0)
if (flags < 0)
SocketError new() throw()
return flags
}
/**
Enable/disable the reuse of the same address
*/
setReuseAddr: func (reuse: Bool) {
option := reuse ? 1 : 0
setsockopt(descriptor, SOL_SOCKET, SO_REUSEADDR, option&, option class size)
}
}
AddressFamily: cover {
UNSPEC: extern(AF_UNSPEC) static Int
IP4: extern(AF_INET) static Int
IP6: extern(AF_INET6) static Int
}
SocketType: cover {
STREAM: extern(SOCK_STREAM) static Int
DATAGRAM: extern(SOCK_DGRAM) static Int
}
SocketMsgFlags: cover {
OOB: extern(MSG_OOB) static Int
DONTROUTE: extern(MSG_DONTROUTE) static Int
DONTWAIT: extern(MSG_DONTWAIT) static Int
NOSIGNAL: extern(MSG_NOSIGNAL) static Int
PEEK: extern(MSG_PEEK) static Int
WAITALL: extern(MSG_WAITALL) static Int
}
SocketShutdownOptions: cover {
NO_MORE_RECIEVES: extern(SHUT_RD) static Int
NO_MORE_SENDS: extern(SHUT_WR) static Int
NO_MORE_SENDS_OR_RECIEVES: extern(SHUT_RDWR) static Int
}
SocketControls: cover {
SET_SOCKET_FLAGS: extern(F_SETFL) static Int
GET_SOCKET_FLAGS: extern(F_GETFL) static Int
NON_BLOCKING: extern(O_NONBLOCK) static Int
ASYNCHRONOUS: extern(O_ASYNC) static Int // this probably shoulden't be implemented as it's badly supported
}