| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350 |
- /* 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(KNET_UNIX) || defined(ANDROID)
- #include <sys/types.h>
- #include <sys/socket.h>
- #include <netinet/in.h>
- #include <netdb.h>
- #include <errno.h>
- #define INVALID_SOCKET ((SOCKET)0)
- #define KNET_SOCKET_ERROR ((SOCKET)-1)
- #define KNET_ACCEPT_FAILURE ((SOCKET)-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 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
- {
- /// Stores the number of bytes allocated to buf.
- /// When sending out a message, this field specifies the number of bytes the client
- /// can write to buf, at maximum.
- unsigned long len;
- char *buf;
- };
- #endif
- struct OverlappedTransferBuffer
- {
- kNetBuffer buffer;
- #ifdef WIN32
- WSAOVERLAPPED overlapped;
- #endif
- /// Stores the number of bytes actually in use in buffer.buf. When sending out a message,
- /// specify the actual number of bytes filled to buffer.buf here.
- int bytesContains;
- /// Stores the total number of bytes allocated to the buffer in the overlapped structure.
- int bytesAllocated;
- 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).
- /// @param maxBytesToSend Specifies the size of the buffer that must be returned. Specify the size (or at least an
- /// upper limit) of the message you are sending here. Specify the actual number of bytes filled in the resulting
- /// structure.
- /// @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(int maxBytesToSend);
- /// 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
|