Skip to content

Instantly share code, notes, and snippets.

@anthonyraf
Forked from stefan-wolfsheimer/Socket-C++.md
Created May 15, 2022 14:48
Show Gist options
  • Save anthonyraf/fffc44af5e545650e921fc8b36e771bb to your computer and use it in GitHub Desktop.
Save anthonyraf/fffc44af5e545650e921fc8b36e771bb to your computer and use it in GitHub Desktop.
Socket programming pattern C++

Makefile

CPP=g++ -std=c++14

all:server client
	echo "ok"

server: server.cpp config.h socket.o 
	${CPP} server.cpp socket.o -o server

client: client.cpp config.h socket.o 
	${CPP} client.cpp socket.o -o client

socket.o: socket.h socket.cpp
	${CPP} -c socket.cpp -o socket.o

General framework

socket.h

#pragma once
#include <cstdint>
#include <string>
#include <functional>

class Socket
{
public:
  Socket(const std::string & _ip = "0.0.0.0",
         uint16_t _port=8080,
         bool verbose=false);

  ~Socket();
  void serve(std::function<bool(Socket*)> handler);
  bool isVerbose() const;

  void connect();

  void read(char * buff, size_t len);
  void write(const char * buff, size_t len);

  uint64_t read_uint64();
  uint8_t read_uint8();

  void write_uint64(const uint64_t & value);
  void write_uint8(const uint8_t & value);

private:
  int sockfd;
  int connfd;
  uint16_t port;
  struct sockaddr_in * servaddr;
  std::string ip;
  bool verbose;
};

socket.cpp

#include "socket.h"
#include <stdexcept>
#include <iostream>
#include <unistd.h>
#include <stdlib.h>
#include <stdio.h>
#include <netdb.h>
#include <string.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>

Socket::Socket(const std::string & _ip, uint16_t _port, bool _verbose)
  : ip(_ip), port(_port), verbose(_verbose)
{
  socklen_t len;
  servaddr = nullptr;

  // socket create and verification 
  sockfd = socket(AF_INET, SOCK_STREAM, 0); 
  if (sockfd == -1)
  {
    throw std::runtime_error("socket creation failed");
  } 
  else
  {
    if(verbose)
    {
      std::cout << "Socket successfully created" << std::endl;
    }
  }
  servaddr = new sockaddr_in;
  bzero(servaddr, sizeof(struct sockaddr_in)); 
  
  // assign IP, PORT 
  servaddr->sin_family = AF_INET;
  if(ip == "0.0.0.0")
  {
    servaddr->sin_addr.s_addr = htonl(INADDR_ANY);
  }
  else
  {
    servaddr->sin_addr.s_addr = inet_addr(ip.c_str());
  }
  servaddr->sin_port = htons(port); 
}


Socket::~Socket()
{
  // After chatting close the socket 
  close(sockfd);
  if(servaddr)
  {
    delete servaddr;
  }
}

bool Socket::isVerbose() const
{
  return verbose;
}

void Socket::serve(std::function<bool(Socket*)> handler)
{
  // Binding newly created socket to given IP and verification 
  if ((bind(sockfd, (struct sockaddr*)servaddr, sizeof(sockaddr_in))) != 0)
  {
    throw std::runtime_error("socket bind failed");
  } 
  else
  {
    if(verbose)
    {
      std::cout << "socket successfully binded" << std::endl;
    }
  }

  // Now server is ready to listen and verification 
  if ((listen(sockfd, 5)) != 0)
  {
    throw std::runtime_error("Listen failed");
  } 
  else
  {
    if(verbose)
    {
      std::cout << "Server listening" << std::endl;
    }
  }

  struct sockaddr_in cli_addr;
  socklen_t clilen = sizeof(cli_addr);
  bool running = true;
  while(running)
  {
    connfd = accept(sockfd, 
                    (struct sockaddr *) &cli_addr, 
                    &clilen);
    if (connfd < 0)
    {
      throw std::runtime_error("ERROR on accept");
    }
    running = handler(this);
    close(connfd);
  }
}

