Пишу несложный сетевой протокол. Мне нужно, чтобы приходящие пакеты в зависимости от значения некоторого поля выводились либо в консоль, либо добавлялись в конец файла, который может создаваться с рандомным именем каждый раз, когда происходит соединение. Какой паттерн проектирования для этого использовать? Пытаюсь прикрутить шаблон стейтмашин, но получается неоптимально: когда нужно сбрасывать нагрузку пакета в конец файла, приходится этот файл открывать каждый раз, когда приходит пакет данного типа. Поток пакетов может быть неоднородным, т.е. друг за другом приходят пакеты, содержимое которых надо записывать в разные места.
Вот что я написал сейчас:
Код | #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); }
|
Сейчас работает запись в консоль или в файл в зависимости от пакетов, но проблем нет только тогда, когда приходят пакеты одного типа. Если приходят пакеты разного типа, но файл перезаписывается, например, когда происходит смена состояния. Это не то, что я хочу.
Плохо и то, что на каждый пакет открывается/закрывается файл. Еще и запись попакетная.
Нужно предусмотреть и обработку ошибок в пакетах. Как это сделать? |