| 52 | | /// Send data over the socket. |
|---|
| 53 | | /// |
|---|
| 54 | | /// Params: |
|---|
| 55 | | /// buf = the bytes to send |
|---|
| 56 | | void sendData(void[] buf) |
|---|
| 57 | | in |
|---|
| 58 | | { |
|---|
| 59 | | assert (buf.length > 0); |
|---|
| 60 | | } |
|---|
| 61 | | body |
|---|
| 62 | | { |
|---|
| 63 | | // At end_time, we bail. |
|---|
| 64 | | Time end_time = Clock.now + this.timeout; |
|---|
| 65 | | |
|---|
| 66 | | // Set up a SocketSet so we can use select() - it's pretty efficient. |
|---|
| 67 | | SocketSet set = new SocketSet(); |
|---|
| 68 | | scope (exit) |
|---|
| 69 | | delete set; |
|---|
| 70 | | |
|---|
| 71 | | size_t pos = 0; |
|---|
| 72 | | while (Clock.now < end_time) |
|---|
| 73 | | { |
|---|
| 74 | | set.reset(); |
|---|
| 75 | | set.add(this.socket); |
|---|
| 76 | | |
|---|
| 77 | | // Can we write yet, can we write yet? |
|---|
| 78 | | int code = Socket.select(null, set, null, this.timeout); |
|---|
| 79 | | if (code == -1 || code == 0) |
|---|
| 80 | | break; |
|---|
| 81 | | |
|---|
| 82 | | // Send it (or as much as possible!) |
|---|
| 83 | | int delta = this.socket.send(buf[pos .. buf.length]); |
|---|
| 84 | | if (delta == -1) |
|---|
| 85 | | break; |
|---|
| 86 | | |
|---|
| 87 | | pos += delta; |
|---|
| 88 | | if (pos >= buf.length) |
|---|
| 89 | | break; |
|---|
| 90 | | } |
|---|
| 91 | | |
|---|
| 92 | | // If we didn't send everything, we're dead in the water. |
|---|
| 93 | | if (pos != buf.length) |
|---|
| 94 | | exception ("CLIENT: Timeout when sending command"); |
|---|
| 95 | | } |
|---|
| 96 | | |
|---|
| 97 | | /// Read a CRLF terminated line from the socket. |
|---|
| 98 | | /// |
|---|
| 99 | | /// Returns: the line read |
|---|
| 100 | | char[] readLine() |
|---|
| 101 | | { |
|---|
| 102 | | // Figure, first, how long we're allowed to take. |
|---|
| 103 | | Time end_time = Clock.now + this.timeout; |
|---|
| 104 | | |
|---|
| 105 | | // An overall buffer and a one-char buffer. |
|---|
| 106 | | char[] buffer; |
|---|
| 107 | | char[1] buf; |
|---|
| 108 | | size_t buffer_pos = 0; |
|---|
| 109 | | |
|---|
| 110 | | // Push a byte onto the buffer. |
|---|
| 111 | | void push_byte() |
|---|
| 112 | | { |
|---|
| 113 | | // Lines aren't usually that long. Allocate in blocks of 16 bytes. |
|---|
| 114 | | if (buffer.length <= buffer_pos) |
|---|
| 115 | | buffer.length = buffer.length + 16; |
|---|
| 116 | | |
|---|
| 117 | | buffer[buffer_pos++] = buf[0]; |
|---|
| 118 | | } |
|---|
| 119 | | |
|---|
| 120 | | // Get the resultant buffer. |
|---|
| 121 | | char[] get_buffer() |
|---|
| 122 | | { |
|---|
| 123 | | return buffer[0 .. buffer_pos]; |
|---|
| 124 | | } |
|---|
| 125 | | |
|---|
| 126 | | // Now the socket set for selecting purposes. |
|---|
| 127 | | SocketSet set = new SocketSet(); |
|---|
| 128 | | scope (exit) |
|---|
| 129 | | delete set; |
|---|
| 130 | | |
|---|
| 131 | | while (Clock.now < end_time) |
|---|
| 132 | | { |
|---|
| 133 | | set.reset(); |
|---|
| 134 | | set.add(this.socket); |
|---|
| 135 | | |
|---|
| 136 | | // Try to read from the socket. |
|---|
| 137 | | int code = Socket.select(set, null, null, this.timeout); |
|---|
| 138 | | if (code == -1 || code == 0) |
|---|
| 139 | | break; |
|---|
| 140 | | |
|---|
| 141 | | // Okay, now we're ready. Read in the measly byte. |
|---|
| 142 | | int delta = this.socket.receive(buf); |
|---|
| 143 | | if (delta != 1) |
|---|
| 144 | | break; |
|---|
| 145 | | |
|---|
| 146 | | if (buf == "\r") |
|---|
| 147 | | continue; |
|---|
| 148 | | else if (buf == "\n") |
|---|
| 149 | | break; |
|---|
| 150 | | |
|---|
| 151 | | push_byte(); |
|---|
| 152 | | } |
|---|
| 153 | | |
|---|
| 154 | | return get_buffer(); |
|---|
| 155 | | } |
|---|
| 156 | | |
|---|
| 157 | | /// Find a server which is listening on the specified port. |
|---|
| 158 | | /// |
|---|
| 159 | | /// Params: |
|---|
| 160 | | /// hostname = the hostname to lookup and connect to |
|---|
| 161 | | /// port = the port to connect on |
|---|
| 162 | | Socket findAvailableServer(char[] hostname, int port) |
|---|
| 163 | | { |
|---|
| 164 | | // First we need to get a list of IP addresses. |
|---|
| 165 | | auto host = new NetHost(); |
|---|
| 166 | | scope (exit) |
|---|
| 167 | | delete host; |
|---|
| 168 | | |
|---|
| 169 | | // Try to resolve the actual address for this hostname. |
|---|
| 170 | | host.getHostByName(hostname); |
|---|
| 171 | | scope (exit) |
|---|
| 172 | | delete host.addrList; |
|---|
| 173 | | |
|---|
| 174 | | // None were found... darn. |
|---|
| 175 | | if (host.addrList.length == 0) |
|---|
| 176 | | throw new AddressException("Unable to resolve host '" ~ hostname ~ "'"); |
|---|
| 177 | | |
|---|
| 178 | | // Get all the sockets ready (or just one if there's just one address.) |
|---|
| 179 | | Socket[] sockets = new Socket[host.addrList.length]; |
|---|
| 180 | | scope (exit) |
|---|
| 181 | | delete sockets; |
|---|
| 182 | | |
|---|
| 183 | | // And now just connect to all of them. |
|---|
| 184 | | for (int i = 0; i < host.addrList.length; i++) |
|---|
| 185 | | { |
|---|
| 186 | | sockets[i] = new Socket(AddressFamily.INET, SocketType.STREAM, ProtocolType.TCP); |
|---|
| 187 | | sockets[i].blocking = false; |
|---|
| 188 | | |
|---|
| 189 | | Address addr = new IPv4Address(host.addrList[i], cast(ushort) port); |
|---|
| 190 | | scope (exit) |
|---|
| 191 | | delete addr; |
|---|
| 192 | | |
|---|
| 193 | | // Start trying to connect as soon as possible. |
|---|
| 194 | | sockets[i].connect(addr); |
|---|
| 195 | | } |
|---|
| 196 | | |
|---|
| 197 | | // Set up some stuff so we can select through the hosts. |
|---|
| 198 | | SocketSet set = new SocketSet(); |
|---|
| 199 | | this.socket = null; |
|---|
| 200 | | |
|---|
| 201 | | scope (exit) |
|---|
| 202 | | delete set; |
|---|
| 203 | | |
|---|
| 204 | | // Wait until we find a good socket... |
|---|
| 205 | | while (this.socket is null) |
|---|
| 206 | | { |
|---|
| 207 | | set.reset(); |
|---|
| 208 | | foreach (Socket s; sockets) |
|---|
| 209 | | set.add(s); |
|---|
| 210 | | |
|---|
| 211 | | // Anyone available? |
|---|
| 212 | | int code = Socket.select(null, set, null, this.timeout); |
|---|
| 213 | | if (code == -1 || code == 0) |
|---|
| 214 | | break; |
|---|
| 215 | | |
|---|
| 216 | | // Now we have to check to find a good socket, and break out if we find one. |
|---|
| 217 | | foreach (Socket s; sockets) |
|---|
| 218 | | if (set.isSet(s)) |
|---|
| 219 | | { |
|---|
| 220 | | this.socket = s; |
|---|
| 221 | | break; |
|---|
| 222 | | } |
|---|
| 223 | | } |
|---|
| 224 | | |
|---|
| 225 | | // Close the other sockets (or all on error.) |
|---|
| 226 | | foreach (Socket s; sockets) |
|---|
| 227 | | if (s !is this.socket) |
|---|
| 228 | | { |
|---|
| 229 | | s.shutdown(SocketShutdown.BOTH); |
|---|
| 230 | | s.detach(); |
|---|
| 231 | | |
|---|
| 232 | | delete s; |
|---|
| 233 | | } |
|---|
| 234 | | |
|---|
| 235 | | // No socket, no data. Can't do anything about that. |
|---|
| 236 | | if (this.socket is null) |
|---|
| 237 | | { |
|---|
| 238 | | char[10] tmp; |
|---|
| 239 | | exception ("CLIENT: Unable to connect within the specified time limit (" ~ Integer.itoa(tmp, cast(uint) this.timeout.millis) ~ " ms.)"); |
|---|
| 240 | | } |
|---|
| 241 | | |
|---|
| 242 | | // Make it blocking again, because that's the norm. |
|---|
| 243 | | this.socket.blocking = true; |
|---|
| 244 | | |
|---|
| 245 | | return this.socket; |
|---|
| 246 | | } |
|---|