/include/kNet/Socket.h
C Header | 342 lines | 151 code | 62 blank | 129 comment | 8 complexity | 692135134a11ea6dd06a95a623219993 MD5 | raw file
Possible License(s): Apache-2.0
- /* Copyright The kNet Project.
-
- Licensed under the Apache License, Version 2.0 (the "License");
- you may not use this file except in compliance with the License.
- You may obtain a copy of the License at
-
- http://www.apache.org/licenses/LICENSE-2.0
-
- Unless required by applicable law or agreed to in writing, software
- distributed under the License is distributed on an "AS IS" BASIS,
- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- See the License for the specific language governing permissions and
- limitations under the License. */
- #pragma once
-
- /** @file Socket.h
- @brief The Socket class. */
-
- #ifdef WIN32
-
- #include "kNetBuildConfig.h"
- #include "win32/WS2Include.h"
- #define KNET_EWOULDBLOCK WSAEWOULDBLOCK
- #define KNET_SOCKET_ERROR SOCKET_ERROR
- #define KNET_ACCEPT_FAILURE SOCKET_ERROR
-
- namespace kNet
- {
- typedef int socklen_t;
- }
-
- #elif defined(UNIX) || defined(ANDROID)
-
- #include <sys/types.h>
- #include <sys/socket.h>
- #include <netinet/in.h>
- #include <netdb.h>
- #include <errno.h>
-
- #define INVALID_SOCKET (0)
- #define KNET_SOCKET_ERROR (-1)
- #define KNET_ACCEPT_FAILURE (-1)
- #define KNET_EWOULDBLOCK EWOULDBLOCK
- #define closesocket close
- #define TIMEVAL timeval
- #define SD_SEND SHUT_WR
- #define SD_BOTH SHUT_RDWR
- #define _stricmp strcasecmp
-
- namespace kNet
- {
- typedef unsigned int SOCKET;
- }
- #endif
-
- #include <vector>
- #include <list>
-
- #include "SharedPtr.h"
- #include "EndPoint.h"
- #include "WaitFreeQueue.h"
- #include "Event.h"
-
- namespace kNet
- {
-
- /// Identifiers for the possible bottom-level tranport layers.
- enum SocketTransportLayer
- {
- InvalidTransportLayer = 0, ///< A default invalid value for uninitialized sockets.
- SocketOverUDP,
- SocketOverTCP
- };
-
- std::string SocketTransportLayerToString(SocketTransportLayer transport);
-
- /// Converts the given string (case-insensitive parsing) to the corresponding SocketTransportLayer enum.
- /// "tcp" & "socketovertcp" -> SocketOverTCP.
- /// "udp" & "socketoverudp" -> SocketOverUDP.
- /// Other strings -> InvalidTransportLayer.
- SocketTransportLayer StringToSocketTransportLayer(const char *str);
-
- enum SocketType
- {
- InvalidSocketType = 0, ///< A default invalid value for uninitialized sockets.
- ServerListenSocket, ///< For TCP: a listen socket. For UDP: the single master socket handle that is used to send & receive all data.
- ServerClientSocket, ///< For TCP: a client data socket. For UDP: a slave-mode Socket object that shares the underlying socket handle with the UDP master Socket.
- ClientSocket ///< A client-side socket.
- };
-
- std::string SocketTypeToString(SocketType type);
-
- typedef int OverlappedTransferTag;
-
- #ifdef WIN32
- typedef WSABUF kNetBuffer;
- #else
- struct kNetBuffer
- {
- /// Specifies the number of bytes allocated to buf. This is the maximum amount of bytes that can
- /// be written to buf.
- unsigned long len;
-
- char *buf;
- };
- #endif
-
- struct OverlappedTransferBuffer
- {
- kNetBuffer buffer;
- #ifdef WIN32
- WSAOVERLAPPED overlapped;
- #endif
-
- /// Specifies the number of bytes buffer.buf actually contains.
- int bytesContains;
-
- sockaddr_in from;
- socklen_t fromLen;
- };
-
- /// Represents a low-level network socket.
- class Socket : public RefCountable
- {
- public:
- Socket();
-
- Socket(SOCKET connection, const EndPoint &localEndPoint, const char *localHostName,
- const EndPoint &remoteEndPoint, const char *remoteHostName,
- SocketTransportLayer transport, SocketType type, size_t maxSendSize);
-
- Socket(const Socket &);
- ~Socket();
-
- Socket &operator=(const Socket &);
-
- /// Sets the underlying socket send buffer (SO_SNDBUF) size.
- void SetSendBufferSize(int bytes);
- /// Sets the underlying socket receive buffer (SO_RCVBUF) size.
- void SetReceiveBufferSize(int bytes);
-
- /// Returns the current value for the send buffer of this socket.
- int SendBufferSize() const;
- /// Returns the current value for the receive buffer of this socket.
- int ReceiveBufferSize() const;
-
- /// Returns true if the connection is both write-open AND read-open.
- bool Connected() const;
- /// Returns true if the connection is open for reading from. This does not mean that there necessarily is new data
- /// to be immediately read - it only means that the socket is open for receiving data.
- bool IsReadOpen() const { return IsOverlappedReceiveReady() || readOpen; }
- /// Returns true if the connection is open for writing to.
- bool IsWriteOpen() const { return writeOpen; }
- /// Forces this socket to be treated as if it has been read-closed.
- void MarkReadClosed() { readOpen = false; }
- /// Forces this socket to be treated as if it has been write-closed.
- void MarkWriteClosed() { writeOpen = false; }
-
- /// If this function returns true, this socket represents a UDP server socket instance. For a UDP server,
- /// the data for all clients is received through this same socket, and there are no individual sockets created for
- /// each new connection, like is done with TCP.
- bool IsUDPServerSocket() const { return transport == SocketOverUDP && type == ServerListenSocket; }
- /// Returns whether this socket is a UDP slave socket. [worker thread]
- bool IsUDPSlaveSocket() const { return transport == SocketOverUDP && type == ServerClientSocket; }
-
- /// Performs a write close operation on the socket, signalling the other end that no more data will be sent. Any data
- /// currently left in the send buffers will be sent over to the other side, but no new data can be sent. The connection
- /// will remain half-open for reading, and Receive() calls may still be made to the socket. When a read returns 0, the
- /// connection will be transitioned to bidirectionally closed state (Connected() will return false).
- void Disconnect();
- /// Performs an immediate write and read close on the socket, without waiting for the connection to gracefully shut down.
- void Close();
-
- /// Sends the given data through the socket. This function may only be called if Socket::IsWriteOpen() returns true. If
- /// the socket is not write-open, calls to this function will fail.
- /// This function is an orthogonal API to the overlapped IO Send routines. Do not mix these API calls when doing
- /// networking, but instead choose one preferred method and consistently use it.
- bool Send(const char *data, size_t numBytes);
-
- /// Waits until the Socket is ready for sending. Returns true if the socket transitioned to write-ready state in the given
- /// time period, or false if the wait timed out or if some other error occurred.
- /// This function is an orthogonal API to the overlapped IO Send routines. Do not mix these API calls when doing
- /// networking, but instead choose one preferred method and consistently use it.
- bool WaitForSendReady(int msecs);
-
- /// Starts the sending of new data. After having filled the data to send to the OverlappedTransferBuffer that is
- /// returned here, commit the send by calling EndSend. If you have called BeginSend, but decide not to send any data,
- /// call AbortSend instead (otherwise memory will leak).
- /// @return A transfer buffer where the data to send is to be filled in. If no new data can be sent at this time,
- /// this function returns 0.
- OverlappedTransferBuffer *BeginSend();
- /// Finishes and queues up the given transfer that was created with a call to BeginSend.
- /// @return True if send succeeded, false otherwise. In either case, the ownership of the passed buffer send
- /// is taken by this Socket and may not be accessed anymore. Discard the pointer after calling this function.
- bool EndSend(OverlappedTransferBuffer *send);
- /// Cancels the sending of data. Call this function if you first call BeginSend, but decide you don't want to send the data.
- /// This frees the given buffer, do not dereference it after calling this function.
- void AbortSend(OverlappedTransferBuffer *send);
-
- #ifdef WIN32
- /// Returns the number of sends in the send queue.
- int NumOverlappedSendsInProgress() const { return queuedSendBuffers.Size(); }
- /// Returns the maximum number of sends that can be queued up simultaneously.
- int MaxOverlappedSendsInProgress() const { return queuedSendBuffers.Capacity(); }
- #endif
- /// Returns true if it is possible to send out new data. In this case, a call to BeginSend will succeed.
- bool IsOverlappedSendReady();
- /// Returns the event object that should be waited on to receive a notification when it is possible to send out new data.
- Event GetOverlappedSendEvent();
-
- /// Waits for the max given amount of time for new data to be received from the socket.
- /// @return True if new data was received, false if the timeout period elapsed.
- // bool WaitForData(int msecs); // this will be removed completely.
-
- /// Reads in data from the socket. If there is no data available, this function will not block, but will immediately
- /// return 0.
- /// This function issues an immediate recv() call to the socket and is not compatible with the Overlapped Transfer API
- /// above. Do not mix the use of these two APIs, but pick one method to use and stay with it.
- /// @param endPoint [out] If the socket is an UDP socket that is not bound to an address, this will contain the source address.
- /// @return The number of bytes that were successfully read.
- size_t Receive(char *dst, size_t maxBytes, EndPoint *endPoint = 0);
-
- /// Call to receive new data from the socket.
- /// @return A buffer that contains the data, or 0 if no new data was available. When you are finished reading the buffer, call
- /// EndReceive to free up the buffer, or memory will leak.
- OverlappedTransferBuffer *BeginReceive();
- /// Finishes a read operation on the socket. Frees the given buffer to be re-queued for a future socket read operation.
- void EndReceive(OverlappedTransferBuffer *buffer);
- #ifdef WIN32
- /// Returns the number of receive buffers that have been queued for the socket.
- int NumOverlappedReceivesInProgress() const { return queuedReceiveBuffers.Size(); }
- /// Returns the maximum number of receive buffers that can be queued for the socket.
- int MaxOverlappedReceivesInProgress() const { return queuedReceiveBuffers.Capacity(); }
- #endif
- /// Returns true if there is new data to be read in. In that case, BeginReceive() will not return 0.
- bool IsOverlappedReceiveReady() const;
- /// Returns the event object that will be notified whenever data is available to be read from the socket.
- Event GetOverlappedReceiveEvent(); // [worker thread]
-
- /// Returns which transport layer the connection is using. This value is either SocketOverUDP or SocketOverTCP.
- SocketTransportLayer TransportLayer() const { return transport; }
-
- /// Returns the type of this socket object.
- SocketType Type() const { return type; }
-
- /// Returns the maximum amount of bytes that can be sent through to the network in one call. If you try sending
- /// more data than specified by this value, the result is undefined.
- size_t MaxSendSize() const { return maxSendSize; }
-
- /// Returns the local EndPoint this socket is bound to.
- const EndPoint &LocalEndPoint() const { return localEndPoint; }
- /// Returns the local address (local hostname) of the local end point this socket is bound to.
- const char *LocalAddress() const { return localHostName.c_str(); }
- /// Returns the local port that this socket is bound to.
- unsigned short LocalPort() const { return localEndPoint.port; }
-
- /// Returns the remote EndPoint this socket is connected to.
- /// If SocketType == ServerListenSocket, this socket is not bound
- /// to any remote end point, and so the returned struct is uninitialized (filled with zeroes).
- const EndPoint &RemoteEndPoint() const { return remoteEndPoint; }
- /// Returns the destination address (destination hostname) of the remote end point this socket is connected to.
- /// If SocketType == ServerListenSocket, returns an empty string.
- const char *DestinationAddress() const { return remoteHostName.c_str(); }
- /// Returns the destination port of the remote end point this socket is connected to.
- /// If SocketType == ServerListenSocket, returns 0.
- unsigned short DestinationPort() const { return remoteEndPoint.port; }
-
- /// Returns a human-readable representation of this socket, specifying the peer address and port this socket is
- /// connected to.
- std::string ToString() const;
-
- /// Sets the socket to blocking or nonblocking state.
- void SetBlocking(bool isBlocking);
-
- SOCKET &GetSocketHandle() { return connectSocket; }
-
- /// Enables or disables the use of Nagle's algorithm (TCP_NODELAY) for this socket.
- /// Nagle's algorithm reduces the bandwidth consumption of the channel, but can increase latency.
- /// Conversely, disabling Nagle's algorithm improves the latency, but increases the number of IP packets sent to the network.
- /// By default, Nagle's algorithm is enabled.
- /// Read more about Nagle's algorithm here: http://msdn.microsoft.com/en-us/library/ms817942.aspx
- void SetNaglesAlgorithmEnabled(bool enabled);
-
- private:
- /// Stores the handle to the underlying BSD socket object. Has the value INVALID_SOCKET if uninitialized.
- SOCKET connectSocket;
-
- /// Specifies the local system end point (IP and port) this socket is bound to.
- EndPoint localEndPoint;
-
- /// Specifies the network host name of the local end point (the local system).
- /// If the local end point does not have a hostname, this field is the string representation of the
- /// system IP address (one of them, there may be multiple IPs).
- std::string localHostName;
-
- /// Specifies the remote system end point (IP and port) this socket is bound to (== the "peer" address).
- /// If SocketType == ServerListenSocket or transport == SocketOverUDP, this socket is not bound
- /// to any remote end point, and so this struct is uninitialized (filled with zeroes).
- EndPoint remoteEndPoint;
-
- /// If this socket is a UDP socket, the remoteEndPoint variable is copied (cached) to the variable
- /// udpPeerAddress to avoid having to reconstruct it at each Socket::Send() call (this is an optimization).
- /// This field is only used if this socket is a UDP socket.
- sockaddr_in udpPeerAddress;
-
- /// Specifies the network host name of the remote end point (== the remote system == the "peer").
- /// If the remote end point does not have a known hostname, this field is the string representation of the
- /// remote IP address. If SocketType == ServerListenSocket, this field is empty.
- std::string remoteHostName;
-
- /// Specifies the underlying transport protocol that this Socket is using (TCP or UDP).
- SocketTransportLayer transport;
-
- /// Specifies the type of this SOCKET object.
- /// UDP server sockets are never bound to a remote, since the same socket is used to send and receive data to and
- /// from all client addresses. UDP client sockets and TCP sockets on the other hand are always bound to a peer.
- /// UDP client sockets are shared slave copies of the single UDP server socket. In this case, this field is used
- /// to remember that this Socket object is not authoritative over the OS socket object and should not close it
- /// when tearing down this socket.
- SocketType type;
-
- /// Specifies the maximum amount of data that can be sent to the socket at one call to send().
- size_t maxSendSize;
-
- /// Tracks whether data can be sent through this socket.
- bool writeOpen;
-
- /// Tracks whether the socket is open for receiving data (doesn't mean that there necessarily exists new data to be read).
- bool readOpen;
-
- #ifdef WIN32
- WaitFreeQueue<OverlappedTransferBuffer*> queuedReceiveBuffers;
- WaitFreeQueue<OverlappedTransferBuffer*> queuedSendBuffers;
-
- /// Frees all allocated data in the queuedReceiveBuffers and queuedSendBuffers queues.
- void FreeOverlappedTransferBuffers();
-
- void EnqueueNewReceiveBuffer(OverlappedTransferBuffer *buffer = 0);
- #endif
- };
-
- } // ~kNet