1 /* 2 * DSFML - The Simple and Fast Multimedia Library for D 3 * 4 * Copyright (c) 2013 - 2017 Jeremy DeHaan (dehaan.jeremiah@gmail.com) 5 * 6 * This software is provided 'as-is', without any express or implied warranty. 7 * In no event will the authors be held liable for any damages arising from the 8 * use of this software. 9 * 10 * Permission is granted to anyone to use this software for any purpose, 11 * including commercial applications, and to alter it and redistribute it 12 * freely, subject to the following restrictions: 13 * 14 * 1. The origin of this software must not be misrepresented; you must not claim 15 * that you wrote the original software. If you use this software in a product, 16 * an acknowledgment in the product documentation would be appreciated but is 17 * not required. 18 * 19 * 2. Altered source versions must be plainly marked as such, and must not be 20 * misrepresented as being the original software. 21 * 22 * 3. This notice may not be removed or altered from any source distribution 23 */ 24 25 /** 26 * TCP is a connected protocol, which means that a TCP socket can only 27 * communicate with the host it is connected to. 28 * 29 * It can't send or receive anything if it is not connected. 30 * 31 * The TCP protocol is reliable but adds a slight overhead. It ensures that your 32 * data will always be received in order and without errors (no data corrupted, 33 * lost or duplicated). 34 * 35 * When a socket is connected to a remote host, you can retrieve informations 36 * about this host with the `getRemoteAddress` and `getRemotePort` functions. 37 * 38 * You can also get the local port to which the socket is bound (which is 39 * automatically chosen when the socket is connected), with the `getLocalPort` 40 * function. 41 * 42 * Sending and receiving data can use either the low-level or the high-level 43 * functions. The low-level functions process a raw sequence of bytes, and 44 * cannot ensure that one call to Send will exactly match one call to Receive at 45 * the other end of the socket. 46 * 47 * The high-level interface uses packets (see $(PACKET_LINK)), which are easier 48 * to use and provide more safety regarding the data that is exchanged. You can 49 * look at the $(PACKET_LINK) class to get more details about how they work. 50 * 51 * The socket is automatically disconnected when it is destroyed, but if you 52 * want to explicitely close the connection while the socket instance is still 53 * alive, you can call disconnect. 54 * 55 * Example: 56 * --- 57 * // ----- The client ----- 58 * 59 * // Create a socket and connect it to 192.168.1.50 on port 55001 60 * auto socket = new TcpSocket(); 61 * socket.connect("192.168.1.50", 55001); 62 * 63 * // Send a message to the connected host 64 * string message = "Hi, I am a client"; 65 * socket.send(message); 66 * 67 * // Receive an answer from the server 68 * char[1024] buffer; 69 * size_t received = 0; 70 * socket.receive(buffer, received); 71 * writeln("The server said: ", buffer[0 .. received]); 72 * 73 * // ----- The server ----- 74 * 75 * // Create a listener to wait for incoming connections on port 55001 76 * auto listener = TcpListener(); 77 * listener.listen(55001); 78 * 79 * // Wait for a connection 80 * auto socket = new TcpSocket(); 81 * listener.accept(socket); 82 * writeln("New client connected: ", socket.getRemoteAddress()); 83 * 84 * // Receive a message from the client 85 * char[1024] buffer; 86 * size_t received = 0; 87 * socket.receive(buffer, received); 88 * writeln("The client said: ", buffer[0 .. received]); 89 * 90 * // Send an answer 91 * string message = "Welcome, client"; 92 * socket.send(message); 93 * --- 94 * 95 * See_Also: 96 * $(SOCKET_LINK), $(UDPSOCKET_LINK), $(PACKET_LINK) 97 */ 98 module dsfml.network.tcpsocket; 99 100 import core.time; 101 102 import dsfml.network.ipaddress; 103 import dsfml.network.packet; 104 import dsfml.network.socket; 105 106 import dsfml.system.err; 107 108 /** 109 * Specialized socket using the TCP protocol. 110 */ 111 class TcpSocket:Socket 112 { 113 package sfTcpSocket* sfPtr; 114 115 /// Default constructor. 116 this() 117 { 118 sfPtr = sfTcpSocket_create(); 119 } 120 121 /// Destructor. 122 ~this() 123 { 124 import dsfml.system.config; 125 mixin(destructorOutput); 126 sfTcpSocket_destroy(sfPtr); 127 } 128 129 /** 130 * Get the port to which the socket is bound locally. 131 * 132 * If the socket is not connected, this function returns 0. 133 * 134 * Returns: Port to which the socket is bound. 135 */ 136 ushort getLocalPort() 137 { 138 return sfTcpSocket_getLocalPort(sfPtr); 139 } 140 141 /** 142 * Get the address of the connected peer. 143 * 144 * It the socket is not connected, this function returns `IpAddress.None`. 145 * 146 * Returns: Address of the remote peer. 147 */ 148 IpAddress getRemoteAddress() 149 { 150 IpAddress temp; 151 152 sfTcpSocket_getRemoteAddress(sfPtr,&temp); 153 154 return temp; 155 } 156 157 /** 158 * Get the port of the connected peer to which the socket is connected. 159 * 160 * If the socket is not connected, this function returns 0. 161 * 162 * Returns: Remote port to which the socket is connected. 163 */ 164 ushort getRemotePort() 165 { 166 return sfTcpSocket_getRemotePort(sfPtr); 167 } 168 169 /** 170 * Set the blocking state of the socket. 171 * 172 * In blocking mode, calls will not return until they have completed their 173 * task. For example, a call to `receive` in blocking mode won't return 174 * until some data was actually received. 175 * 176 * In non-blocking mode, calls will always return immediately, using the 177 * return code to signal whether there was data available or not. By 178 * default, all sockets are blocking. 179 * 180 * Params: 181 * blocking = true to set the socket as blocking, false for non-blocking 182 */ 183 void setBlocking(bool blocking) 184 { 185 sfTcpSocket_setBlocking(sfPtr, blocking); 186 } 187 188 /** 189 * Connect the socket to a remote peer. 190 * 191 * In blocking mode, this function may take a while, especially if the 192 * remote peer is not reachable. The last parameter allows you to stop 193 * trying to connect after a given timeout. 194 * 195 * If the socket was previously connected, it is first disconnected. 196 * 197 * Params: 198 * host = Address of the remote peer 199 * port = Port of the remote peer 200 * timeout = Optional maximum time to wait 201 * 202 * Returns: Status code. 203 */ 204 Status connect(IpAddress host, ushort port, Duration timeout = Duration.zero()) 205 { 206 return sfTcpSocket_connect(sfPtr, &host, port, timeout.total!"usecs"); 207 } 208 209 /** 210 * Disconnect the socket from its remote peer. 211 * 212 * This function gracefully closes the connection. If the socket is not 213 * connected, this function has no effect. 214 */ 215 void disconnect() 216 { 217 sfTcpSocket_disconnect(sfPtr); 218 } 219 220 /** 221 * Tell whether the socket is in blocking or non-blocking mode. 222 * 223 * Returns: true if the socket is blocking, false otherwise. 224 */ 225 bool isBlocking() const 226 { 227 return (sfTcpSocket_isBlocking(sfPtr)); 228 } 229 230 /** 231 * Send raw data to the remote peer. 232 * 233 * This function will fail if the socket is not connected. 234 * 235 * Params: 236 * data = Sequence of bytes to send 237 * 238 * Returns: Status code. 239 */ 240 Status send(const(void)[] data) 241 { 242 import dsfml.system..string; 243 244 Status toReturn = sfTcpSocket_send(sfPtr, data.ptr, data.length); 245 err.write(dsfml.system..string.toString(sfErr_getOutput())); 246 return toReturn; 247 } 248 249 /** 250 * Send a formatted packet of data to the remote peer. 251 * 252 * This function will fail if the socket is not connected. 253 * 254 * Params: 255 * packet = Packet to send 256 * 257 * Returns: Status code. 258 */ 259 Status send(Packet packet) 260 { 261 import std.stdio; 262 //temporary packet to be removed on function exit 263 scope SfPacket temp = new SfPacket(); 264 265 //getting packet's "to send" data 266 temp.append(packet.onSend()); 267 268 //send the data 269 return sfTcpSocket_sendPacket(sfPtr, temp.sfPtr); 270 } 271 272 /** 273 * Receive raw data from the remote peer. 274 * 275 * In blocking mode, this function will wait until some bytes are actually 276 * received. This function will fail if the socket is not connected. 277 * 278 * Params: 279 * data = Array to fill with the received bytes 280 * sizeReceived = This variable is filled with the actual number of bytes 281 received 282 * 283 * Returns: Status code. 284 */ 285 Status receive(void[] data , out size_t sizeReceived) 286 { 287 return sfTcpSocket_receive(sfPtr, data.ptr, data.length, &sizeReceived); 288 } 289 290 /** 291 * Receive a formatted packet of data from the remote peer. 292 * 293 * In blocking mode, this function will wait until the whole packet has been 294 * received. This function will fail if the socket is not connected. 295 * 296 * Params: 297 * packet = Packet to fill with the received data 298 * 299 * Returns: Status code. 300 */ 301 Status receive(Packet packet) 302 { 303 //temporary packet to be removed on function exit 304 scope SfPacket temp = new SfPacket(); 305 306 //get the sent data 307 Status status = sfTcpSocket_receivePacket(sfPtr, temp.sfPtr); 308 309 //put data into the packet so that it can process it first if it wants. 310 packet.onRecieve(temp.getData()); 311 312 return status; 313 } 314 } 315 316 unittest 317 { 318 //TODO: Expand to use more methods in TcpSocket 319 version(DSFML_Unittest_Network) 320 { 321 import std.stdio; 322 import dsfml.network.tcplistener; 323 324 writeln("Unittest for Tcp Socket"); 325 326 //socket connecting to server 327 auto clientSocket = new TcpSocket(); 328 329 //listener looking for new sockets 330 auto listener = new TcpListener(); 331 listener.listen(55003); 332 333 //get our client socket to connect to the server 334 clientSocket.connect(IpAddress.LocalHost, 55003); 335 336 //packet to send data 337 auto sendPacket = new Packet(); 338 339 //Packet to receive data 340 auto receivePacket = new Packet(); 341 342 //socket on the server side connected to the client's socket 343 auto serverSocket = new TcpSocket(); 344 345 //accepts a new connection and binds it to the socket in the parameter 346 listener.accept(serverSocket); 347 348 string temp = "I'm sending you stuff!"; 349 350 //Let's greet the server! 351 //sendPacket.writeString("Hello, I'm a client!"); 352 //clientSocket.send(sendPacket); 353 354 clientSocket.send(temp); 355 356 //And get the data on the server side 357 //serverSocket.receive(receivePacket); 358 359 char[1024] temp2; 360 size_t received; 361 362 serverSocket.receive(temp2, received); 363 364 //What did we get from the client? 365 writeln("Gotten from client: ", cast(string)temp2[0..received]); 366 367 //clear the packets to send/get new information 368 sendPacket.clear(); 369 receivePacket.clear(); 370 371 //Respond back to the client 372 sendPacket.write("Hello, I'm your server."); 373 374 serverSocket.send(sendPacket); 375 376 clientSocket.receive(receivePacket); 377 378 string message; 379 receivePacket.read!string(message); 380 writeln("Gotten from server: ", message); 381 382 clientSocket.disconnect(); 383 writeln(); 384 } 385 } 386 387 package extern(C): 388 389 struct sfTcpSocket; 390 391 //Create a new TCP socket 392 sfTcpSocket* sfTcpSocket_create(); 393 394 395 //Destroy a TCP socket 396 void sfTcpSocket_destroy(sfTcpSocket* socket); 397 398 399 //Set the blocking state of a TCP listener 400 void sfTcpSocket_setBlocking(sfTcpSocket* socket, bool blocking); 401 402 403 //Tell whether a TCP socket is in blocking or non-blocking mode 404 bool sfTcpSocket_isBlocking(const(sfTcpSocket)* socket); 405 406 407 //Get the port to which a TCP socket is bound locally 408 ushort sfTcpSocket_getLocalPort(const(sfTcpSocket)* socket); 409 410 411 //Get the address of the connected peer of a TCP socket 412 void sfTcpSocket_getRemoteAddress(const(sfTcpSocket)* socket, IpAddress* ipAddress); 413 414 415 //Get the port of the connected peer to which a TCP socket is connected 416 ushort sfTcpSocket_getRemotePort(const(sfTcpSocket)* socket); 417 418 419 //Connect a TCP socket to a remote peer 420 Socket.Status sfTcpSocket_connect(sfTcpSocket* socket, IpAddress* host, ushort port, long timeout); 421 422 423 //Disconnect a TCP socket from its remote peer 424 void sfTcpSocket_disconnect(sfTcpSocket* socket); 425 426 427 //Send raw data to the remote peer of a TCP socket 428 Socket.Status sfTcpSocket_send(sfTcpSocket* socket, const void* data, size_t size); 429 430 431 //Receive raw data from the remote peer of a TCP socket 432 Socket.Status sfTcpSocket_receive(sfTcpSocket* socket, void* data, size_t maxSize, size_t* sizeReceived); 433 434 435 //Send a formatted packet of data to the remote peer of a TCP socket 436 Socket.Status sfTcpSocket_sendPacket(sfTcpSocket* socket, sfPacket* packet); 437 438 439 //Receive a formatted packet of data from the remote peer 440 Socket.Status sfTcpSocket_receivePacket(sfTcpSocket* socket, sfPacket* packet); 441 442 const(char)* sfErr_getOutput();