void Socket::connect()
{
  if(::connect(sockfd,(struct sockaddr *)servaddr, sizeof(sockaddr_in)) < 0)
  {
    throw std::runtime_error("ERROR connecting");
  }
  connfd = sockfd;
}

void Socket::read(char * buff, size_t len)
{
  ::read(connfd, buff, len);
}

void Socket::write(const char * buff, size_t len)
{
  ::write(connfd, buff, len);
}

char * Socket::read()
{
  std::size_t len = this->read_uint64();
  char * ch = new char[len];
  this->read(ch, len);
  return ch;
}

void Socket::write(const char * str)
{
  uint64_t len = strlen(str) + 1;
  this->write_uint64(len);
  this->write(str, len);
}

uint64_t Socket::read_uint64()
{
  char buff[8];
  this->read(buff, 8);
  uint64_t res = 0;
  for(int i = 0; i < 8; i++)
  {
    res += uint64_t(buff[i]) << 8*(7-i);
  }
  return res;
}

uint8_t Socket::read_uint8()
{
  char buff;
  this->read(&buff, 1);
  return (uint8_t)buff;
}

void Socket::write_uint64(const uint64_t & value)
{
  char buff[8];
  for(int i = 0; i < 8; i++)
  {
    buff[i] = uint8_t((value >> 8*(7 - i)) & 0xFF);
  }
  this->write(buff, 8);
}

void Socket::write_uint8(const uint8_t & value)
{
  this->write((const char*)&value, 1);
}

Application

config.h

#pragma once
#define BUFFER_SIZE 80

server.cpp

#include "socket.h"
#include "config.h"
#include <string.h>
#include <iostream>

bool handler(Socket * sock)
{
  char buff[BUFFER_SIZE];
  bool connected = true;
  bool keep_listening = true;
  while(connected)
  {
    bzero(buff, BUFFER_SIZE);
    sock->read(buff, sizeof(buff));
    buff[BUFFER_SIZE-1] = 0;
    std::cout << "from client " << buff << std::endl;
    if (strcmp("exit", buff) == 0)
    {
      if(sock->isVerbose())
      {
        std::cout << "exit" << std::endl;
      }
      connected = false;
      keep_listening = false;
      sock->write("disconnect", strlen("disconnect") + 1);
    }
    else if (strcmp("disconnect", buff) == 0)
    {
      connected = false;
      keep_listening = true;
      sock->write("disconnect", strlen("disconnect") + 1);
      if(sock->isVerbose())
      {
        std::cout << "disconnect" << std::endl;
      }

    }
    else
    {
      printf(">");
      bzero(buff, BUFFER_SIZE);
      for(int i =0; i < BUFFER_SIZE-1; i++)
      {
        char ch = getchar();
        if(ch != '\n')
        {
          buff[i] = ch;
        }
        else
        {
          break;
        }
      }
      sock->write(buff, sizeof(buff));
    }
  }
  return keep_listening;
}


