boost ::asio отдельный массив для каждого клиента

#c

#c

Вопрос:

Я изучил C , и теперь я хотел бы двигаться дальше и немного изучить сетевое программирование. Я решил использовать boost:: asio, потому что он мультиплатформенный. Я написал эту простую программу:

клиент:

 #include <cstdlib>
#include <cstring>
#include <iostream>
#include <boost/asio.hpp>

using boost::asio::ip::tcp;

enum { max_length = 1000000 };

int main(int argc, char* argv[])
{
    while(1)
      {

  try
  {
    if (argc != 3)
    {
      std::cerr << "Usage: blocking_tcp_echo_client <host> <port>n";
      return 1;
    }

    boost::asio::io_service io_service;

    tcp::resolver resolver(io_service);
    tcp::resolver::query query(tcp::v4(), argv[1], argv[2]);
    tcp::resolver::iterator iterator = resolver.resolve(query);

    tcp::socket s(io_service);
    s.connect(*iterator);

    using namespace std; // For strlen.

    std::cout << "Enter message: ";
    char request[max_length];
    std::cin.getline(request, max_length);
    if (request == "n")
      continue;

    size_t request_length = strlen(request);
    boost::asio::write(s, boost::asio::buffer(request, request_length));

    char reply[max_length];
    boost::system::error_code error;
    size_t reply_length = s.read_some(boost::asio::buffer(reply), error);

   if (error == boost::asio::error::eof)
     break; // Connection closed cleanly by peer.
   else if (error)
     throw boost::system::system_error(error); // Some other error.




    std::cout << "Reply is: ";
    std::cout.write(reply, reply_length);

    std::cout << "n";
  }
  catch (std::exceptionamp; e)
  {
    std::cerr << "Exception: " << e.what() << "n";
    exit(1);
  }
  }

  return 0;
}
  

сервер:

 #include <cstdlib>
#include <iostream>
#include <boost/bind.hpp>
#include <boost/smart_ptr.hpp>
#include <boost/asio.hpp>
#include <boost/thread.hpp>
#include <boost/regex.hpp> 
#include <boost/lexical_cast.hpp>
#include <string>

using boost::asio::ip::tcp;
const int max_length = 1000000;

std::string user_array[100];

typedef boost::shared_ptr<tcp::socket> socket_ptr;

unsigned short analyze_user_request(std::stringamp; user_request, short unsigned* ID, std::string* request_value)
{
  // function returns:
  // 0: if user request is incorrect
  // 1: if user requests "PUT" operation
  // 2: if user requests "GET" operation
  // Furthermore, if request is correct, its value (i.e. ID number and/or string) is saved to short unsigned and string values passed by pointers. 

 boost::regex exp("^[[:space:]]*(PUT|GET)[[:space:]] ([[:digit:]]{1,2})(?:[[:space:]] (.*))?$");

  boost::smatch what; 
  if (regex_match(user_request, what, exp, boost::match_extra)) 
   { 
     short unsigned id_number = boost::lexical_cast<short unsigned>(what[2]);

     if (what[1] == "PUT")
       {
         boost::regex exp1("^[a-zA-Z0-9] $");
     std::string value = boost::lexical_cast<std::string>(what[3]);
     if (value.length() > 4095)
       return 0;
     if (!regex_match(value, exp1))
       return 0;
     else
       {
          *request_value = value;
          *ID = id_number;
         return 1;
           }
       }

     if (what[1] == "GET")
       {
     *ID = id_number;
         return 2;
       }

   }

  if (!regex_match(user_request, what, exp, boost::match_extra)) 
    return 0;
   }


void session(socket_ptr sock)
{
  try
  {
    for (;;)
    {
      char data[max_length];

      boost::system::error_code error;
      size_t length = sock->read_some(boost::asio::buffer(data), error);
      if (error == boost::asio::error::eof)
        break; // Connection closed cleanly by peer.
      else if (error)
        throw boost::system::system_error(error); // Some other error.
      // convert buffer data to string for further procession
      std::string line(boost::asio::buffers_begin(boost::asio::buffer(data)), boost::asio::buffers_begin(boost::asio::buffer(data))   length);
      std::string reply; // will be "QK", "INVALID", or "OK <value>"
           unsigned short vID;

      unsigned short* ID = amp;vID;
      std::string vrequest_value;
      std::string* request_value = amp;vrequest_value;

      unsigned short output = analyze_user_request(line, ID, request_value);

      if (output == 1)
    {
    // PUT    
      reply = "OK";
      user_array[*ID] = *request_value;
    }

        else if (output == 2)
      {
        // GET
        reply = user_array[*ID];
        if (reply == "")
          reply = "EMPTY";

      }

    else
      reply = "INVALID";

            boost::system::error_code ignored_error;
            size_t ans_len=reply.length();
        boost::asio::write(*sock, boost::asio::buffer(reply));
    }
  }
  catch (std::exceptionamp; e)
  {
    std::cerr << "Exception in thread: " << e.what() << "n";
  }
}

void server(boost::asio::io_serviceamp; io_service, short port)
{
  tcp::acceptor a(io_service, tcp::endpoint(tcp::v4(), port));
  for (;;)
  {
    socket_ptr sock(new tcp::socket(io_service));
    a.accept(*sock);
    boost::thread t(boost::bind(session, sock));
  }
}

int main(int argc, char* argv[])
{
  try
  {
    if (argc != 2)
    {
      std::cerr << "Usage: blocking_tcp_echo_server <port>n";
      return 1;
    }

    boost::asio::io_service io_service;

    using namespace std; // For atoi.
    server(io_service, atoi(argv[1]));
  }
  catch (std::exceptionamp; e)
  {
    std::cerr << "Exception: " << e.what() << "n";
  }

  return 0;
}
  

По сути, это приложение, которое позволяет пользователю хранить данные на сервере. Пользователь может вставлять новые данные с помощью команды PUT, за которой следует идентификационный номер и значение данных, и извлекать данные с помощью команды GET, за которой следует идентификатор. Пользовательские запросы обрабатываются в функции analyze_user_request и впоследствии записываются в массив или считываются из него. Проблема в том, что теперь все клиенты используют один и тот же глобальный arry. Это означает, что если один клиент сохраняет что-то под определенным идентификатором, все остальные клиенты могут это прочитать, потому что они обращаются к одному и тому же массиву. Интересно, как я могу связать массив с разными клиентами и создать новый массив при подключении нового клиента?

Комментарии:

1. Как насчет инкапсуляции клиента в класс?

Ответ №1:

Как насчет инкапсуляции данных сеанса в класс и создания отдельного объекта сеанса для каждого соединения. Примерно это может выглядеть так:

Определение класса сеанса:

 class Session {
public:
// logic from your session function 
void handleRequests(socket_ptr sock);

private:
// session data here
}

typedef boost::shared_ptr<Session> SessionPtr;
  

В функции «сервер» в цикле accept создайте новый объект и передайте его в новый поток:

 SessionPtr newSession(new Session());

boost::thread acceptThread(boost::bind(amp;Session::handleRequests, newSession, sock));
  

Извините за возможные ошибки в коде, я далек от своей среды разработки и не могу это протестировать.

Более элегантное решение для обработки нескольких подключений отдельно см. в примере boost ::asio «Сервер чата»:http://www.boost.org/doc/libs/1_47_0/doc/html/boost_asio/example/chat/chat_server.cpp

Комментарии:

1. спасибо, я последовал вашему совету, но теперь кажется, что новый экземпляр класса Session и, следовательно, новый массив создаются каждый раз, когда пользователь вводит какой-либо запрос. Мой текущий код находится здесь: ссылка