DotNetDupe 4.0.6
C++17/20 Implementation of the .NET Base Class Library (BCL)
Loading...
Searching...
No Matches
Socket.cpp
Go to the documentation of this file.
1#include "pch.h"
2#include "System/String.h"
6#include <mutex>
7#include <cstring>
8#include <cstdlib>
9
10#if defined(_WIN32)
11 #define WIN32_LEAN_AND_MEAN
12 #include <winsock2.h>
13 #include <ws2tcpip.h>
14 #pragma comment(lib, "Ws2_32.lib")
15 using SockLen = int;
16 #ifndef SD_RECEIVE
17 #define SD_RECEIVE 0
18 #define SD_SEND 1
19 #define SD_BOTH 2
20 #endif
21#else
22 #include <sys/socket.h>
23 #include <sys/types.h>
24 #include <netinet/in.h>
25 #include <arpa/inet.h>
26 #include <unistd.h>
27 #include <fcntl.h>
28 #include <errno.h>
29 #include <sys/select.h>
30 #define SOCKET int
31 #define INVALID_SOCKET -1
32 #define SOCKET_ERROR -1
33 #define SD_RECEIVE 0
34 #define SD_SEND 1
35 #define SD_BOTH 2
36 using SockLen = socklen_t;
37#endif
38
39namespace DotNetDupe {
40 namespace System {
41 namespace Net {
42 namespace Sockets {
43
44 class SocketImpl {
45 public:
46 SOCKET hSocket;
47
48 SocketImpl() : hSocket(INVALID_SOCKET) {}
49 ~SocketImpl() {
50 Cleanup();
51 }
52
53 void Cleanup() {
54 if (hSocket != INVALID_SOCKET) {
55#if defined(_WIN32)
56 closesocket(hSocket);
57#else
58 close(hSocket);
59#endif
60 hSocket = INVALID_SOCKET;
61 }
62 }
63 };
64
65 static int GetLastErrorCode() {
66#if defined(_WIN32)
67 return WSAGetLastError();
68#else
69 return errno;
70#endif
71 }
72
73 static void InitializeSockets() {
74#if defined(_WIN32)
75 static bool bInitialized = false;
76 static std::mutex mutexInit;
77 std::lock_guard<std::mutex> lock(mutexInit);
78 if (!bInitialized) {
79 WSADATA wsaData;
80 if (WSAStartup(MAKEWORD(2, 2), &wsaData) != 0) {
81 throw SocketException(GetLastErrorCode(), "WSAStartup failed");
82 }
83 bInitialized = true;
84 std::atexit([]() { WSACleanup(); });
85 }
86#endif
87 }
88
89 static SOCKET CreateNativeSocket(AddressFamily addressFamily, SocketType socketType, ProtocolType protocolType) {
90 int af = (addressFamily == AddressFamily::InterNetworkV6) ? AF_INET6 : AF_INET;
91 int type = (socketType == SocketType::Dgram) ? SOCK_DGRAM : SOCK_STREAM;
92 int proto = (protocolType == ProtocolType::Udp) ? IPPROTO_UDP : IPPROTO_TCP;
93#if defined(_WIN32)
94 return socket(af, type, proto);
95#else
96 return ::socket(af, type, proto);
97#endif
98 }
99
100 static void InitSockAddrIn(const String& ip, int port, sockaddr_in& addr) {
101 std::memset(&addr, 0, sizeof(addr));
102 addr.sin_family = AF_INET;
103 addr.sin_port = htons(static_cast<u_short>(port));
104 if (ip.IsEmpty() || ip == "0.0.0.0") {
105 addr.sin_addr.s_addr = INADDR_ANY;
106 } else {
107 if (inet_pton(AF_INET, ip.GetRawString(), &addr.sin_addr) != 1) {
108 throw ArgumentException("Invalid IP address.");
109 }
110 }
111 }
112
113 Socket::Socket(AddressFamily addressFamily, SocketType socketType, ProtocolType protocolType)
114 : m_pImpl(new SocketImpl()) {
117
119 m_pImpl->hSocket = CreateNativeSocket(addressFamily, socketType, protocolType);
120 if (m_pImpl->hSocket == INVALID_SOCKET) {
121 throw SocketException(GetLastErrorCode(), "Failed to create native socket.");
122 }
123 }
124
125 Socket::Socket(void* pNativeHandle)
126 : m_pImpl(new SocketImpl()) {
129
131#if defined(_WIN32)
132 m_pImpl->hSocket = reinterpret_cast<SOCKET>(pNativeHandle);
133#else
134 m_pImpl->hSocket = static_cast<int>(reinterpret_cast<intptr_t>(pNativeHandle));
135#endif
136 }
137
139
140 Socket::Socket(Socket&& other) noexcept : m_pImpl(std::move(other.m_pImpl)) {}
141
142 Socket& Socket::operator=(Socket&& other) noexcept {
143 if (this != &other) m_pImpl = std::move(other.m_pImpl);
144 return *this;
145 }
146
147 void Socket::Bind(const String& ip, int port) {
149 if (!m_pImpl || m_pImpl->hSocket == INVALID_SOCKET) throw SocketException(-1, String("Socket is closed."));
150
152 sockaddr_in addr;
153 InitSockAddrIn(ip, port, addr);
154 int optval = 1;
155#if defined(_WIN32)
156 setsockopt(m_pImpl->hSocket, SOL_SOCKET, SO_REUSEADDR, reinterpret_cast<const char*>(&optval), sizeof(optval));
157#else
158 ::setsockopt(m_pImpl->hSocket, SOL_SOCKET, SO_REUSEADDR, &optval, sizeof(optval));
159#endif
160
162 if (bind(m_pImpl->hSocket, reinterpret_cast<sockaddr*>(&addr), sizeof(addr)) == SOCKET_ERROR) {
163 throw SocketException(GetLastErrorCode(), "Failed to bind socket.");
164 }
165 }
166
167 void Socket::Listen(int backlog) {
169 if (!m_pImpl || m_pImpl->hSocket == INVALID_SOCKET) {
170 throw SocketException(-1, "Socket is closed.");
171 }
172
174 if (listen(m_pImpl->hSocket, backlog) == SOCKET_ERROR) {
175 throw SocketException(GetLastErrorCode(), "Failed to listen on socket.");
176 }
177 }
178
181 if (!m_pImpl || m_pImpl->hSocket == INVALID_SOCKET) {
182 throw SocketException(-1, "Socket is closed.");
183 }
184
186 sockaddr_in clientAddr;
187 SockLen clientSize = sizeof(clientAddr);
188#if defined(_WIN32)
189 SOCKET clientSocket = accept(m_pImpl->hSocket, reinterpret_cast<sockaddr*>(&clientAddr), &clientSize);
190#else
191 int clientSocket = ::accept(m_pImpl->hSocket, reinterpret_cast<sockaddr*>(&clientAddr), &clientSize);
192#endif
193 if (clientSocket == INVALID_SOCKET) {
194 throw SocketException(GetLastErrorCode(), "Failed to accept connection.");
195 }
196
197 return SmartPointer<Socket>(new Socket(reinterpret_cast<void*>(clientSocket)), true);
198 }
199
200 void Socket::Connect(const String& ip, int port) {
202 if (!m_pImpl || m_pImpl->hSocket == INVALID_SOCKET) throw SocketException(-1, "Socket is closed.");
203
205 sockaddr_in addr;
206 InitSockAddrIn(ip, port, addr);
207 if (connect(m_pImpl->hSocket, reinterpret_cast<sockaddr*>(&addr), sizeof(addr)) == SOCKET_ERROR) {
208 throw SocketException(GetLastErrorCode(), "Failed to connect to host.");
209 }
210 }
211
212 int Socket::Send(const char* buffer, int offset, int size) {
214 if (!m_pImpl || m_pImpl->hSocket == INVALID_SOCKET) throw SocketException(-1, "Socket is closed.");
215
217 int bytesSent = send(m_pImpl->hSocket, buffer + offset, size, 0);
218 if (bytesSent == SOCKET_ERROR) throw SocketException(GetLastErrorCode(), "Failed to send data.");
219 return bytesSent;
220 }
221
222 int Socket::Receive(char* buffer, int offset, int size) {
224 if (!m_pImpl || m_pImpl->hSocket == INVALID_SOCKET) throw SocketException(-1, "Socket is closed.");
225
227 int bytesReceived = recv(m_pImpl->hSocket, buffer + offset, size, 0);
228 if (bytesReceived == SOCKET_ERROR) throw SocketException(GetLastErrorCode(), "Failed to receive data.");
229 return bytesReceived;
230 }
231
232 int Socket::SendTo(const char* buffer, int offset, int size, const String& ip, int port) {
234 if (!m_pImpl || m_pImpl->hSocket == INVALID_SOCKET) throw SocketException(-1, "Socket is closed.");
235
237 sockaddr_in addr;
238 InitSockAddrIn(ip, port, addr);
239 int bytesSent = sendto(m_pImpl->hSocket, buffer + offset, size, 0, reinterpret_cast<sockaddr*>(&addr), sizeof(addr));
240 if (bytesSent == SOCKET_ERROR) throw SocketException(GetLastErrorCode(), "Failed to send data to host.");
241 return bytesSent;
242 }
243
244 static void FormatSockAddrIp(const sockaddr_in& addr, String& ip, int& port) {
245 char ipBuf[INET_ADDRSTRLEN];
246 if (inet_ntop(AF_INET, &addr.sin_addr, ipBuf, sizeof(ipBuf)) != nullptr) {
247 ip = String(ipBuf);
248 }
249 port = ntohs(addr.sin_port);
250 }
251
252 int Socket::ReceiveFrom(char* buffer, int offset, int size, String& ip, int& port) {
254 if (!m_pImpl || m_pImpl->hSocket == INVALID_SOCKET) throw SocketException(-1, "Socket is closed.");
255
257 sockaddr_in addr;
258 SockLen addrSize = sizeof(addr);
259 std::memset(&addr, 0, sizeof(addr));
260 int bytesReceived = recvfrom(m_pImpl->hSocket, buffer + offset, size, 0, reinterpret_cast<sockaddr*>(&addr), &addrSize);
261 if (bytesReceived == SOCKET_ERROR) throw SocketException(GetLastErrorCode(), "Failed to receive data from host.");
262
263 FormatSockAddrIp(addr, ip, port);
264 return bytesReceived;
265 }
266
269 if (m_pImpl) m_pImpl->Cleanup();
270 }
271
274 if (!m_pImpl || m_pImpl->hSocket == INVALID_SOCKET) return;
275
277 int nativeHow = (how == SocketShutdown::Receive) ? SD_RECEIVE : ((how == SocketShutdown::Send) ? SD_SEND : SD_BOTH);
278 shutdown(m_pImpl->hSocket, nativeHow);
279 }
280
281 static int SelectSingleSocket(SOCKET hSocket, int microSeconds, SelectMode mode) {
282 fd_set fds;
283 FD_ZERO(&fds);
284 FD_SET(hSocket, &fds);
285 timeval timeout{ microSeconds / 1000000, microSeconds % 1000000 };
286 fd_set* pRead = (mode == SelectMode::SelectRead) ? &fds : nullptr;
287 fd_set* pWrite = (mode == SelectMode::SelectWrite) ? &fds : nullptr;
288 fd_set* pErr = (mode == SelectMode::SelectError) ? &fds : nullptr;
289#if defined(_WIN32)
290 return select(0, pRead, pWrite, pErr, &timeout);
291#else
292 return select(hSocket + 1, pRead, pWrite, pErr, &timeout);
293#endif
294 }
295
296 bool Socket::Poll(int microSeconds, SelectMode mode) {
297 if (!m_pImpl || m_pImpl->hSocket == INVALID_SOCKET) return false;
298 return SelectSingleSocket(m_pImpl->hSocket, microSeconds, mode) > 0;
299 }
300
301 static bool IsPeerConnected(SOCKET hSocket) {
302 sockaddr_in peerAddr;
303 SockLen peerLen = sizeof(peerAddr);
304 return (getpeername(hSocket, reinterpret_cast<sockaddr*>(&peerAddr), &peerLen) != SOCKET_ERROR);
305 }
306
307 static bool IsSocketActivePeek(SOCKET hSocket) {
308 char buf;
309#if defined(_WIN32)
310 int result = recv(hSocket, &buf, 1, MSG_PEEK);
311 if (result == 0) return false;
312 return (result != SOCKET_ERROR || WSAGetLastError() == WSAEWOULDBLOCK);
313#else
314 int result = ::recv(hSocket, &buf, 1, MSG_PEEK | MSG_DONTWAIT);
315 if (result == 0) return false;
316 return (result != SOCKET_ERROR || (errno == EAGAIN || errno == EWOULDBLOCK));
317#endif
318 }
319
320 bool Socket::Connected() const {
321 if (!m_pImpl || m_pImpl->hSocket == INVALID_SOCKET || !IsPeerConnected(m_pImpl->hSocket)) return false;
322 int sel = SelectSingleSocket(m_pImpl->hSocket, 0, SelectMode::SelectRead);
323 if (sel == SOCKET_ERROR) return false;
324 if (sel == 0) return true;
325 return IsSocketActivePeek(m_pImpl->hSocket);
326 }
327
329 return m_pImpl ? reinterpret_cast<void*>(m_pImpl->hSocket) : nullptr;
330 }
331
332 }
333 }
334 }
335}
Defines the exception thrown when an invalid argument is provided to a method.
#define SD_BOTH
Definition Socket.cpp:19
#define SD_SEND
Definition Socket.cpp:18
int SockLen
Definition Socket.cpp:15
#define SD_RECEIVE
Definition Socket.cpp:17
Implements the Berkeley sockets interface for network communications.
Exception thrown when a network socket error occurs per RFC 793 / RFC 768.
High-performance UTF-8 / UTF-16 string manipulation class mirroring .NET System.String.
ArgumentException(const String &sMessage)
Initializes a new instance of the ArgumentException class with a specified error message.
SocketException(int errorCode, const String &message)
Initializes a new instance of the SocketException class with the specified error code and message.
int Receive(char *buffer, int offset, int size)
Receives data from a bound Socket into a receive buffer.
Definition Socket.cpp:222
bool Connected() const
Gets a value that indicates whether a Socket is connected to a remote host as of the last Send or Rec...
Definition Socket.cpp:320
bool Poll(int microSeconds, SelectMode mode)
Determines the status of the Socket.
Definition Socket.cpp:296
~Socket()
Destructor closing encapsulated socket handles.
Definition Socket.cpp:138
SmartPointer< Socket > Accept()
Creates a new Socket for a newly created connection.
Definition Socket.cpp:179
int SendTo(const char *buffer, int offset, int size, const String &ip, int port)
Sends data to the specified endpoint.
Definition Socket.cpp:232
Socket & operator=(const Socket &)=delete
int Send(const char *buffer, int offset, int size)
Sends data to a connected Socket.
Definition Socket.cpp:212
void Bind(const String &ip, int port)
Associates a Socket with a local endpoint.
Definition Socket.cpp:147
void Shutdown(SocketShutdown how)
Disables sends and receives on a Socket.
Definition Socket.cpp:272
void * GetNativeHandle() const
Retrieves the underlying native socket descriptor.
Definition Socket.cpp:328
int ReceiveFrom(char *buffer, int offset, int size, String &ip, int &port)
Receives a datagram into the data buffer and stores the endpoint.
Definition Socket.cpp:252
void Listen(int backlog)
Places a Socket in a listening state.
Definition Socket.cpp:167
Socket(AddressFamily addressFamily, SocketType socketType, ProtocolType protocolType)
Initializes a new instance of the Socket class using the specified address family,...
Definition Socket.cpp:113
void Close()
Closes the Socket connection and releases all associated resources.
Definition Socket.cpp:267
void Connect(const String &ip, int port)
Establishes a connection to a remote host.
Definition Socket.cpp:200
A unified smart pointer that supports both unique and shared ownership semantics.
Represents text as a sequence of UTF-8 code units with culture-invariant operations.
Definition String.h:74
String()
Initializes a new instance of the String class to an empty string.
Definition String.cpp:64
const char * GetRawString() const
Definition String.cpp:230
static SOCKET CreateNativeSocket(AddressFamily addressFamily, SocketType socketType, ProtocolType protocolType)
Definition Socket.cpp:89
static void InitSockAddrIn(const String &ip, int port, sockaddr_in &addr)
Definition Socket.cpp:100
static int SelectSingleSocket(SOCKET hSocket, int microSeconds, SelectMode mode)
Definition Socket.cpp:281
SelectMode
Defines the polling modes for the Socket.Poll method.
Definition Socket.h:54
static bool IsPeerConnected(SOCKET hSocket)
Definition Socket.cpp:301
AddressFamily
Specifies the addressing scheme that an instance of the Socket class can use.
Definition Socket.h:20
SocketShutdown
Defines constants that are used by the Socket.Shutdown method.
Definition Socket.h:46
static void FormatSockAddrIp(const sockaddr_in &addr, String &ip, int &port)
Definition Socket.cpp:244
ProtocolType
Specifies the protocols that the Socket class supports.
Definition Socket.h:38
static bool IsSocketActivePeek(SOCKET hSocket)
Definition Socket.cpp:307
SocketType
Specifies the type of socket that an instance of the Socket class represents.
Definition Socket.h:29