SFML - Simple and Fast Multimedia Library
Main Page
Namespaces
Classes
Files
File List
SocketTCP.cpp00001
00002 //
00003 // SFML - Simple and Fast Multimedia Library
00004 // Copyright (C) 2007-2009 Laurent Gomila (laurent.gom@gmail.com)
00005 //
00006 // This software is provided 'as-is', without any express or implied warranty.
00007 // In no event will the authors be held liable for any damages arising from the use of this software.
00008 //
00009 // Permission is granted to anyone to use this software for any purpose,
00010 // including commercial applications, and to alter it and redistribute it freely,
00011 // subject to the following restrictions:
00012 //
00013 // 1. The origin of this software must not be misrepresented;
00014 // you must not claim that you wrote the original software.
00015 // If you use this software in a product, an acknowledgment
00016 // in the product documentation would be appreciated but is not required.
00017 //
00018 // 2. Altered source versions must be plainly marked as such,
00019 // and must not be misrepresented as being the original software.
00020 //
00021 // 3. This notice may not be removed or altered from any source distribution.
00022 //
00024
00026 // Headers
00028 #include <SFML/Network/SocketTCP.hpp>
00029 #include <SFML/Network/IPAddress.hpp>
00030 #include <SFML/Network/Packet.hpp>
00031 #include <SFML/Network/SocketHelper.hpp>
00032 #include <algorithm>
00033 #include <iostream>
00034 #include <string.h>
00035
00036
00037 #ifdef _MSC_VER
00038 #pragma warning(disable : 4127) // "conditional expression is constant" generated by the FD_SET macro
00039 #endif
00040
00041
00042 namespace sf
00043 {
00047 SocketTCP::SocketTCP()
00048 {
00049 Create(SocketHelper::InvalidSocket());
00050 }
00051
00052
00056 void SocketTCP::SetBlocking(bool Blocking)
00057 {
00058 // Make sure our socket is valid
00059 if (!IsValid())
00060 Create();
00061
00062 SocketHelper::SetBlocking(mySocket, Blocking);
00063 myIsBlocking = Blocking;
00064 }
00065
00066
00070 Socket::Status SocketTCP::Connect(unsigned short Port, const IPAddress& HostAddress, float Timeout)
00071 {
00072 // Make sure our socket is valid
00073 if (!IsValid())
00074 Create();
00075
00076 // Build the host address
00077 sockaddr_in SockAddr;
00078 memset(SockAddr.sin_zero, 0, sizeof(SockAddr.sin_zero));
00079 SockAddr.sin_addr.s_addr = inet_addr(HostAddress.ToString().c_str());
00080 SockAddr.sin_family = AF_INET;
00081 SockAddr.sin_port = htons(Port);
00082
00083 if (Timeout <= 0)
00084 {
00085 // ----- We're not using a timeout : just try to connect -----
00086
00087 if (connect(mySocket, reinterpret_cast<sockaddr*>(&SockAddr), sizeof(SockAddr)) == -1)
00088 {
00089 // Failed to connect
00090 return SocketHelper::GetErrorStatus();
00091 }
00092
00093 // Connection succeeded
00094 return Socket::Done;
00095 }
00096 else
00097 {
00098 // ----- We're using a timeout : we'll need a few tricks to make it work -----
00099
00100 // Save the previous blocking state
00101 bool IsBlocking = myIsBlocking;
00102
00103 // Switch to non-blocking to enable our connection timeout
00104 if (IsBlocking)
00105 SetBlocking(false);
00106
00107 // Try to connect to host
00108 if (connect(mySocket, reinterpret_cast<sockaddr*>(&SockAddr), sizeof(SockAddr)) >= 0)
00109 {
00110 // We got instantly connected! (it may no happen a lot...)
00111 return Socket::Done;
00112 }
00113
00114 // Get the error status
00115 Socket::Status Status = SocketHelper::GetErrorStatus();
00116
00117 // If we were in non-blocking mode, return immediatly
00118 if (!IsBlocking)
00119 return Status;
00120
00121 // Otherwise, wait until something happens to our socket (success, timeout or error)
00122 if (Status == Socket::NotReady)
00123 {
00124 // Setup the selector
00125 fd_set Selector;
00126 FD_ZERO(&Selector);
00127 FD_SET(mySocket, &Selector);
00128
00129 // Setup the timeout
00130 timeval Time;
00131 Time.tv_sec = static_cast<long>(Timeout);
00132 Time.tv_usec = (static_cast<long>(Timeout * 1000) % 1000) * 1000;
00133
00134 // Wait for something to write on our socket (which means that the connection request has returned)
00135 if (select(static_cast<int>(mySocket + 1), NULL, &Selector, NULL, &Time) > 0)
00136 {
00137 // At this point the connection may have been either accepted or refused.
00138 // To know whether it's a success or a failure, we try to retrieve the name of the connected peer
00139 SocketHelper::LengthType Size = sizeof(SockAddr);
00140 if (getpeername(mySocket, reinterpret_cast<sockaddr*>(&SockAddr), &Size) != -1)
00141 {
00142 // Connection accepted
00143 Status = Socket::Done;
00144 }
00145 else
00146 {
00147 // Connection failed
00148 Status = SocketHelper::GetErrorStatus();
00149 }
00150 }
00151 else
00152 {
00153 // Failed to connect before timeout is over
00154 Status = SocketHelper::GetErrorStatus();
00155 }
00156 }
00157
00158 // Switch back to blocking mode
00159 SetBlocking(true);
00160
00161 return Status;
00162 }
00163 }
00164
00165
00169 bool SocketTCP::Listen(unsigned short Port)
00170 {
00171 // Make sure our socket is valid
00172 if (!IsValid())
00173 Create();
00174
00175 // Build the address
00176 sockaddr_in SockAddr;
00177 memset(SockAddr.sin_zero, 0, sizeof(SockAddr.sin_zero));
00178 SockAddr.sin_addr.s_addr = htonl(INADDR_ANY);
00179 SockAddr.sin_family = AF_INET;
00180 SockAddr.sin_port = htons(Port);
00181
00182 // Bind the socket to the specified port
00183 if (bind(mySocket, reinterpret_cast<sockaddr*>(&SockAddr), sizeof(SockAddr)) == -1)
00184 {
00185 // Not likely to happen, but...
00186 std::cerr << "Failed to bind socket to port " << Port << std::endl;
00187 return false;
00188 }
00189
00190 // Listen to the bound port
00191 if (listen(mySocket, 0) == -1)
00192 {
00193 // Oops, socket is deaf
00194 std::cerr << "Failed to listen to port " << Port << std::endl;
00195 return false;
00196 }
00197
00198 return true;
00199 }
00200
00201
00206 Socket::Status SocketTCP::Accept(SocketTCP& Connected, IPAddress* Address)
00207 {
00208 // Address that will be filled with client informations
00209 sockaddr_in ClientAddress;
00210 SocketHelper::LengthType Length = sizeof(ClientAddress);
00211
00212 // Accept a new connection
00213 Connected = accept(mySocket, reinterpret_cast<sockaddr*>(&ClientAddress), &Length);
00214
00215 // Check errors
00216 if (!Connected.IsValid())
00217 {
00218 if (Address)
00219 *Address = IPAddress();
00220
00221 return SocketHelper::GetErrorStatus();
00222 }
00223
00224 // Fill address if requested
00225 if (Address)
00226 *Address = IPAddress(inet_ntoa(ClientAddress.sin_addr));
00227
00228 return Socket::Done;
00229 }
00230
00231
00235 Socket::Status SocketTCP::Send(const char* Data, std::size_t Size)
00236 {
00237 // First check that socket is valid
00238 if (!IsValid())
00239 return Socket::Error;
00240
00241 // Check parameters
00242 if (Data && Size)
00243 {
00244 // Loop until every byte has been sent
00245 int Sent = 0;
00246 int SizeToSend = static_cast<int>(Size);
00247 for (int Length = 0; Length < SizeToSend; Length += Sent)
00248 {
00249 // Send a chunk of data
00250 Sent = send(mySocket, Data + Length, SizeToSend - Length, 0);
00251
00252 // Check if an error occured
00253 if (Sent <= 0)
00254 return SocketHelper::GetErrorStatus();
00255 }
00256
00257 return Socket::Done;
00258 }
00259 else
00260 {
00261 // Error...
00262 std::cerr << "Cannot send data over the network (invalid parameters)" << std::endl;
00263 return Socket::Error;
00264 }
00265 }
00266
00267
00272 Socket::Status SocketTCP::Receive(char* Data, std::size_t MaxSize, std::size_t& SizeReceived)
00273 {
00274 // First clear the size received
00275 SizeReceived = 0;
00276
00277 // Check that socket is valid
00278 if (!IsValid())
00279 return Socket::Error;
00280
00281 // Check parameters
00282 if (Data && MaxSize)
00283 {
00284 // Receive a chunk of bytes
00285 int Received = recv(mySocket, Data, static_cast<int>(MaxSize), 0);
00286
00287 // Check the number of bytes received
00288 if (Received > 0)
00289 {
00290 SizeReceived = static_cast<std::size_t>(Received);
00291 return Socket::Done;
00292 }
00293 else if (Received == 0)
00294 {
00295 return Socket::Disconnected;
00296 }
00297 else
00298 {
00299 return SocketHelper::GetErrorStatus();
00300 }
00301 }
00302 else
00303 {
00304 // Error...
00305 std::cerr << "Cannot receive data from the network (invalid parameters)" << std::endl;
00306 return Socket::Error;
00307 }
00308 }
00309
00310
00314 Socket::Status SocketTCP::Send(Packet& PacketToSend)
00315 {
00316 // Get the data to send from the packet
00317 std::size_t DataSize = 0;
00318 const char* Data = PacketToSend.OnSend(DataSize);
00319
00320 // Send the packet size
00321 Uint32 PacketSize = htonl(static_cast<unsigned long>(DataSize));
00322 Send(reinterpret_cast<const char*>(&PacketSize), sizeof(PacketSize));
00323
00324 // Send the packet data
00325 if (PacketSize > 0)
00326 {
00327 return Send(Data, DataSize);
00328 }
00329 else
00330 {
00331 return Socket::Done;
00332 }
00333 }
00334
00335
00340 Socket::Status SocketTCP::Receive(Packet& PacketToReceive)
00341 {
00342 // We start by getting the size of the incoming packet
00343 Uint32 PacketSize = 0;
00344 std::size_t Received = 0;
00345 if (myPendingPacketSize < 0)
00346 {
00347 // Loop until we've received the entire size of the packet
00348 // (even a 4 bytes variable may be received in more than one call)
00349 while (myPendingHeaderSize < sizeof(myPendingHeader))
00350 {
00351 char* Data = reinterpret_cast<char*>(&myPendingHeader) + myPendingHeaderSize;
00352 Socket::Status Status = Receive(Data, sizeof(myPendingHeader) - myPendingHeaderSize, Received);
00353 myPendingHeaderSize += Received;
00354
00355 if (Status != Socket::Done)
00356 return Status;
00357 }
00358
00359 PacketSize = ntohl(myPendingHeader);
00360 myPendingHeaderSize = 0;
00361 }
00362 else
00363 {
00364 // There is a pending packet : we already know its size
00365 PacketSize = myPendingPacketSize;
00366 }
00367
00368 // Then loop until we receive all the packet data
00369 char Buffer[1024];
00370 while (myPendingPacket.size() < PacketSize)
00371 {
00372 // Receive a chunk of data
00373 std::size_t SizeToGet = std::min(static_cast<std::size_t>(PacketSize - myPendingPacket.size()), sizeof(Buffer));
00374 Socket::Status Status = Receive(Buffer, SizeToGet, Received);
00375 if (Status != Socket::Done)
00376 {
00377 // We must save the size of the pending packet until we can receive its content
00378 if (Status == Socket::NotReady)
00379 myPendingPacketSize = PacketSize;
00380 return Status;
00381 }
00382
00383 // Append it into the packet
00384 if (Received > 0)
00385 {
00386 myPendingPacket.resize(myPendingPacket.size() + Received);
00387 char* Begin = &myPendingPacket[0] + myPendingPacket.size() - Received;
00388 memcpy(Begin, Buffer, Received);
00389 }
00390 }
00391
00392 // We have received all the datas : we can copy it to the user packet, and clear our internal packet
00393 PacketToReceive.Clear();
00394 if (!myPendingPacket.empty())
00395 PacketToReceive.OnReceive(&myPendingPacket[0], myPendingPacket.size());
00396 myPendingPacket.clear();
00397 myPendingPacketSize = -1;
00398
00399 return Socket::Done;
00400 }
00401
00402
00406 bool SocketTCP::Close()
00407 {
00408 if (IsValid())
00409 {
00410 if (!SocketHelper::Close(mySocket))
00411 {
00412 std::cerr << "Failed to close socket" << std::endl;
00413 return false;
00414 }
00415
00416 mySocket = SocketHelper::InvalidSocket();
00417 }
00418
00419 myIsBlocking = true;
00420
00421 return true;
00422 }
00423
00424
00429 bool SocketTCP::IsValid() const
00430 {
00431 return mySocket != SocketHelper::InvalidSocket();
00432 }
00433
00434
00438 bool SocketTCP::operator ==(const SocketTCP& Other) const
00439 {
00440 return mySocket == Other.mySocket;
00441 }
00442
00443
00447 bool SocketTCP::operator !=(const SocketTCP& Other) const
00448 {
00449 return mySocket != Other.mySocket;
00450 }
00451
00452
00458 bool SocketTCP::operator <(const SocketTCP& Other) const
00459 {
00460 return mySocket < Other.mySocket;
00461 }
00462
00463
00468 SocketTCP::SocketTCP(SocketHelper::SocketType Descriptor)
00469 {
00470 Create(Descriptor);
00471 }
00472
00473
00477 void SocketTCP::Create(SocketHelper::SocketType Descriptor)
00478 {
00479 // Use the given socket descriptor, or get a new one
00480 mySocket = Descriptor ? Descriptor : socket(PF_INET, SOCK_STREAM, 0);
00481 myIsBlocking = true;
00482
00483 // Reset the pending packet
00484 myPendingHeaderSize = 0;
00485 myPendingPacket.clear();
00486 myPendingPacketSize = -1;
00487
00488 // Setup default options
00489 if (IsValid())
00490 {
00491 // To avoid the "Address already in use" error message when trying to bind to the same port
00492 int Yes = 1;
00493 if (setsockopt(mySocket, SOL_SOCKET, SO_REUSEADDR, reinterpret_cast<char*>(&Yes), sizeof(Yes)) == -1)
00494 {
00495 std::cerr << "Failed to set socket option \"SO_REUSEADDR\" ; "
00496 << "binding to a same port may fail if too fast" << std::endl;
00497 }
00498
00499 // Disable the Nagle algorithm (ie. removes buffering of TCP packets)
00500 if (setsockopt(mySocket, IPPROTO_TCP, TCP_NODELAY, reinterpret_cast<char*>(&Yes), sizeof(Yes)) == -1)
00501 {
00502 std::cerr << "Failed to set socket option \"TCP_NODELAY\" ; "
00503 << "all your TCP packets will be buffered" << std::endl;
00504 }
00505
00506 // Set blocking by default (should always be the case anyway)
00507 SetBlocking(true);
00508 }
00509 }
00510
00511 } // namespace sf
:: Copyright © 2007-2008 Laurent Gomila, all rights reserved ::
Documentation generated by doxygen 1.5.2 ::
Wyszukiwarka
Podobne podstrony:
SocketUDP?pp sourceSocketHelper?pp sourceSocketUDP 8hpp sourceSocketTCP 8hpp sourceSockets 8hpp sourceSocketHelper 8hpp sourcesource30function socket fd issetMatrix3?pp sourceSocketClientThread?pp sourcearm biquad ?scade ?1 ?st q31? sourcearm conv ?2? sourcearm mat mult q15? sourcefunction socket selectResource 8inl sourcearm fir lattice init q31? sourcewięcej podobnych podstron