148 lines
3.4 KiB
C++
148 lines
3.4 KiB
C++
#include "td/network/TCPSocket.h"
|
|
#include "td/misc/Compression.h"
|
|
|
|
#include <iostream>
|
|
|
|
#ifdef _WIN32
|
|
#define WOULDBLOCK WSAEWOULDBLOCK
|
|
#define MSG_DONTWAIT 0
|
|
#else
|
|
#define WOULDBLOCK EWOULDBLOCK
|
|
#endif
|
|
|
|
namespace td {
|
|
namespace network {
|
|
|
|
TCPSocket::TCPSocket() : Socket(Socket::TCP), m_Port(0) {
|
|
m_Handle = static_cast<SocketHandle>(INVALID_SOCKET);
|
|
}
|
|
|
|
bool TCPSocket::Connect(const IPAddress& address, unsigned short port) {
|
|
if (this->GetStatus() == Connected)
|
|
return true;
|
|
|
|
struct addrinfo hints {};
|
|
memset(&hints, 0, sizeof(addrinfo));
|
|
|
|
struct addrinfo* result = nullptr;
|
|
|
|
hints.ai_family = AF_INET;
|
|
hints.ai_socktype = SOCK_STREAM;
|
|
hints.ai_protocol = IPPROTO_TCP;
|
|
|
|
if (::getaddrinfo(address.ToString().c_str(), std::to_string(static_cast<int>(port)).c_str(), &hints, &result) != 0) {
|
|
std::cerr << "Failed to get address info !\n";
|
|
return false;
|
|
}
|
|
|
|
if ((m_Handle = ::socket(AF_INET, SOCK_STREAM, IPPROTO_TCP)) < 0) {
|
|
std::cerr << "Failed to create socket !\n";
|
|
return false;
|
|
}
|
|
|
|
struct addrinfo* ptr = nullptr;
|
|
for (ptr = result; ptr != nullptr; ptr = ptr->ai_next) {
|
|
struct sockaddr* sockaddr = ptr->ai_addr;
|
|
if (::connect(m_Handle, sockaddr, sizeof(sockaddr_in)) != 0) {
|
|
std::cerr << "Failed to connect with this address !\n";
|
|
continue;
|
|
}
|
|
m_RemoteAddr = *sockaddr;
|
|
break;
|
|
}
|
|
|
|
freeaddrinfo(result);
|
|
|
|
if (!ptr) {
|
|
std::cerr << "Could not find a suitable interface for connecting !\n";
|
|
return false;
|
|
}
|
|
|
|
this->SetStatus(Connected);
|
|
m_RemoteIP = address;
|
|
m_Port = port;
|
|
return true;
|
|
}
|
|
|
|
size_t TCPSocket::Send(const unsigned char* data, size_t size) {
|
|
if (this->GetStatus() != Connected)
|
|
return 0;
|
|
|
|
size_t sent = 0;
|
|
|
|
while (sent < size) {
|
|
int cur = ::send(m_Handle, reinterpret_cast<const char*>(data + sent), size - sent, 0);
|
|
if (cur <= 0) {
|
|
Disconnect();
|
|
return 0;
|
|
}
|
|
sent += static_cast<std::size_t>(cur);
|
|
}
|
|
|
|
return sent;
|
|
}
|
|
|
|
std::size_t TCPSocket::Receive(DataBuffer& buffer, std::size_t amount) {
|
|
buffer.Resize(amount);
|
|
buffer.SetReadOffset(0);
|
|
|
|
int recvAmount = ::recv(m_Handle, reinterpret_cast<char*>(buffer.data()), amount, 0);
|
|
if (recvAmount <= 0) {
|
|
#if defined(_WIN32) || defined(WIN32)
|
|
int err = WSAGetLastError();
|
|
#else
|
|
int err = errno;
|
|
#endif
|
|
if (err == WOULDBLOCK) {
|
|
buffer.Clear();
|
|
return 0;
|
|
}
|
|
|
|
Disconnect();
|
|
buffer.Clear();
|
|
return 0;
|
|
}
|
|
buffer.Resize(static_cast<std::size_t>(recvAmount));
|
|
return static_cast<std::size_t>(recvAmount);
|
|
}
|
|
|
|
DataBuffer TCPSocket::Receive(std::size_t amount) {
|
|
std::unique_ptr<char[]> buf(new char[amount]);
|
|
|
|
int received = ::recv(m_Handle, buf.get(), amount, 0);
|
|
|
|
if (received <= 0) {
|
|
#if defined(_WIN32) || defined(WIN32)
|
|
int err = WSAGetLastError();
|
|
#else
|
|
int err = errno;
|
|
#endif
|
|
if (err == WOULDBLOCK)
|
|
return DataBuffer();
|
|
|
|
Disconnect();
|
|
return DataBuffer();
|
|
}
|
|
|
|
return DataBuffer(std::string(buf.get(), static_cast<std::size_t>(received)));
|
|
}
|
|
|
|
TCPSocket::TCPSocket(TCPSocket&& other) : Socket(TCP) {
|
|
m_Handle = other.m_Handle;
|
|
m_Port = other.m_Port;
|
|
m_RemoteAddr = other.m_RemoteAddr;
|
|
m_RemoteIP = other.m_RemoteIP;
|
|
SetStatus(other.GetStatus());
|
|
SetBlocking(other.IsBlocking());
|
|
other.m_Handle = static_cast<SocketHandle>(INVALID_SOCKET);
|
|
}
|
|
|
|
void SendPacket(const DataBuffer& data, network::TCPSocket& socket) {
|
|
DataBuffer compressed = utils::Compress(data);
|
|
socket.Send(reinterpret_cast<const std::uint8_t*>(compressed.ToString().data()), compressed.GetSize());
|
|
}
|
|
|
|
|
|
} // ns network
|
|
} // ns mc
|