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