Разработка своего сетевого протокола

 
0
 
C++
ava
Aoizora | 25.12.2016, 16:00
Пишу несложный сетевой протокол. Мне нужно, чтобы приходящие пакеты в зависимости от значения некоторого поля выводились либо в консоль, либо добавлялись в конец файла, который может создаваться с рандомным именем каждый раз, когда происходит соединение. Какой паттерн проектирования для этого использовать? Пытаюсь прикрутить шаблон стейтмашин, но получается неоптимально: когда нужно сбрасывать нагрузку пакета в конец файла, приходится этот файл открывать каждый раз, когда приходит пакет данного типа. Поток пакетов может быть неоднородным, т.е. друг за другом приходят пакеты, содержимое которых надо записывать в разные места.

Вот что я написал сейчас:


#ifndef NETWORK_H
#define NETWORK_H

#include <Windows.h>
#include <stdio.h>
#include "packet.h"

class State
{
public:
    virtual int recv(SOCKET sock) = 0;
    virtual int send(SOCKET sock, char *data, size_t len) = 0;
};

class ConsoleState : public State
{
public:
    int recv(SOCKET sock);
    int send(SOCKET sock, char *data, size_t len);
};

class FileState : public State
{
    HANDLE hFile;
public:
    FileState();
    ~FileState();
    int recv(SOCKET sock);
    int send(SOCKET sock, char *data, size_t len);
};

class Network
{
    SOCKET sock;
    State *state;
    static int last_state;
public:
    Network();
    Network(SOCKET s) : sock(s), state(nullptr) {}

    void set_state(State *s)
    {
        delete state;
        state = s;
    }
    int send(char *data, size_t len);
    int recv();
};

#endif



#include "network.h"

int ConsoleState::recv(SOCKET sock)
{
    char buffer[200] = { 0 };

    int ret = ::recv(sock, buffer, 200, 0);
    printf("recieved : %s\n", buffer);

    return ret;
}

int ConsoleState::send(SOCKET sock, char *data, size_t len)
{
    packet header = { 0 };

    header.command = CMD_WRITE_CONSOLE;
    header.type_of_data = TYPE_ASCII_TEXT;
    header.size_of_data = len;

    ::send(sock, reinterpret_cast<char *>(&header), sizeof(header), 0);
    int ret = ::send(sock, data, len, 0);
    return ret;
}

FileState::FileState()
{
    hFile = CreateFile(TEXT("log.txt"),
        GENERIC_WRITE,
        FILE_SHARE_READ,
        NULL,
        CREATE_ALWAYS,
        FILE_ATTRIBUTE_NORMAL,
        NULL);
}

FileState::~FileState()
{
    CloseHandle(hFile);
}

int FileState::recv(SOCKET sock)
{
    char buffer[200];
    DWORD bytes_read = 0;
    DWORD bytes_written = 0;

    bytes_read = ::recv(sock, buffer, 200, 0);
    printf("recieved %d bytes\n", bytes_read);
    WriteFile(hFile, buffer, bytes_read, &bytes_written, NULL);

    return bytes_read;
}

int Network::last_state = 0;

int FileState::send(SOCKET sock, char *data, size_t len)
{
    return 0;
}

int Network::send(char *data, size_t len)
{
    int ret = state->send(sock, data, len);
    return ret;
}

int Network::recv()
{
    packet header;

    ::recv(sock, reinterpret_cast<char *>(&header), sizeof(header), 0);

    if (last_state != header.command)
    {
        last_state = header.command;
        switch(header.command)
        {
        case CMD_WRITE_CONSOLE:
            set_state(new ConsoleState);
            break;
        case CMD_WRITE_FILE:
            set_state(new FileState);
            break;
        }
    }

    return state->recv(sock);
}


Сейчас работает запись в консоль или в файл в зависимости от пакетов, но проблем нет только тогда, когда приходят пакеты одного типа. Если приходят пакеты разного типа, но файл перезаписывается, например, когда происходит смена состояния. Это не то, что я хочу.

Плохо и то, что на каждый пакет открывается/закрывается файл. Еще и запись попакетная.

Нужно предусмотреть и обработку ошибок в пакетах. Как это сделать?
Kommentare (2)
ava
likehood | 25.12.2016, 19:49 #
Насколько я понял, главная проблема в том, что в строках 80 и 83 при смене состояния каждый раз создаются новые объекты, а значит заново открываются файлы. В принципе, паттерн StateMachine не требует обязательно создавать новые объекты. Можно в начале создать два объекта ConsoleState и FileState, и в классе Network хранить указатель на один из этих объектов. При смене состояния указатель переключается на другой объект, а сами объекты не уничтожаются/создаются. Если конечно я правильно понял вопрос :smile.

P.S. Как я понял функция send в классах ConsoleState и FileState делает одно и то же. Может, её лучше реализовать в базовом классе State?
ava
Aoizora | 25.12.2016, 21:04 #
Спасибо. Видимо, так и будет лучше, если отойти от стандартной реализации стейтмашины.
Registrieren Sie sich oder melden Sie sich an, um schreiben zu können.
Unternehmen des Tages
Вы также можете добавить свою фирму в каталог IT-фирм, и публиковать статьи, новости, вакансии и другую информацию от имени фирмы.
Подробнее
Mitwirkende
advanced
Absenden