int main(int argc, const char ** argv) 
{
  int port = 8080;
  std::string ipaddr = "0.0.0.0";
  bool printHelp = false;
  bool argError = false;
  bool verbose = false;
  for(int i = 0; i < argc; ++i)
  {
    std::string arg(argv[i]);
    if(arg == "--port" || arg == "-p")
    {
      ++i;
      if(i < argc)
      {
        try
        {
          port = stoi(std::string(argv[i]));
        }
        catch(std::exception ex)
        {
          std::cerr << "invalid port number " << argv[i] << std::endl;
          argError = true;
        }
      }
      else
      {
        std::cerr << "missing argument " << argv[i] << " PORT" << std::endl;
        argError = true;
      }
    }
    if(arg == "--addr" || arg == "-a")
    {
      ++i;
      if(i < argc)
      {
        ipaddr = argv[i];
      }
      else
      {
        std::cerr << "missing argument " << argv[i] << " IPv4" << std::endl;
        argError = true;
      }
    }
    if(arg == "--help" || arg == "-h")
    {
      printHelp = true;
    }
    if(arg == "--verbose" || arg == "-v")
    {
      verbose = true;
    }
  }
  if(printHelp || argError)
  {
    std::cout << argv[0] << "[OPTIONS]" << std::endl;
    std::cout << "OPTIONS:" << std::endl;
    std::cout << "--port|-p PORT" << std::endl;
    std::cout << "--addr|-a IPv4" << std::endl;
    std::cout << "--verbose|-v" << std::endl;
    std::cout << "--help|-h" << std::endl;
    if(argError)
    {
      return 1;
    }
    else
    {
      return 0;
    }
  }
  try
  {
    Socket sock(ipaddr, port, verbose);
    sock.serve(handler);
  }
  catch(const std::exception & ex)
  {
    std::cerr << "server failed:" << ex.what() << std::endl;
    return 8;
  }
  return 0;
} 

client.cpp

#include "socket.h"
#include "config.h"
#include <iostream>
#include <string>
#include <string.h>
#include <stdlib.h>

void conversation(const std::string ipaddr,
                  int port,
                  bool verbose)
{

  Socket sock(ipaddr, port, verbose);
  sock.connect();
  char buff[BUFFER_SIZE];
  int n;
  bool connected = true;
  while(connected)
  {
    bzero(buff, sizeof(buff));
    std::cout << ">";
    for(int i =0; i < BUFFER_SIZE-1; i++)
    {
      char ch = getchar();
      if(ch != '\n')
      {
        buff[i] = ch;
      }
      else
      {
        break;
      }
    }
    sock.write(buff, sizeof(buff));
    bzero(buff, sizeof(buff));
    sock.read(buff, sizeof(buff));
    buff[BUFFER_SIZE-1] = 0;
    std::cout << "from server:" << buff << std::endl;
    if(strcmp("disconnect", buff) == 0)
    {
      connected = false;
    }
  }
}

int main(int argc, const char ** argv)
{
  int port = 8080;
  std::string ipaddr = "127.0.0.1";
  bool printHelp = false;
  bool argError = false;
  bool verbose = false;
  for(int i = 0; i < argc; ++i)
  {
    std::string arg(argv[i]);
    if(arg == "--port" || arg == "-p")
    {
      ++i;
      if(i < argc)
      {
        try
        {
          port = stoi(std::string(argv[i]));
        }
        catch(std::exception ex)
        {
          std::cerr << "invalid port number " << argv[i] << std::endl;
          argError = true;
        }
      }
      else
      {
        std::cerr << "missing argument " << argv[i] << " PORT" << std::endl;
        argError = true;
      }
    }
    if(arg == "--addr" || arg == "-a")
    {
      ++i;
      if(i < argc)
      {
        ipaddr = argv[i];
      }
      else
      {
        std::cerr << "missing argument " << argv[i] << " IPv4" << std::endl;
        argError = true;
      }
    }
    if(arg == "--help" || arg == "-h")
    {
      printHelp = true;
    }
    if(arg == "--verbose" || arg == "-v")
    {
      verbose = true;
    }
  }
  if(printHelp || argError)
  {
    std::cout << argv[0] << "[OPTIONS]" << std::endl;
    std::cout << "OPTIONS:" << std::endl;
    std::cout << "--port|-p PORT" << std::endl;
    std::cout << "--addr|-a IPv4" << std::endl;
    std::cout << "--verbose|-v" << std::endl;
    std::cout << "--help|-h" << std::endl;
    if(argError)
    {
      return 1;
    }
    else
    {
      return 0;
    }
  }

  try
  {
    conversation(ipaddr, port, verbose);
  }
  catch(const std::exception & ex)
  {
    std::cerr << "conversation failed:" << ex.what() << std::endl;
    return 8;
  }
  return 0;
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment