#include <glwebtools/internal/glwebtools_default_config.h>
#if GLWEBTOOLS_USE_METRO_SOCKET
#include <glwebtools/os/glwebtools_socket.h>
#include <glwebtools/internal/glwebtools_memory.h>
#include <glwebtools/internal/glwebtools_macro.h>
#include <glwebtools/glwebtools_error.h>
#include <wrl\implements.h>
#include <wrl\module.h>
#include <wrl\async.h>
bool __uncaught_exception();
#include <ppltasks.h>
#include <windows.networking.sockets.h>
#include <windows.networking.connectivity.h>
#include <string>
#include <sstream>
#include <future>
namespace glwebtools
{
namespace
{
std::wstring stows(const std::string& s)
{
return std::wstring(s.begin(), s.end());
}
std::wstring stows(const char* s)
{
return stows(std::string(s));
}
std::string wstos(const std::wstring& ws)
{
return std::string(ws.begin(), ws.end());
}
std::wstring htow(HSTRING hstr_)
{
unsigned int length;
const wchar_t* buffer = WindowsGetStringRawBuffer(hstr_, &length);
return std::wstring(buffer, length);
}
std::string htos(HSTRING hstr_)
{
return wstos(htow(hstr_));
}
unsigned long inet_addr(const char *str)
{
unsigned long a,b,c,d=0;
char ch;
std::stringstream s(str);
s >> a >> ch >> b >> ch >> c >> ch >> d;
return (a << 24) | (b << 16) | (c << 8) | d;
}
glwebtools::Error inet_wton(const std::wstring& in, AddrIpv4& out)
{
out.Addr.ulongAddr = inet_addr(wstos(in).c_str());
return glwebtools::E_SUCCESS;
}
std::wstring inet_ntow(const AddrIpv4& in)
{
std::wstringstream s;
s << in.Addr.uChar.Addr4 << '.' << in.Addr.uChar.Addr3 << '.'<< in.Addr.uChar.Addr2 << '.'<< in.Addr.uChar.Addr1;
return s.str();
}
std::string inet_ntoa(const AddrIpv4& in)
{
std::stringstream s;
s << in.Addr.uChar.Addr4 << '.' << in.Addr.uChar.Addr3 << '.'<< in.Addr.uChar.Addr2 << '.'<< in.Addr.uChar.Addr1;
return s.str();
}
std::wstring port_itow(unsigned short port)
{
std::wstringstream s;
s << port;
return s.str();
}
unsigned short port_wtoi(const std::wstring& in)
{
std::wstringstream s(in);
unsigned short port;
s >> port;
return port;
}
unsigned short port_htoi(HSTRING hstr_)
{
std::wstringstream s(htow(hstr_));
unsigned short port;
s >> port;
return port;
}
glwebtools::Error GetIpv4Addr(ABI::Windows::Networking::IHostName* pHostName, AddrIpv4& addr_out)
{
if (pHostName == 0)
{
return glwebtools::E_INVALID_PARAMETER;
}
HRESULT hr;
ABI::Windows::Networking::HostNameType hnt;
hr = pHostName->get_Type(&hnt);
if (FAILED(hr))
{
return glwebtools::E_INVALID_PARAMETER;
}
if (hnt != ABI::Windows::Networking::HostNameType::HostNameType_Ipv4)
{
return glwebtools::E_INVALID_PARAMETER;
}
Microsoft::WRL::Wrappers::HString hostname;
hr = pHostName->get_RawName(hostname.GetAddressOf());
if (FAILED(hr))
{
return glwebtools::E_INVALID_PARAMETER;
}
std::wstring wshostname = htow(hostname.Get());
return inet_wton(wshostname, addr_out);
}
template <typename F>
auto run_now(F f)->decltype(f())
{
typedef decltype(f()) result_type;
result_type result;
std::thread operation_wrapper(
[f, &result]()
{
concurrency::task<result_type> operation = concurrency::task<result_type>(f);
result = operation.get();
});
operation_wrapper.join();
return result;
}
struct tcp_connection_handler
{
Microsoft::WRL::ComPtr<ABI::Windows::Networking::Sockets::IStreamSocket> m_socket;
Microsoft::WRL::ComPtr<ABI::Windows::Storage::Streams::IDataReader> m_reader;
Microsoft::WRL::ComPtr<ABI::Windows::Storage::Streams::IDataWriter> m_writer;
bool valid() const
{
return m_socket;
}
glwebtools::Error Create()
{
HRESULT hr;
Microsoft::WRL::ComPtr<IInspectable> pInspectable;
hr = ::RoActivateInstance(Microsoft::WRL::Wrappers::HString::MakeReference(RuntimeClass_Windows_Networking_Sockets_StreamSocket).Get(), &pInspectable);
if (FAILED(hr))
{
return glwebtools::E_INVALID_OPERATION;
}
Microsoft::WRL::ComPtr<ABI::Windows::Networking::Sockets::IStreamSocket> socket;
hr = pInspectable.As(&socket);
if (FAILED(hr))
{
return glwebtools::E_INVALID_OPERATION;
}
return Create(socket);
}
glwebtools::Error Create(Microsoft::WRL::ComPtr<ABI::Windows::Networking::Sockets::IStreamSocket> socket)
{
HRESULT hr;
m_socket = socket;
// Create writer
Microsoft::WRL::ComPtr<ABI::Windows::Storage::Streams::IOutputStream> outputStream;
hr = m_socket->get_OutputStream(&outputStream);
if (FAILED(hr))
{
return glwebtools::E_INVALID_OPERATION;
}
Microsoft::WRL::ComPtr<ABI::Windows::Storage::Streams::IDataWriterFactory> writerFactory;
hr = Windows::Foundation::GetActivationFactory(Microsoft::WRL::Wrappers::HString::MakeReference(RuntimeClass_Windows_Storage_Streams_DataWriter).Get(), &writerFactory);
if (FAILED(hr))
{
return glwebtools::E_INVALID_OPERATION;
}
hr = writerFactory->CreateDataWriter(outputStream.Get(), &m_writer);
if (FAILED(hr))
{
return glwebtools::E_INVALID_OPERATION;
}
// Get socket input stream
Microsoft::WRL::ComPtr<ABI::Windows::Storage::Streams::IInputStream> inputStream;
hr = m_socket->get_InputStream(&inputStream);
if (FAILED(hr))
{
return glwebtools::E_INVALID_OPERATION;
}
//Create reader factory
Microsoft::WRL::ComPtr<ABI::Windows::Storage::Streams::IDataReaderFactory> readerFactory;
hr = Windows::Foundation::GetActivationFactory(Microsoft::WRL::Wrappers::HString::MakeReference(RuntimeClass_Windows_Storage_Streams_DataReader).Get(), &readerFactory);
if (FAILED(hr))
{
return glwebtools::E_INVALID_OPERATION;
}
// Create reader from input stream
hr = readerFactory->CreateDataReader(inputStream.Get(), &m_reader);
if (FAILED(hr))
{
return glwebtools::E_INVALID_OPERATION;
}
ABI::Windows::Storage::Streams::InputStreamOptions value = ABI::Windows::Storage::Streams::InputStreamOptions_Partial;
m_reader->put_InputStreamOptions(value);
return glwebtools::E_SUCCESS;
}
void Reset()
{
m_socket.Reset();
m_writer.Reset();
m_reader.Reset();
}
};
struct socket_handler
{
typedef Socket::Error Error;
typedef Socket::Option Option;
typedef Socket::Type Type;
typedef Socket::Proto Proto;
// TCP
Microsoft::WRL::ComPtr<ABI::Windows::Networking::Sockets::IStreamSocketListener> m_spListener;
std::vector<tcp_connection_handler> m_tcpConnections;
size_t m_lastAccepted;
EventRegistrationToken m_ConnectionReceived_token;
bool m_loadingAsync;
static const size_t min_read_bytes = 4096;
// UDP
Microsoft::WRL::ComPtr<ABI::Windows::Networking::Sockets::IDatagramSocket> m_udpSocket;
std::vector<std::tuple<Microsoft::WRL::ComPtr<ABI::Windows::Storage::Streams::IDataReader>, AddrIpv4> > m_udpMessageBuffer;
std::mutex m_udpMessageBufferMutex;
concurrency::task_completion_event<void> m_udpMessageEvent;
EventRegistrationToken m_MessageReceived_token;
// Type of socket (STREAM/TCP or DATAGRAM/UDP)
Type m_type;
bool m_blocking;
int m_connectState;
enum NonBlockConnectState
{
NONBLOCK_CONNECT_STATE_START = 0,
NONBLOCK_CONNECT_STATE_WAITING,
NONBLOCK_CONNECT_STATE_CONNECTED,
NONBLOCK_CONNECT_STATE_DISCONNECTED,
NONBLOCK_CONNECT_STATE_FAILED
};
tcp_connection_handler& GetConnection(int id)
{
return m_tcpConnections[id];
}
Error m_lastError;
bool IsOpened() const
{
return (bool)m_udpSocket || !m_tcpConnections.empty();
}
bool OpenTcp(Option option)
{
Close();
tcp_connection_handler tcp_connection;
if (GLWEBTOOLS_FAIL(tcp_connection.Create()))
return false;
m_tcpConnections.push_back(tcp_connection);
m_type = Socket::T_STREAM;
m_blocking = (option & Socket::O_BLOCKING) != 0;
m_connectState = NONBLOCK_CONNECT_STATE_DISCONNECTED;
return true;
}
bool OpenUdp(Option option)
{
Close();
HRESULT hr;
Microsoft::WRL::ComPtr<IInspectable> pInspectable;
hr = ::RoActivateInstance(Microsoft::WRL::Wrappers::HString::MakeReference(RuntimeClass_Windows_Networking_Sockets_DatagramSocket).Get(), &pInspectable);
if (FAILED(hr))
{
this->SetLastError(Socket::E_UNKNOWN);
return false;
}
hr = pInspectable.As(&m_udpSocket);
if (FAILED(hr))
{
this->SetLastError(Socket::E_UNKNOWN);
return false;
}
m_type = Socket::T_DATAGRAM;
m_blocking = (option & Socket::O_BLOCKING) != 0;
return true;
}
bool Open(Type type, Proto protocol, Option option)
{
if (type == Socket::T_STREAM)
return OpenTcp(option);
if (type == Socket::T_DATAGRAM)
return OpenUdp(option);
return false;
}
void Close()
{
// udp
if (m_udpSocket)
{
m_udpSocket->remove_MessageReceived(m_MessageReceived_token);
{
std::lock_guard<std::mutex> lock(m_udpMessageBufferMutex);
m_udpMessageBuffer.clear();
}
m_udpSocket.Reset();
}
// tcp
ResetTcpListener();
m_loadingAsync = false;
m_connectState = NONBLOCK_CONNECT_STATE_START;
}
bool Connect(const AddrIpv4& addr)
{
bool result = run_now(
[this, addr]() -> bool
{
HRESULT hr;
// Set serviceName
Microsoft::WRL::Wrappers::HString serviceName;
hr = serviceName.Set(port_itow(addr.port).c_str());
if (FAILED(hr))
{
this->SetLastError(Socket::E_UNKNOWN);
return false;
}
// Set hostName
Microsoft::WRL::Wrappers::HString hostName;
std::wstring s = inet_ntow(addr);
hr = hostName.Set(s.c_str());
if (FAILED(hr))
{
this->SetLastError(Socket::E_UNKNOWN);
return false;
}
Microsoft::WRL::ComPtr<ABI::Windows::Networking::IHostNameFactory> pHostNameFactory;
hr = Windows::Foundation::GetActivationFactory(Microsoft::WRL::Wrappers::HString::MakeReference(RuntimeClass_Windows_Networking_HostName).Get(), &pHostNameFactory);
if (FAILED(hr))
{
this->SetLastError(Socket::E_UNKNOWN);
return false;
}
Microsoft::WRL::ComPtr<ABI::Windows::Networking::IHostName> pHostName;
hr = pHostNameFactory->CreateHostName(hostName.Get(), &pHostName);
if (FAILED(hr))
{
this->SetLastError(Socket::E_UNKNOWN);
return false;
}
concurrency::task_completion_event<bool> completionEvent;
Microsoft::WRL::ComPtr<ABI::Windows::Foundation::IAsyncAction> action;
hr = m_tcpConnections.back().m_socket->ConnectAsync(pHostName.Get(), serviceName.Get(), &action);
if (FAILED(hr))
{
this->SetLastError(Socket::E_UNKNOWN);
return false;
}
hr = action->put_Completed(Microsoft::WRL::Callback<ABI::Windows::Foundation::IAsyncActionCompletedHandler>
(
[this, completionEvent](ABI::Windows::Foundation::IAsyncAction*, ABI::Windows::Foundation::AsyncStatus asyncStatus) -> HRESULT
{
completionEvent.set(asyncStatus == ABI::Windows::Foundation::AsyncStatus::Completed);
return S_OK;
}
).Get());
if (FAILED(hr))
{
this->SetLastError(Socket::E_UNKNOWN);
return false;
}
m_connectState = NONBLOCK_CONNECT_STATE_WAITING;
concurrency::task<bool> completion_task = concurrency::create_task(completionEvent);
return completion_task.get();
});
if (result == true)
m_connectState = NONBLOCK_CONNECT_STATE_CONNECTED;
else
m_connectState = NONBLOCK_CONNECT_STATE_FAILED;
return result;
}
bool IsConnected() const
{
if (m_type == Socket::T_DATAGRAM)
{
return false;
}
if (m_type == Socket::T_STREAM)
{
return m_connectState == NONBLOCK_CONNECT_STATE_CONNECTED;
}
return false;
}
void ResetTcpListener()
{
if (m_spListener)
{
m_spListener->remove_ConnectionReceived(m_ConnectionReceived_token);
}
for (auto tcpconnection : m_tcpConnections)
{
tcpconnection.Reset();
}
m_tcpConnections.clear();
m_lastAccepted = m_tcpConnections.size();
}
bool Bind(const AddrIpv4& addr)
{
if (IsOpened() == false)
return false;
if (m_type == Socket::T_STREAM)
{
return run_now(
[this, &addr]() -> bool
{
ResetTcpListener();
HRESULT hr = Windows::Foundation::ActivateInstance(Microsoft::WRL::Wrappers::HString::MakeReference(RuntimeClass_Windows_Networking_Sockets_StreamSocketListener).Get(), &m_spListener);
if (FAILED(hr))
{
return false;
}
// Set serviceName
Microsoft::WRL::Wrappers::HString serviceName;
hr = serviceName.Set(port_itow(addr.port).c_str());
if (FAILED(hr))
{
this->SetLastError(Socket::E_UNKNOWN);
return false;
}
concurrency::task_completion_event<bool> completionEvent;
Microsoft::WRL::ComPtr<ABI::Windows::Foundation::IAsyncAction> action;
hr = m_spListener->BindServiceNameAsync(serviceName.Get(), &action);
if (FAILED(hr))
{
return false;
}
hr = action->put_Completed(Microsoft::WRL::Callback<ABI::Windows::Foundation::IAsyncActionCompletedHandler>
(
[this, completionEvent](ABI::Windows::Foundation::IAsyncAction*, ABI::Windows::Foundation::AsyncStatus asyncStatus) -> HRESULT
{
completionEvent.set(asyncStatus == ABI::Windows::Foundation::AsyncStatus::Completed);
return S_OK;
}
).Get());
if (FAILED(hr))
{
this->SetLastError(Socket::E_UNKNOWN);
return false;
}
bool result = concurrency::create_task(completionEvent).get();
if (result == true && m_blocking == false)
{
// todo: Start reading async to have available bytes
}
return result;
});
}
else // udp socket
{
return run_now(
[this, &addr]() -> bool
{
HRESULT hr;
// Task completion event that is set when the request operation completes.
hr = m_udpSocket->add_MessageReceived(Microsoft::WRL::Callback<ABI::Windows::Foundation::ITypedEventHandler<ABI::Windows::Networking::Sockets::DatagramSocket*, ABI::Windows::Networking::Sockets::DatagramSocketMessageReceivedEventArgs*> >
(
[this](ABI::Windows::Networking::Sockets::IDatagramSocket* socket, ABI::Windows::Networking::Sockets::IDatagramSocketMessageReceivedEventArgs* args) -> HRESULT
{
Microsoft::WRL::ComPtr<ABI::Windows::Storage::Streams::IDataReader> reader;
// Get reader
HRESULT hr;
hr = args->GetDataReader(&reader);
if (FAILED(hr))
{
this->SetLastError(Socket::E_UNKNOWN);
return hr;
}
AddrIpv4 addr_out;
Microsoft::WRL::ComPtr<ABI::Windows::Networking::IHostName> pHostName;
hr = args->get_RemoteAddress(&pHostName);
if (FAILED(hr))
{
this->SetLastError(Socket::E_UNKNOWN);
return hr;
}
glwebtools::Error result = GetIpv4Addr(pHostName.Get(), addr_out);
{
if (GLWEBTOOLS_FAIL(result))
{
this->SetLastError(Socket::E_UNKNOWN);
return hr;
}
}
Microsoft::WRL::Wrappers::HString port;
hr = args->get_RemotePort(port.GetAddressOf());
if (FAILED(hr))
{
this->SetLastError(Socket::E_UNKNOWN);
return hr;
}
addr_out.port = port_htoi(port.Get());
{
std::lock_guard<std::mutex> lock(m_udpMessageBufferMutex);
m_udpMessageBuffer.insert(m_udpMessageBuffer.begin(), std::make_tuple(reader, addr_out));
}
if (m_blocking == true)
{
m_udpMessageEvent.set();
}
return S_OK;
}
).Get(), &m_MessageReceived_token);
if (FAILED(hr))
{
this->SetLastError(Socket::E_UNKNOWN);
return false;
}
// Set serviceName
Microsoft::WRL::Wrappers::HString serviceName;
hr = serviceName.Set(port_itow(addr.port).c_str());
if (FAILED(hr))
{
this->SetLastError(Socket::E_UNKNOWN);
return false;
}
concurrency::task_completion_event<bool> completionEvent;
Microsoft::WRL::ComPtr<ABI::Windows::Foundation::IAsyncAction> action;
hr = m_udpSocket->BindServiceNameAsync(serviceName.Get(), &action);
if (FAILED(hr))
{
return false;
}
hr = action->put_Completed(Microsoft::WRL::Callback<ABI::Windows::Foundation::IAsyncActionCompletedHandler>
(
[this, completionEvent](ABI::Windows::Foundation::IAsyncAction*, ABI::Windows::Foundation::AsyncStatus asyncStatus) -> HRESULT
{
completionEvent.set(asyncStatus == ABI::Windows::Foundation::AsyncStatus::Completed);
return S_OK;
}
).Get());
if (FAILED(hr))
{
this->SetLastError(Socket::E_UNKNOWN);
return false;
}
return concurrency::create_task(completionEvent).get();
});
}
}
bool Listen(int backlog)
{
if (m_spListener == false)
return false;
if ((size_t)backlog < m_tcpConnections.size())
{
auto event = Microsoft::WRL::Callback<__FITypedEventHandler_2_Windows__CNetworking__CSockets__CStreamSocketListener_Windows__CNetworking__CSockets__CStreamSocketListenerConnectionReceivedEventArgs >
([this](ABI::Windows::Networking::Sockets::IStreamSocketListener* listener, ABI::Windows::Networking::Sockets::IStreamSocketListenerConnectionReceivedEventArgs* args)->HRESULT
{
Microsoft::WRL::ComPtr<ABI::Windows::Networking::Sockets::IStreamSocket> tcpSocket;
HRESULT hr = args->get_Socket(&tcpSocket);
if (FAILED(hr))
{
this->SetLastError(Socket::E_UNKNOWN);
return hr;
}
tcp_connection_handler tcp_connection;
if (GLWEBTOOLS_FAIL(tcp_connection.Create(tcpSocket)))
{
this->SetLastError(Socket::E_UNKNOWN);
return S_FALSE;
}
m_tcpConnections.push_back(tcp_connection);
return S_OK;
});
m_spListener->add_ConnectionReceived(event.Get(), &m_ConnectionReceived_token);
m_connectState = NONBLOCK_CONNECT_STATE_WAITING;
}
return true;
}
int Accept(AddrIpv4& addr)
{
if (m_connectState != NONBLOCK_CONNECT_STATE_WAITING)
return -1;
if (m_spListener == false)
return -1;
if (m_tcpConnections.empty())
return -1;
if (m_tcpConnections.back().valid() == false)
return -1;
// check if already accepted
if (m_lastAccepted >= m_tcpConnections.size())
return -1;
if (GetRemoteAddress(addr) == false)
return -1;
++m_lastAccepted;
m_connectState = NONBLOCK_CONNECT_STATE_CONNECTED;
return m_lastAccepted-1;
}
bool Reject(int id)
{
if ((size_t)id < m_tcpConnections.size())
{
GetConnection(id).Reset();
}
return false;
}
int Send(const void* buf, int size, int id = 0)
{
if (IsConnected() == false)
return -1;
if (buf == 0 || size < 0)
return -1;
if (size == 0)
return 0;
return run_now(
[this, buf, size, id]() -> int
{
HRESULT hr;
if (GetConnection(id).valid() == false)
return false;
hr = GetConnection(id).m_writer->WriteBytes(size, (BYTE*)buf);
if (FAILED(hr))
{
this->SetLastError(Socket::E_UNKNOWN);
return -1;
}
Microsoft::WRL::ComPtr<ABI::Windows::Foundation::IAsyncOperation<unsigned int> > action;
hr = GetConnection(id).m_writer->StoreAsync(&action);
if (FAILED(hr))
{
this->SetLastError(Socket::E_UNKNOWN);
return -1;
}
// Task completion event that is set when the request operation completes.
concurrency::task_completion_event<unsigned int> completionEvent;
hr = action->put_Completed(Microsoft::WRL::Callback<ABI::Windows::Foundation::IAsyncOperationCompletedHandler<unsigned int> >(
[this, completionEvent](ABI::Windows::Foundation::IAsyncOperation<unsigned int>* operation, ABI::Windows::Foundation::AsyncStatus asyncStatus)->HRESULT
{
unsigned int result = 0;
if (asyncStatus == ABI::Windows::Foundation::AsyncStatus::Error)
{
this->SetLastError(Socket::E_UNKNOWN);
}
if (asyncStatus == ABI::Windows::Foundation::AsyncStatus::Completed)
{
HRESULT hr = operation->GetResults(&result);
if (FAILED(hr))
{
this->SetLastError(Socket::E_UNKNOWN);
}
}
completionEvent.set(result);
return S_OK;
}).Get());
if (FAILED(hr))
{
this->SetLastError(Socket::E_UNKNOWN);
return -1;
}
concurrency::task<unsigned int> completion_task = concurrency::create_task(completionEvent);
return completion_task.get();
});
}
int SendTo(const AddrIpv4& addr, const void* buf, int size)
{
if (m_udpSocket == false)
return -1;
if (buf == 0 || size < 0)
return -1;
if (size == 0)
return 0;
return run_now(
[this, addr, buf, size]() -> int
{
HRESULT hr;
// Set serviceName
Microsoft::WRL::Wrappers::HString serviceName;
hr = serviceName.Set(port_itow(addr.port).c_str());
if (FAILED(hr))
{
this->SetLastError(Socket::E_UNKNOWN);
return false;
}
// Set hostName string
Microsoft::WRL::Wrappers::HString hostName;
std::wstring s = inet_ntow(addr);
hr = hostName.Set(s.c_str());
if (FAILED(hr))
{
this->SetLastError(Socket::E_UNKNOWN);
return false;
}
// Create HostName Factory
Microsoft::WRL::ComPtr<ABI::Windows::Networking::IHostNameFactory> pHostNameFactory;
hr = Windows::Foundation::GetActivationFactory(Microsoft::WRL::Wrappers::HString::MakeReference(RuntimeClass_Windows_Networking_HostName).Get(), &pHostNameFactory);
if (FAILED(hr))
{
this->SetLastError(Socket::E_UNKNOWN);
return false;
}
// Create HostName from string
Microsoft::WRL::ComPtr<ABI::Windows::Networking::IHostName> pHostName;
hr = pHostNameFactory->CreateHostName(hostName.Get(), &pHostName);
if (FAILED(hr))
{
this->SetLastError(Socket::E_UNKNOWN);
return false;
}
// Create OuputStream
Microsoft::WRL::ComPtr<ABI::Windows::Storage::Streams::IOutputStream> outputStream;
{
// local completionEvent
concurrency::task_completion_event<ABI::Windows::Storage::Streams::IOutputStream*> completionEvent;
Microsoft::WRL::ComPtr<ABI::Windows::Foundation::IAsyncOperation<ABI::Windows::Storage::Streams::IOutputStream*> > operation;
hr = m_udpSocket->GetOutputStreamAsync(pHostName.Get(), serviceName.Get(), operation.GetAddressOf());
if (FAILED(hr))
{
this->SetLastError(Socket::E_UNKNOWN);
return -1;
}
hr = operation->put_Completed(Microsoft::WRL::Callback<__FIAsyncOperationCompletedHandler_1_Windows__CStorage__CStreams__CIOutputStream>
(
[this, completionEvent](ABI::Windows::Foundation::IAsyncOperation<ABI::Windows::Storage::Streams::IOutputStream*>* operation, ABI::Windows::Foundation::AsyncStatus asyncStatus) -> HRESULT
{
ABI::Windows::Storage::Streams::IOutputStream* outputStream = 0;
if (asyncStatus == ABI::Windows::Foundation::AsyncStatus::Error)
{
this->SetLastError(Socket::E_UNKNOWN);
}
// Get ouput stream
HRESULT hr = operation->GetResults(&outputStream);
if (FAILED(hr))
{
this->SetLastError(Socket::E_UNKNOWN);
}
completionEvent.set(outputStream);
return S_OK;
}
).Get());
if (FAILED(hr))
{
this->SetLastError(Socket::E_UNKNOWN);
return -1;
}
outputStream = concurrency::create_task(completionEvent).get();
}
if (outputStream == 0)
return -1;
// Create writer factory
Microsoft::WRL::ComPtr<ABI::Windows::Storage::Streams::IDataWriterFactory> writerFactory;
hr = Windows::Foundation::GetActivationFactory(Microsoft::WRL::Wrappers::HString::MakeReference(RuntimeClass_Windows_Storage_Streams_DataWriter).Get(), &writerFactory);
if (FAILED(hr))
{
this->SetLastError(Socket::E_UNKNOWN);
return -1;
}
// Create writer
Microsoft::WRL::ComPtr<ABI::Windows::Storage::Streams::IDataWriter> writer;
hr = writerFactory->CreateDataWriter(outputStream.Get(), &writer);
if (FAILED(hr))
{
this->SetLastError(Socket::E_UNKNOWN);
return -1;
}
hr = writer->WriteBytes(size, (BYTE*)buf);
if (FAILED(hr))
{
this->SetLastError(Socket::E_UNKNOWN);
return -1;
}
Microsoft::WRL::ComPtr<ABI::Windows::Foundation::IAsyncOperation<unsigned int> > action;
hr = writer->StoreAsync(&action);
if (FAILED(hr))
{
this->SetLastError(Socket::E_UNKNOWN);
return -1;
}
// Task completion event that is set when the request operation completes.
concurrency::task_completion_event<unsigned int> completionEvent;
hr = action->put_Completed(Microsoft::WRL::Callback<__FIAsyncOperationCompletedHandler_1_UINT32>(
[this, completionEvent](ABI::Windows::Foundation::IAsyncOperation<unsigned int>* operation, ABI::Windows::Foundation::AsyncStatus asyncStatus)->HRESULT
{
unsigned int result = 0;
if (asyncStatus == ABI::Windows::Foundation::AsyncStatus::Error)
{
this->SetLastError(Socket::E_UNKNOWN);
}
if (asyncStatus == ABI::Windows::Foundation::AsyncStatus::Completed)
{
HRESULT hr = operation->GetResults(&result);
if (FAILED(hr))
{
this->SetLastError(Socket::E_UNKNOWN);
}
}
completionEvent.set(result);
return S_OK;
}).Get());
if (FAILED(hr))
{
this->SetLastError(Socket::E_UNKNOWN);
return -1;
}
return concurrency::create_task(completionEvent).get();
});
return 0;
}
bool IsReadable(int id = 0)
{
if (m_type == Socket::T_STREAM)
{
// In connection-oriented socket, if the socket is closed recv should return immmediately
if (IsOpened() == false)
return true;
HRESULT hr;
if (GetConnection(id).valid() == false)
return false;
unsigned int buffer_length;
hr = GetConnection(id).m_reader->get_UnconsumedBufferLength(&buffer_length);
if (FAILED(hr))
{
return false;
}
if (buffer_length > 0)
return true;
if (m_loadingAsync == false)
{
m_loadingAsync = true;
return run_now(
[this, id]() -> bool
{
HRESULT hr;
// Try to read data
Microsoft::WRL::ComPtr<ABI::Windows::Foundation::IAsyncOperation<unsigned int> > action;
hr = GetConnection(id).m_reader->LoadAsync(min_read_bytes, &action);
if (FAILED(hr))
{
return false;
}
// Set receive completion handler
hr = action->put_Completed(Microsoft::WRL::Callback<__FIAsyncOperationCompletedHandler_1_UINT32 >(
[this](ABI::Windows::Foundation::IAsyncOperation<unsigned int>* operation, ABI::Windows::Foundation::AsyncStatus asyncStatus)->HRESULT
{
m_loadingAsync = false;
return S_OK;
}).Get());
// not readable now, so return false
return false;
});
}
return false;
}
else
{
return (m_udpMessageBuffer.empty() == false);
}
return false;
}
int Receive(void* buf_out, int size, int id = 0)
{
if (IsConnected() == false)
return -1;
if (buf_out == 0 || size < 0)
return -1;
if (size == 0)
return 0;
return run_now(
[this, buf_out, size, id]() -> int
{
HRESULT hr;
if (GetConnection(id).valid() == false)
return -1;
UINT32 buffer_length;
hr = GetConnection(id).m_reader->get_UnconsumedBufferLength(&buffer_length);
if (buffer_length == 0)
{
if (m_loadingAsync == true)
return -1; // already waiting for data. No data available.
// Read async into reader
Microsoft::WRL::ComPtr<ABI::Windows::Foundation::IAsyncOperation<unsigned int> > action;
hr = GetConnection(id).m_reader->LoadAsync(size-buffer_length, &action);
if (FAILED(hr))
{
this->SetLastError(Socket::E_UNKNOWN);
return -1;
}
// Task completion event that is set when the request operation completes.
concurrency::task_completion_event<unsigned int> completionEvent;
m_loadingAsync = true;
// Set receive completion handler
hr = action->put_Completed(Microsoft::WRL::Callback<__FIAsyncOperationCompletedHandler_1_UINT32 >(
[this, id, buf_out, size, completionEvent](ABI::Windows::Foundation::IAsyncOperation<unsigned int>* operation, ABI::Windows::Foundation::AsyncStatus asyncStatus)->HRESULT
{
HRESULT hr;
m_loadingAsync = false;
unsigned int buffer_length = 0;
if (asyncStatus == ABI::Windows::Foundation::AsyncStatus::Error)
{
this->SetLastError(Socket::E_UNKNOWN);
}
if (asyncStatus == ABI::Windows::Foundation::AsyncStatus::Completed)
{
hr = GetConnection(id).m_reader->get_UnconsumedBufferLength(&buffer_length);
if (buffer_length > 0)
{
buffer_length = (std::min)(buffer_length, (unsigned int)size);
hr = GetConnection(id).m_reader->ReadBytes(buffer_length, (BYTE*)buf_out);
if (FAILED(hr))
{
this->SetLastError(Socket::E_UNKNOWN);
}
}
}
if (m_blocking == true)
{
completionEvent.set(buffer_length);
}
return S_OK;
}).Get());
if (FAILED(hr))
{
this->SetLastError(Socket::E_UNKNOWN);
return -1;
}
if (m_blocking == true)
{
return concurrency::create_task(completionEvent).get();
}
return 0;
}
else
{
// read available bytes
buffer_length = (std::min)(buffer_length, (unsigned int)size);
hr = GetConnection(id).m_reader->ReadBytes(buffer_length, (BYTE*)buf_out);
if (FAILED(hr))
{
return -1;
}
if (m_loadingAsync == false)
{
m_loadingAsync = true;
// Try to read data
Microsoft::WRL::ComPtr<ABI::Windows::Foundation::IAsyncOperation<unsigned int> > action;
hr = GetConnection(id).m_reader->LoadAsync(size-buffer_length, &action);
if (FAILED(hr))
{
return false;
}
// Set receive completion handler
hr = action->put_Completed(Microsoft::WRL::Callback<__FIAsyncOperationCompletedHandler_1_UINT32 >(
[this](ABI::Windows::Foundation::IAsyncOperation<unsigned int>* operation, ABI::Windows::Foundation::AsyncStatus asyncStatus)->HRESULT
{
m_loadingAsync = false;
return S_OK;
}).Get());
}
return buffer_length;
}
});
}
int ReceiveFrom(AddrIpv4& addr_out, void* buf_out, int size)
{
if (IsOpened() == false)
return -1;
if (buf_out == 0 || size < 0)
return -1;
if (size == 0)
return 0;
return run_now(
[this, &addr_out, buf_out, size]() -> int
{
HRESULT hr;
if (m_blocking == true)
{
concurrency::create_task(m_udpMessageEvent).wait();
}
Microsoft::WRL::ComPtr<ABI::Windows::Storage::Streams::IDataReader> reader;
{
std::lock_guard<std::mutex> lock(m_udpMessageBufferMutex);
if (m_udpMessageBuffer.empty())
return 0;
auto message = m_udpMessageBuffer.back();
m_udpMessageBuffer.pop_back();
reader = std::get<0>(message);
addr_out = std::get<1>(message);
}
unsigned int result;
hr = reader->get_UnconsumedBufferLength(&result);
if (FAILED(hr))
{
this->SetLastError(Socket::E_UNKNOWN);
return -1;
}
if (result == 0)
return 0;
result = (std::min)((unsigned int)size, result);
hr = reader->ReadBytes(result, (BYTE*)buf_out);
if (FAILED(hr))
{
this->SetLastError(Socket::E_UNKNOWN);
return -1;
}
return (int)result;
});
return false;
}
bool GetAddress(AddrIpv4& addr)
{
if (IsOpened() == false)
return false;
Microsoft::WRL::ComPtr<ABI::Windows::Networking::IHostName> pHostName;
Microsoft::WRL::Wrappers::HString port;
HRESULT hr;
if (m_type == Socket::T_STREAM)
{
for (auto tcpConnection: m_tcpConnections)
{
if (tcpConnection.valid() == false)
continue;
Microsoft::WRL::ComPtr<ABI::Windows::Networking::Sockets::IStreamSocketInformation> socketinfo;
hr = tcpConnection.m_socket->get_Information(&socketinfo);
if (FAILED(hr))
{
return false;
}
hr = socketinfo->get_LocalAddress(&pHostName);
if (FAILED(hr))
{
return false;
}
hr = socketinfo->get_LocalPort(port.GetAddressOf());
if (FAILED(hr))
{
return false;
}
return true;
}
return false;
}
else // udp socket
{
Microsoft::WRL::ComPtr<ABI::Windows::Networking::Sockets::IDatagramSocketInformation> socketinfo;
hr = m_udpSocket->get_Information(&socketinfo);
if (FAILED(hr))
{
return false;
}
hr = socketinfo->get_LocalAddress(&pHostName);
if (FAILED(hr))
{
return false;
}
hr = socketinfo->get_LocalPort(port.GetAddressOf());
if (FAILED(hr))
{
return false;
}
}
if (GLWEBTOOLS_FAIL(GetIpv4Addr(pHostName.Get(), addr)))
{
return false;
}
addr.port = port_htoi(port.Get());
return true;
}
bool GetRemoteAddress(AddrIpv4& addr)
{
if (IsOpened() == false)
return false;
Microsoft::WRL::ComPtr<ABI::Windows::Networking::IHostName> pHostName;
Microsoft::WRL::Wrappers::HString port;
HRESULT hr;
if (m_type == Socket::T_STREAM)
{
for (auto tcpConnection: m_tcpConnections)
{
if (tcpConnection.valid() == false)
continue;
Microsoft::WRL::ComPtr<ABI::Windows::Networking::Sockets::IStreamSocketInformation> socketinfo;
hr = tcpConnection.m_socket->get_Information(&socketinfo);
if (FAILED(hr))
{
return false;
}
hr = socketinfo->get_RemoteAddress(&pHostName);
if (FAILED(hr))
{
return false;
}
hr = socketinfo->get_RemotePort(port.GetAddressOf());
if (FAILED(hr))
{
return false;
}
return true;
}
return false;
}
else // udp socket
{
Microsoft::WRL::ComPtr<ABI::Windows::Networking::Sockets::IDatagramSocketInformation> socketinfo;
hr = m_udpSocket->get_Information(&socketinfo);
if (FAILED(hr))
{
return false;
}
hr = socketinfo->get_RemoteAddress(&pHostName);
if (FAILED(hr))
{
return false;
}
hr = socketinfo->get_RemotePort(port.GetAddressOf());
if (FAILED(hr))
{
return false;
}
}
if (GLWEBTOOLS_FAIL(GetIpv4Addr(pHostName.Get(), addr)))
{
return false;
}
addr.port = port_htoi(port.Get());
return true;
}
Error GetLastError() const
{
return m_lastError;
}
void SetLastError(Error error)
{
m_lastError = error;
}
void ClearError()
{
m_lastError = Socket::E_NONE;
}
static Error Init()
{
return Socket::E_NONE;
}
static Error Deinit()
{
return Socket::E_NONE;
}
static std::string GetHostName()
{
HRESULT hr;
Microsoft::WRL::ComPtr<ABI::Windows::Networking::Connectivity::INetworkInformationStatics> networkInformation;
hr = Windows::Foundation::GetActivationFactory(Microsoft::WRL::Wrappers::HString::MakeReference(RuntimeClass_Windows_Networking_Connectivity_NetworkInformation).Get(), &networkInformation);
if (FAILED(hr))
{
return "";
}
Microsoft::WRL::ComPtr<__FIVectorView_1_Windows__CNetworking__CHostName> hostNames;
hr = networkInformation->GetHostNames(&hostNames);
if (FAILED(hr))
{
return "";
}
Microsoft::WRL::ComPtr<ABI::Windows::Networking::IHostName> hostName;
hr = hostNames->GetAt(0, &hostName);
if (FAILED(hr))
{
return "";
}
Microsoft::WRL::Wrappers::HString hsHostName;
hr = hostName->get_DisplayName(hsHostName.GetAddressOf());
if (FAILED(hr))
{
return "";
}
return htos(hsHostName.Get());
}
static const char *GetAddrIP(const AddrIpv4 &addr)
{
return inet_ntoa(addr).c_str();
}
static AddrIpv4 MakeAddr(const char *ip, int port)
{
AddrIpv4 addr;
addr.Addr.ulongAddr = inet_addr(ip);
addr.port = (unsigned short)port;
return addr;
}
static int ResolveHostTCP(const char* host, int port, AddrIpv4 &address)
{
HRESULT hr;
Microsoft::WRL::ComPtr<IInspectable> pInspectable;
hr = ::RoActivateInstance(Microsoft::WRL::Wrappers::HString::MakeReference(RuntimeClass_Windows_Networking_Sockets_DatagramSocket).Get(), &pInspectable);
if (FAILED(hr))
{
return -1;
}
Microsoft::WRL::ComPtr<ABI::Windows::Networking::Sockets::IDatagramSocket> udpSocket;
hr = pInspectable.As(&udpSocket);
if (FAILED(hr))
{
return -1;
}
bool result = run_now(
[udpSocket, host, port]() -> bool
{
HRESULT hr;
// Set serviceName
Microsoft::WRL::Wrappers::HString serviceName;
hr = serviceName.Set(port_itow(port).c_str());
if (FAILED(hr))
{
return false;
}
// Set hostName
Microsoft::WRL::Wrappers::HString hostName;
std::wstring s = stows(host);
hr = hostName.Set(s.c_str());
if (FAILED(hr))
{
return false;
}
Microsoft::WRL::ComPtr<ABI::Windows::Networking::IHostNameFactory> pHostNameFactory;
hr = Windows::Foundation::GetActivationFactory(Microsoft::WRL::Wrappers::HString::MakeReference(RuntimeClass_Windows_Networking_HostName).Get(), &pHostNameFactory);
if (FAILED(hr))
{
return false;
}
Microsoft::WRL::ComPtr<ABI::Windows::Networking::IHostName> pHostName;
hr = pHostNameFactory->CreateHostName(hostName.Get(), &pHostName);
if (FAILED(hr))
{
return false;
}
concurrency::task_completion_event<bool> completionEvent;
Microsoft::WRL::ComPtr<ABI::Windows::Foundation::IAsyncAction> action;
hr = udpSocket->ConnectAsync(pHostName.Get(), serviceName.Get(), &action);
if (FAILED(hr))
{
return false;
}
hr = action->put_Completed(Microsoft::WRL::Callback<ABI::Windows::Foundation::IAsyncActionCompletedHandler>
(
[completionEvent](ABI::Windows::Foundation::IAsyncAction*, ABI::Windows::Foundation::AsyncStatus asyncStatus) -> HRESULT
{
completionEvent.set(asyncStatus == ABI::Windows::Foundation::AsyncStatus::Completed);
return S_OK;
}
).Get());
if (FAILED(hr))
{
return false;
}
concurrency::task<bool> completion_task = concurrency::create_task(completionEvent);
return completion_task.get();
});
if (result == false)
return -1;
Microsoft::WRL::ComPtr<ABI::Windows::Networking::Sockets::IDatagramSocketInformation> socketinfo;
hr = udpSocket->get_Information(&socketinfo);
if (FAILED(hr))
{
return -1;
}
Microsoft::WRL::ComPtr<ABI::Windows::Networking::IHostName> pHostName;
hr = socketinfo->get_RemoteAddress(&pHostName);
if (FAILED(hr))
{
return -1;
}
Microsoft::WRL::Wrappers::HString hsport;
hr = socketinfo->get_RemotePort(hsport.GetAddressOf());
if (FAILED(hr))
{
return -1;
}
if (GLWEBTOOLS_FAIL(GetIpv4Addr(pHostName.Get(), address)))
{
return -1;
}
address.port = port_htoi(hsport.Get());
return 0;
}
};
}
Socket::Socket()
{
m_handler = static_cast<void*>(GLWEBTOOLS_NEW socket_handler());
#if GLWEBTOOLS_DEBUG
if(!m_handler)
{
GLWEBTOOLS_ASSERT(m_handler);
GLWEBTOOLS_LOG_MAJOR_ERROR("%s", "Could not allocate socket");
}
#endif
}
Socket::~Socket()
{
if(m_handler)
{
socket_handler* socket= static_cast<socket_handler*>(m_handler);
GLWEBTOOLS_DELETE(socket);
m_handler = 0;
}
}
bool Socket::IsOpened() const
{
if(m_handler)
{
socket_handler* socket = static_cast<socket_handler*>(m_handler);
return socket->IsOpened();
}
return false;
}
bool Socket::OpenTcp(Option option)
{
if(m_handler)
{
socket_handler* socket = static_cast<socket_handler*>(m_handler);
return socket->OpenTcp(static_cast<socket_handler::Option>(option));
}
return false;
}
bool Socket::OpenUdp(Option option)
{
if(m_handler)
{
socket_handler* socket = static_cast<socket_handler*>(m_handler);
return socket->OpenUdp(static_cast<socket_handler::Option>(option));
}
return false;
}
bool Socket::Open(Type type, Proto protocol, Option option)
{
if(m_handler)
{
socket_handler* socket = static_cast<socket_handler*>(m_handler);
return socket->Open(static_cast<socket_handler::Type>(type),
static_cast<socket_handler::Proto>(protocol),
static_cast<socket_handler::Option>(option));
}
return false;
}
void Socket::Close()
{
if(m_handler)
{
socket_handler* socket = static_cast<socket_handler*>(m_handler);
socket->Close();
}
}
bool Socket::Connect(const AddrIpv4& addr)
{
if(m_handler)
{
socket_handler* socket = static_cast<socket_handler*>(m_handler);
return socket->Connect(addr);
}
return false;
}
bool Socket::IsConnected() const
{
if(m_handler)
{
socket_handler* socket = static_cast<socket_handler*>(m_handler);
return socket->IsConnected();
}
return false;
}
bool Socket::Bind(const AddrIpv4& addr)
{
if(m_handler)
{
socket_handler* socket = static_cast<socket_handler*>(m_handler);
return socket->Bind(addr);
}
return false;
}
bool Socket::Listen(int backlog)
{
if(m_handler)
{
socket_handler* socket = static_cast<socket_handler*>(m_handler);
return socket->Listen(backlog);
}
return false;
}
int Socket::Accept(AddrIpv4& addr)
{
if(m_handler)
{
socket_handler* socket = static_cast<socket_handler*>(m_handler);
return socket->Accept(addr);
}
return -1;
}
bool Socket::Reject(int id)
{
if(m_handler)
{
socket_handler* socket = static_cast<socket_handler*>(m_handler);
return socket->Reject(id);
}
return false;
}
int Socket::Send(const void* buf, int size, int id)
{
if(m_handler)
{
socket_handler* socket = static_cast<socket_handler*>(m_handler);
return socket->Send(buf, size, id);
}
return -1;
}
int Socket::SendTo(const AddrIpv4& addr, const void* buf, int size)
{
if(m_handler)
{
socket_handler* socket = static_cast<socket_handler*>(m_handler);
return socket->SendTo(addr, buf, size);
}
return -1;
}
bool Socket::IsReadable(int id) const
{
if(m_handler)
{
socket_handler* socket = static_cast<socket_handler*>(m_handler);
return socket->IsReadable(id);
}
return false;
}
int Socket::Receive(void* buf_out, int size, int id)
{
if(m_handler)
{
socket_handler* socket = static_cast<socket_handler*>(m_handler);
return socket->Receive(buf_out, size, id);
}
return -1;
}
int Socket::ReceiveFrom(AddrIpv4& addr_out, void* buf_out, int size)
{
if(m_handler)
{
socket_handler* socket = static_cast<socket_handler*>(m_handler);
int result = socket->ReceiveFrom(addr_out, buf_out, size);
return result;
}
return -1;
}
bool Socket::GetAddress(AddrIpv4& addr)
{
if(m_handler)
{
socket_handler* socket = static_cast<socket_handler*>(m_handler);
bool result = socket->GetAddress(addr);
return result;
}
return false;
}
Socket::Error Socket::GetLastError() const
{
if(m_handler)
{
socket_handler* socket = static_cast<socket_handler*>(m_handler);
return static_cast<Socket::Error>(socket->GetLastError());
}
return E_NOTINITIALIZED;
}
void Socket::ClearError()
{
if(m_handler)
{
socket_handler* socket = static_cast<socket_handler*>(m_handler);
socket->ClearError();
}
}
int Socket::ResolveHostTCP(const char* host, int port, glwebtools::AddrIpv4 &address)
{
return socket_handler::ResolveHostTCP(host, port, address);
}
Socket::Error Socket::Init()
{
socket_handler::Error err = socket_handler::Init();
return static_cast<Socket::Error>(err);
}
Socket::Error Socket::Deinit()
{
return static_cast<Socket::Error>(socket_handler::Deinit());
}
std::string Socket::GetHostName()
{
return socket_handler::GetHostName();
}
const char *Socket::GetAddrIP(const AddrIpv4 &address)
{
return socket_handler::GetAddrIP(address);
}
AddrIpv4 Socket::MakeAddr(const char *ip, int port)
{
glwebtools::AddrIpv4 address = socket_handler::MakeAddr(ip, port);
return address;
}
}
#endif //GLWEBTOOLS_USE_METRO_SOCKET