Fabio
Programador - Avançado
Programador - Avançado
Mensagens : 18260
Reputação : 388
Desde : 04/05/2012
Idade : 22
Link : gtamodvicio.blogspot.com
Ver perfil do usuáriohttp://gtamodvicio.blogspot.com
  • Reputação da mensagem: 100% (5 votos)
em Qui 17 Set 2015, 23:57
Estou refazendo minha antiga classe controladora de sockets com algumas coisas bem interessantes, então resolvi compartilhar aqui.

Estas classes permitem fazer servidores e clientes em rede via socket (atualmente só fiz compatível com Windows, mas está nos planos fazer compatível com linux), tratando os envios/recebimentos como streams de C++, isso facilita seu armazenamento, manipulação, etc.

Por causa do uso de streams é tão fácil enviar o conteúdo de um arquivo quanto enviar uma string qualquer, você pode até dizer quantos bytes podem ser recebidos no máximo.

Download do projeto de exemplo: https://copy.com/eRHvjNDOFICTBYAn/server-example.7z?download=1 (precisa da biblioteca cereal http://uscilab.github.io/cereal para compilar)
Esse projeto de exemplo é onde eu estou desenvolvendo e testando o sistema de sockets, tem inclusive o envio e recebimento de arquivo direto nas funções de socket através da std::fstream direto no método receive(), estejam a vontade para vasculhar o projeto que acompanha o executável (é apenas um executável mesmo, tanto o server quanto o cliente estão dentro dele, basta usar -server como paramêtro para abrir o modo servidor).

Todos esses arquivos estão sendo desenvolvidos ainda, por isso podem estar um pouco bangunçados ou parecendo gambiarra, estejam a vontade para modificar e usar em seus projetos.

Quando eu tiver bastante tempo disponível, vou elaborar uma explicação detalhada nesse tópico.

CSocket.h
Código:
#pragma once
#ifndef _CXX_CSOCKET_H_
#define _CXX_CSOCKET_H_

#include <thread>
#include <memory>
#include <cstdint>
#include <exception>
#include <string>
#include <atomic>
#include <mutex>
#include <chrono>
#include <map>
#include <sstream>
#include <functional>
#include <fstream>

class CSocketException : std::exception{
   const std::string exception_str;

public:
   const char *what() const{
      return exception_str.c_str();
   }

   CSocketException(const std::string &error)
      : std::exception(error.c_str()),
      exception_str(error)
   {

   }
};

class CSocket {
   std::unique_ptr<char[]> nativeResult;
   const size_t nativeResultSize;
   uintptr_t funcresult;

protected:
   std::string address, port;

   std::unique_ptr<char[]>                     hintsn;
   uintptr_t                              result;

public:
   class connection;

   typedef uintptr_t            SocketType;

   typedef std::function<int (connection &conn, char *recvbytes, size_t size, CSocket &sock)> callbackf_t;

   class connection {
      static std::atomic<int> defaultSignal;

   public:
      SocketType               socket;

      std::thread               fThread;
      std::atomic<bool>         threadRunning;

      callbackf_t               callb;
      
      std::atomic<size_t>         tranfBytesSize, totalBytesSize;

      typedef std::function<int(size_t, connection&)> iocallback_t;
      typedef std::function<int(std::fstream &file, std::streamsize tranfsize, std::streamsize size, connection&, std::atomic<int> &usrSignal)> fcallback_t;

      void resetSndRcvStats();
      int send(std::iostream &stream, iocallback_t func = nullptr);
      int sendbits(std::iostream &stream, iocallback_t func = nullptr, size_t partlen = 1024 * 64);
      int sendfile(std::fstream &fstrm, fcallback_t func = nullptr, size_t partlen = 1024 * 64, std::atomic<int> &usrSignal = defaultSignal);
      int sendfile(const std::string &file, fcallback_t func = nullptr, size_t partlen = 1024 * 64, std::atomic<int> &usrSignal = defaultSignal);
      int receive(std::iostream &stream, size_t input_max, iocallback_t func = nullptr);

      connection();
   };

   bool initialize();

   CSocket();
   ~CSocket();

   CSocket(CSocket&) = delete;
};


