157 lines
3.6 KiB
C++
157 lines
3.6 KiB
C++
#include "TCP.h"
|
|
|
|
void TCPSocket::create_socket() {
|
|
sockfd_ = socket(AF_INET, SOCK_STREAM, 0);
|
|
if (sockfd_ == INVALID_SOCKET_VALUE) {
|
|
throw std::runtime_error("Socket creation failed");
|
|
}
|
|
}
|
|
|
|
void TCPSocket::close_socket() {
|
|
if (sockfd_ != INVALID_SOCKET_VALUE) {
|
|
#ifdef _WIN32
|
|
closesocket(sockfd_);
|
|
#else
|
|
close(sockfd_);
|
|
#endif
|
|
sockfd_ = INVALID_SOCKET_VALUE;
|
|
}
|
|
connected_.store(false);
|
|
}
|
|
|
|
TCPSocket::~TCPSocket() {
|
|
close_socket();
|
|
}
|
|
|
|
bool TCPSocket::is_connected() const { return connected_.load(); }
|
|
|
|
void TCPClient::connect(const std::string &ip, uint16_t port) {
|
|
create_socket();
|
|
|
|
sockaddr_in addr{};
|
|
addr.sin_family = AF_INET;
|
|
addr.sin_port = htons(port);
|
|
inet_pton(AF_INET, ip.c_str(), &addr.sin_addr);
|
|
|
|
if (::connect(sockfd_, (sockaddr *) &addr, sizeof(addr)) != 0) {
|
|
close_socket();
|
|
throw std::runtime_error("Connection failed");
|
|
}
|
|
connected_.store(true);
|
|
}
|
|
|
|
size_t TCPClient::send(const void *data, size_t size) {
|
|
#ifdef _WIN32
|
|
auto sent = ::send(sockfd_, (const char *) data, (int) size, 0);
|
|
#else
|
|
auto sent = ::send(sockfd_, data, size, 0);
|
|
#endif
|
|
|
|
if (sent == SOCKET_ERROR) {
|
|
connected_.store(false);
|
|
throw std::runtime_error("Send failed");
|
|
}
|
|
return static_cast<size_t>(sent);
|
|
}
|
|
|
|
size_t TCPClient::send(const std::string &data) {
|
|
#ifdef _WIN32
|
|
auto sent = ::send(sockfd_, data.c_str(), static_cast<int>(data.size()), 0);
|
|
#else
|
|
auto sent = ::send(sockfd_, data.c_str(), data.size(), 0);
|
|
#endif
|
|
|
|
if (sent == SOCKET_ERROR) {
|
|
connected_.store(false);
|
|
throw std::runtime_error("Send failed");
|
|
}
|
|
return static_cast<size_t>(sent);
|
|
}
|
|
|
|
size_t TCPClient::receive(void *buffer, size_t buffer_size) {
|
|
#ifdef _WIN32
|
|
auto received = ::recv(sockfd_, (char *) buffer, (int) buffer_size, 0);
|
|
#else
|
|
auto received = ::recv(sockfd_, buffer, buffer_size, 0);
|
|
#endif
|
|
|
|
if (received == 0) {
|
|
connected_.store(false);
|
|
return 0;
|
|
}
|
|
if (received == SOCKET_ERROR) {
|
|
connected_.store(false);
|
|
throw std::runtime_error("Receive failed");
|
|
}
|
|
return static_cast<size_t>(received);
|
|
}
|
|
|
|
void TCPServer::listen(uint16_t port, int backlog) {
|
|
create_socket();
|
|
|
|
sockaddr_in addr{};
|
|
addr.sin_family = AF_INET;
|
|
addr.sin_port = htons(port);
|
|
addr.sin_addr.s_addr = INADDR_ANY;
|
|
|
|
if (bind(sockfd_, (sockaddr *) &addr, sizeof(addr)) != 0) {
|
|
close_socket();
|
|
throw std::runtime_error("Bind failed");
|
|
}
|
|
|
|
if (::listen(sockfd_, backlog) != 0) {
|
|
close_socket();
|
|
throw std::runtime_error("Listen failed");
|
|
}
|
|
}
|
|
|
|
std::shared_ptr<TCPClient> TCPServer::accept() {
|
|
sockaddr_in client_addr{};
|
|
socklen_t addr_len = sizeof(client_addr);
|
|
|
|
auto client_sock = ::accept(sockfd_, (sockaddr *) &client_addr, &addr_len);
|
|
if (client_sock == INVALID_SOCKET_VALUE) {
|
|
throw std::runtime_error("Accept failed");
|
|
}
|
|
|
|
auto client = std::make_shared<TCPClient>();
|
|
client->sockfd_ = client_sock;
|
|
client->connected_.store(true);
|
|
return client;
|
|
}
|
|
|
|
void AsyncTCPClient::start_async() {
|
|
running_.store(true);
|
|
recv_thread_ = std::thread([this]() {
|
|
std::vector<uint8_t> buffer(4096);
|
|
while (running_.load()) {
|
|
try {
|
|
auto size = receive(buffer.data(), buffer.size());
|
|
if (size > 0) {
|
|
std::lock_guard<std::mutex> lock(queue_mutex_);
|
|
recv_queue_.emplace(buffer.begin(), buffer.begin() + static_cast<long long>(size));
|
|
}
|
|
}
|
|
catch (...) {
|
|
break;
|
|
}
|
|
}
|
|
});
|
|
}
|
|
|
|
bool AsyncTCPClient::try_pop(std::vector<uint8_t> &data) {
|
|
std::lock_guard<std::mutex> lock(queue_mutex_);
|
|
if (recv_queue_.empty()) return false;
|
|
data = std::move(recv_queue_.front());
|
|
recv_queue_.pop();
|
|
return true;
|
|
}
|
|
|
|
AsyncTCPClient::~AsyncTCPClient() {
|
|
running_.store(false);
|
|
if (recv_thread_.joinable()) {
|
|
recv_thread_.join();
|
|
}
|
|
close_socket();
|
|
}
|