class CServerSocket : public CSocket
{
public:
   typedef std::function<int(connection &conn, char *recvbytes, size_t size, CServerSocket &sock)> svcallbackf_t;

   class connection : public CSocket::connection {
   public:
      svcallbackf_t               callb;

      connection() = default;
   };

private:
   SocketType                              listenSocket;

   std::atomic<bool>                        continueListeningSockets;
   static void                              socketListen(CServerSocket &sv);

   std::map<int, connection>                  clients;

   std::thread                              listerThread;

   static void                              clientMGR(CServerSocket &sv, connection &conn);
   std::mutex                              writeClient;

   svcallbackf_t                           clientFunction;

public:

   bool                                 initialize(const std::string &lport, svcallbackf_t func);

   CServerSocket();
   ~CServerSocket();
};


class CClientSocket : public CSocket {
   int                                    iResult;

public:
   connection                              conn;
   bool                                 initialize();

   CClientSocket(const std::string &svaddress, const std::string &svport);
   ~CClientSocket();
};


#endif


CSocket.cpp
Código:
#include "CSocket.h"
#include <winsock2.h>
#include <ws2tcpip.h>
#include <iostream>

static const char endTransmissionSignal[] = { "\nEnd¨This%Packet\n" };

std::atomic<int> CSocket::connection::defaultSignal = 0;

bool CSocket::initialize()
{
   nativeResult = std::unique_ptr<char[]>((char*)new WSADATA);

   WSADATA &nativeData = *(WSADATA*)nativeResult.get();

   funcresult = WSAStartup(MAKEWORD(2, 2), &nativeData);

   if (funcresult != 0)
   {
      throw CSocketException(std::string("WSAStartup failed: ") + std::to_string(funcresult));
   }

   return true;
}

CSocket::CSocket()
   : nativeResultSize(sizeof(WSADATA)),
   nativeResult(nullptr)
{
   funcresult = (uintptr_t)nullptr;
   
   hintsn = std::unique_ptr<char[]>((char*)new ADDRINFOA);
   ADDRINFOA &hints = *(ADDRINFOA*)hintsn.get();

   ZeroMemory(&hints, sizeof(hints));
   hints.ai_family = AF_INET;
   hints.ai_socktype = SOCK_STREAM;
   hints.ai_protocol = IPPROTO_TCP;
   hints.ai_flags = AI_PASSIVE;

}

CSocket::~CSocket()
{
   WSACleanup();
}

CSocket::connection::connection()
{
   threadRunning = true;
   tranfBytesSize = 0;
   totalBytesSize = 0;
}

void CSocket::connection::resetSndRcvStats()
{
   tranfBytesSize = 0;
   totalBytesSize = 0;
}

int CSocket::connection::sendfile(const std::string &file, fcallback_t func, size_t partlen, std::atomic<int> &usrSignal)
{
   std::fstream fstrm(file, std::ios::in | std::ios::binary);

   if (!fstrm.is_open())
   {
      return -1;
   }

   return sendfile(fstrm, func, partlen, usrSignal);
}

int CSocket::connection::sendfile(std::fstream &fstrm, fcallback_t func, size_t partlen, std::atomic<int> &usrSignal)
{
   resetSndRcvStats();

   if (!fstrm.is_open())
   {
      return -1;
   }

   if (partlen == 0 || partlen == (~0) )
   {
      return send(fstrm, [&](size_t tranfBytesSize, connection &conn) { return func? func(fstrm, tranfBytesSize, conn.totalBytesSize, conn, usrSignal) : 0; });
   }
   else
   {
      return sendbits(fstrm, [&](size_t tranfBytesSize, connection &conn) { return func ? func(fstrm, tranfBytesSize, conn.totalBytesSize, conn, usrSignal) : 0; }, partlen);
   }

   return 0;
}

int CSocket::connection::sendbits(std::iostream &stream, iocallback_t func, size_t partlen)
{
   tranfBytesSize = 0;
   totalBytesSize = 0;

   if (partlen == 0)
   {
      return -1;
   }

   int snd = 0;
   
   stream.seekg(0, std::ios::end);
   std::streamsize s = stream.tellg();
   stream.seekg(0, std::ios::beg);

   totalBytesSize = s;

   if(func) func(tranfBytesSize, *this);

   if (partlen >= s) {
      snd = send(stream, func);
   }
   else {

      std::streamsize parts = (s / partlen);
      std::streamsize partSize = partlen;
      std::streamsize pos = 0;

      std::unique_ptr<char[]> ch(new char[partSize]);

      for (std::streamsize i = 0; i < parts + 1; i++) {
         std::streamsize readsize = partSize, diff = s - pos;

         if (diff == 0) {
            //std::cout << 0 << std::endl;
            break;
         }

         if (diff < readsize)
         {
            readsize = diff;
         }

         stream.read(ch.get(), readsize);

         snd = ::send(socket, ch.get(), readsize, 0);

         pos += readsize;

         tranfBytesSize = pos;
         if (func) func(tranfBytesSize, *this);
         //std::cout << readsize << "      " << stream.good() << std::endl;
      }

      snd = ::send(socket, endTransmissionSignal, sizeof(endTransmissionSignal), 0);
   }

   return snd;
}

/* TODO: callback????*/
int CSocket::connection::send(std::iostream &stream, iocallback_t func)
{
   stream.seekg(0, std::ios::end);
   std::streamsize s = stream.tellg();
   stream.seekg(0, std::ios::beg);

   totalBytesSize = s;

   if (func) func(tranfBytesSize, *this);

   std::unique_ptr<char[]> ch(new char[s + 4]);

   stream.read(ch.get(), s);

   int snd = ::send(socket, ch.get(), s, 0);

   ::send(socket, endTransmissionSignal, sizeof(endTransmissionSignal), 0);

   return snd;
}

int CSocket::connection::receive(std::iostream &stream, size_t input_max, iocallback_t func)
{
   size_t bytesall = 0;
   char buffer[4096] = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
   tranfBytesSize = 0;

   while (bytesall < input_max)
   {
      size_t bytesReceived = recv(socket, buffer, sizeof(buffer) / sizeof(char), MSG_PARTIAL);

      if (bytesReceived == sizeof(endTransmissionSignal))
      {
         if (strcmp(endTransmissionSignal, buffer) == 0)
         {
            break;
         }
      }

      int err = WSAGetLastError();

      if (err == 10035 || bytesReceived == -1)
      {
         break;
      }

      bool subtract = false;
      size_t b = bytesReceived - sizeof(endTransmissionSignal);

      if (err == 0 && bytesReceived > sizeof(endTransmissionSignal) && strcmp(endTransmissionSignal, &buffer[b]) == 0)
      {
         subtract = true;
         bytesReceived = b;
      }

      stream.write(buffer, bytesReceived);
      bytesall += bytesReceived;

      tranfBytesSize = bytesall;

      if (func)
      {
         func(bytesall, *this);
      }

      if (subtract)
      {
         break;
      }
   }

   return bytesall;
}

bool CServerSocket::initialize(const std::string &lport, svcallbackf_t func)
{
   port = lport;
   CSocket::initialize();

   clientFunction = func;

   PADDRINFOA result = *(PADDRINFOA*)&this->result;;

   ADDRINFOA &hints = *(ADDRINFOA*)hintsn.get();

   int serverResult = getaddrinfo(NULL, port.c_str(), &hints, (PADDRINFOA*)&result);
   if (serverResult != 0) {
      WSACleanup();
      throw CSocketException(std::string("getaddrinfo failed: ") + std::to_string(serverResult));

      return false;
   }

   listenSocket = socket(result->ai_family, result->ai_socktype, result->ai_protocol);
   if (listenSocket == INVALID_SOCKET) {
      throw CSocketException(std::string("socket failed: ") + std::to_string(WSAGetLastError()));
      return false;
   }

   serverResult = bind(listenSocket, result->ai_addr, (int)result->ai_addrlen);
   if (serverResult == SOCKET_ERROR) {
      throw CSocketException(std::string("bind failed: ") + std::to_string(WSAGetLastError()));
      return false;
   }

   if (listen(listenSocket, SOMAXCONN) == SOCKET_ERROR) {
      throw CSocketException(std::string("listen failed: ") + std::to_string(WSAGetLastError()));
      return false;
   }

   listerThread = std::thread(socketListen, std::ref(*this));

   return true;
}

void CServerSocket::clientMGR(CServerSocket &sv, connection &conn)
{
   svcallbackf_t call = conn.callb;

   try {
      call(std::ref(conn), nullptr, (size_t)0, std::ref(sv));
   }
   catch (const std::exception &e)
   {
      throw CSocketException(std::string("In client thread: ") + e.what());
   }

}

void CServerSocket::socketListen(CServerSocket &sv)
{
   while (sv.continueListeningSockets)
   {
      SOCKET clientSocket = accept(sv.listenSocket, NULL, NULL);

      if (clientSocket == INVALID_SOCKET)
      {
         if (!sv.continueListeningSockets)
         {
            break;
         }

         std::cout << "INVALID_SOCKET" << std::endl;
         break;
      }

      sv.writeClient.lock();
      auto &cli = sv.clients[sv.clients.size()];

      cli.socket = clientSocket;
      cli.callb = sv.clientFunction;
      cli.threadRunning = true;
      cli.fThread = std::thread(clientMGR, std::ref(sv), std::ref(cli));

      sv.writeClient.unlock();
   }
}

CServerSocket::CServerSocket() : CSocket()
{
   continueListeningSockets = true;
   clientFunction = nullptr;
   listenSocket = NULL;

}

CServerSocket::~CServerSocket()
{
   continueListeningSockets = false;
   closesocket(listenSocket);

   for (auto &ths : clients)
   {
      auto &t = ths.second;

      t.threadRunning = false;

      if (t.fThread.joinable())
      {
         t.fThread.join();
      }
   }

   if (listerThread.joinable())
   {
      listerThread.join();
   }

   CSocket::~CSocket();
}

bool CClientSocket::initialize()
{
   CSocket::initialize();

   ADDRINFOA &hints = *(ADDRINFOA*)hintsn.get();
   iResult = getaddrinfo(address.c_str(), port.c_str(), &hints, (PADDRINFOA*)&result);
   addrinfo *ptr = nullptr;

   for (ptr = (addrinfo*)result; ptr != NULL; ptr = ptr->ai_next) {
      conn.socket = socket(ptr->ai_family, ptr->ai_socktype,
         ptr->ai_protocol);
      if (conn.socket == INVALID_SOCKET) {
         printf("socket failed with error: %ld\n", WSAGetLastError());
         WSACleanup();
         return false;
      }

      // Connect to server.
      iResult = connect(conn.socket, ptr->ai_addr, (int)ptr->ai_addrlen);
      if (iResult == SOCKET_ERROR) {
         closesocket(conn.socket);
         conn.socket = INVALID_SOCKET;
         continue;
      }
      // std::cout << "Connected\n";
      break;
   }

   result = (uintptr_t)ptr;

   return true;
}


CClientSocket::CClientSocket(const std::string &svaddress, const std::string &svport) : CSocket()
{
   address = svaddress;
   port = svport;

   conn.socket = INVALID_SOCKET;

   iResult = SOCKET_ERROR;
}

CClientSocket::~CClientSocket()
{
   if (conn.socket != INVALID_SOCKET) {
      closesocket(conn.socket);
      //shutdown(conn.socket);
   }

   CSocket::~CSocket();
}




Última edição por Fabio em Sab 19 Set 2015, 23:45, editado 1 vez(es)

______________________________
[WIP][C++11][Windows] Wrapper de sockets com suporte a std::[i/o]stream(s) + projeto demonstração 226c0ef57f9d7520d171cbadc68b3c56
Modifico quase qualquer coisa.   :)
------------------>>>http://gtamodvicio.blogspot.com/<<<------------------
[WIP][C++11][Windows] Wrapper de sockets com suporte a std::[i/o]stream(s) + projeto demonstração 76561198069372249
Meu PC:
Spoiler:

Configurações PC:
*Processador: Intel Core i7 4790K 4.00GHz LGA1150
*Placa Mãe: GIGABYTE GA-H97M-D3H Intel (LGA1150)
*HD: 1TB Seagate Barracuda 64MB Sata III 7200RPM (2x - RAID 0)
*HD²: SAMSUNG M3 Externo USB 5400RPM 1TB
*Placa de vídeo: Nvidia GTX660 2GB DDR5 192bit EVGA
*RAM: 8GB DDR3 1600MHZ Kingston HYPER X BEAST (2x4GB)
*Fonte: Corsair 600W Reais CX600M Modular - CP-9020060-WW (80 Plus Bronze)
*Gabinete: Gabinete Raidmax Super Hurricane Branco - 248WB
*Monitor: LG 23MP55HQ Full HD HDMI 23'
Fabio
Programador - Avançado
Programador - Avançado
Mensagens : 18260
Reputação : 388
Desde : 04/05/2012
Idade : 22
Link : gtamodvicio.blogspot.com
Ver perfil do usuáriohttp://gtamodvicio.blogspot.com
em Sab 19 Set 2015, 23:43
[WIP][C++11][Windows] Wrapper de sockets com suporte a std::[i/o]stream(s) + projeto demonstração Wivsrq

:)

______________________________
[WIP][C++11][Windows] Wrapper de sockets com suporte a std::[i/o]stream(s) + projeto demonstração 226c0ef57f9d7520d171cbadc68b3c56
Modifico quase qualquer coisa.   :)
------------------>>>http://gtamodvicio.blogspot.com/<<<------------------
[WIP][C++11][Windows] Wrapper de sockets com suporte a std::[i/o]stream(s) + projeto demonstração 76561198069372249
Meu PC:
Spoiler:

Configurações PC:
*Processador: Intel Core i7 4790K 4.00GHz LGA1150
*Placa Mãe: GIGABYTE GA-H97M-D3H Intel (LGA1150)
*HD: 1TB Seagate Barracuda 64MB Sata III 7200RPM (2x - RAID 0)
*HD²: SAMSUNG M3 Externo USB 5400RPM 1TB
*Placa de vídeo: Nvidia GTX660 2GB DDR5 192bit EVGA
*RAM: 8GB DDR3 1600MHZ Kingston HYPER X BEAST (2x4GB)
*Fonte: Corsair 600W Reais CX600M Modular - CP-9020060-WW (80 Plus Bronze)
*Gabinete: Gabinete Raidmax Super Hurricane Branco - 248WB
*Monitor: LG 23MP55HQ Full HD HDMI 23'
Elyzandro
Veterano
Veterano
Mensagens : 2904
Reputação : 81
Desde : 31/07/2013
Ver perfil do usuário
em Sab 19 Set 2015, 23:55
Isso deve ser tão legal mas eu não entendo nada.

______________________________
I̜mpe͢r͚at̲ivoͯ, ͘no̅ mụnd̶o͋ ̺sl͟o͗w
́Co͔m͢ó ̼a ̒p̎eçͦa̲ ̖MC͝s ̚só͊ ͡faz̀em̈́ ̛s̸p͆ee͌df̡l̡owͭ
̟Pͭra ̌que̲ ä́ p̐r͜es̋s͉aͤ?
Fabio
Programador - Avançado
Programador - Avançado
Mensagens : 18260
Reputação : 388
Desde : 04/05/2012
Idade : 22
Link : gtamodvicio.blogspot.com
Ver perfil do usuáriohttp://gtamodvicio.blogspot.com
em Dom 20 Set 2015, 00:47
@Elyzandro escreveu:Isso deve ser tão legal mas eu não entendo nada.

Deve ter umas 5 pessoas no fórum todo que entendem isso ;-;

______________________________
[WIP][C++11][Windows] Wrapper de sockets com suporte a std::[i/o]stream(s) + projeto demonstração 226c0ef57f9d7520d171cbadc68b3c56
Modifico quase qualquer coisa.   :)
------------------>>>http://gtamodvicio.blogspot.com/<<<------------------
[WIP][C++11][Windows] Wrapper de sockets com suporte a std::[i/o]stream(s) + projeto demonstração 76561198069372249
Meu PC:
Spoiler:

Configurações PC:
*Processador: Intel Core i7 4790K 4.00GHz LGA1150
*Placa Mãe: GIGABYTE GA-H97M-D3H Intel (LGA1150)
*HD: 1TB Seagate Barracuda 64MB Sata III 7200RPM (2x - RAID 0)
*HD²: SAMSUNG M3 Externo USB 5400RPM 1TB
*Placa de vídeo: Nvidia GTX660 2GB DDR5 192bit EVGA
*RAM: 8GB DDR3 1600MHZ Kingston HYPER X BEAST (2x4GB)
*Fonte: Corsair 600W Reais CX600M Modular - CP-9020060-WW (80 Plus Bronze)
*Gabinete: Gabinete Raidmax Super Hurricane Branco - 248WB
*Monitor: LG 23MP55HQ Full HD HDMI 23'
Junior_Djjr
Scripter CLEO - Avançado
Scripter CLEO - Avançado
Título : Censurando since 2011
Mensagens : 22701
Reputação : 727
Desde : 03/05/2012
Idade : 23
Localização : Ibitinga - SP
Link : www.MixMods.com.br
Ver perfil do usuáriohttp://MixMods.com.br
em Dom 20 Set 2015, 00:52
pense positivo: o Google tbm está te olhando :v:

procurei por "Wrapper de sockets" no Google e o segundo resultado foi este post, mas os resultados mudam para cada pessoa, então sei lah :v:

______________________________
BMS agora em nova plataforma:
Forum.MixMods.com.br
SideWinder
Veterano
Veterano
Título : Ruby does not exist
Mensagens : 1364
Reputação : 37
Desde : 06/09/2014
Idade : 20
Localização : sitio do djjr
Link : youtube.com/watch?v=R7AE7MfLt2s
Ver perfil do usuário
em Dom 20 Set 2015, 01:08
Verdade, é o segundo resultado .-.

______________________________
[WIP][C++11][Windows] Wrapper de sockets com suporte a std::[i/o]stream(s) + projeto demonstração 2ywzqdu[WIP][C++11][Windows] Wrapper de sockets com suporte a std::[i/o]stream(s) + projeto demonstração 3816610
[WIP][C++11][Windows] Wrapper de sockets com suporte a std::[i/o]stream(s) + projeto demonstração 388710[WIP][C++11][Windows] Wrapper de sockets com suporte a std::[i/o]stream(s) + projeto demonstração 4495810
[WIP][C++11][Windows] Wrapper de sockets com suporte a std::[i/o]stream(s) + projeto demonstração 1297210[WIP][C++11][Windows] Wrapper de sockets com suporte a std::[i/o]stream(s) + projeto demonstração 4584610
"O orgulhoso prefere perder-se a perguntar qual é o seu caminho."
Winston Churchill
avatar
Gostoso
Gostoso
Mensagens : 3939
Reputação : 59
Desde : 03/06/2012
Idade : 17
Ver perfil do usuário
em Dom 20 Set 2015, 20:06
Já n eh mais :v
SideWinder
Veterano
Veterano
Título : Ruby does not exist
Mensagens : 1364
Reputação : 37
Desde : 06/09/2014
Idade : 20
Localização : sitio do djjr
Link : youtube.com/watch?v=R7AE7MfLt2s
Ver perfil do usuário
em Dom 20 Set 2015, 20:08
É sim !!

______________________________
[WIP][C++11][Windows] Wrapper de sockets com suporte a std::[i/o]stream(s) + projeto demonstração 2ywzqdu[WIP][C++11][Windows] Wrapper de sockets com suporte a std::[i/o]stream(s) + projeto demonstração 3816610
[WIP][C++11][Windows] Wrapper de sockets com suporte a std::[i/o]stream(s) + projeto demonstração 388710[WIP][C++11][Windows] Wrapper de sockets com suporte a std::[i/o]stream(s) + projeto demonstração 4495810
[WIP][C++11][Windows] Wrapper de sockets com suporte a std::[i/o]stream(s) + projeto demonstração 1297210[WIP][C++11][Windows] Wrapper de sockets com suporte a std::[i/o]stream(s) + projeto demonstração 4584610
"O orgulhoso prefere perder-se a perguntar qual é o seu caminho."
Winston Churchill
Conteúdo patrocinado
Permissão deste fórum:
Você não pode responder aos tópicos neste fórum