пятница, 25 марта 2011 г.

С++ ООП. Проектирование сетевого клиента.


Поскольку мы уже немного освоились с перехватом сетевого трафика, с тем как формируются пакеты передаваемые по сети и программировании сетевых приложений, то пора уже приступать к важной части – проектированию приложения. И если с логикой работы программы у нас не возникает затруднений потому, что достаточно пары условий и действий по отправке-приему сообщений, то вот проблемы с раздутием кода из-за появления временных переменных и прочего непотребного хлама начинают уставать глаза и напрягаться мозг. Выход есть. Спроектировать приложение с учетом парадигмы ООП, которая нам поможет и код сделать понятным, да и ООП научиться применять на практике.
Но с чего нужно начать? Куда это ООП прикладывать или приписывать? Как мы уже видели, логика работы с сетью однозначна и не требует улучшений. А вот с самими пакетами дела обстоят намного хуже. Постоянно приходится что-то с ними делать, то разбирать на части, выделяя заголовок или тело, то сравнивать с чем-то. Вот тут-то и начинается простор для проектировщика.
Что на текущий момент нам уже известно:
1)      Приходящие и отправляемые пакеты – это набор символов (в С++ их удобно хранить в массивах).
2)      Обычно такие сообщения имеют заголовок 4 символа (байта).
3)      Тело таких сообщений может быть разной длины.



Не забудем, что еще у нас есть ID клиента для сетевой и его тоже нужно где-то постоянно хранить.
Теперь давайте приблизительно набросаем, что необходимо для простой и незамысловатой работы с сетевыми пакетами:
1)      Длина всего сообщения
2)      Заголовок и его длина.
3)      Тело сообщения и его длина.
4)      ID сетевого клиента и его длина.

Поскольку ООП предполагает принцип разделения данных и методов и сокрытия внутренних механизмов от пользователя (инкапсуляция), то будем исходить из того, что данные будут недоступны напрямую, а для того чтобы получить значение того или иного нужного нам свойства объекта будет использовать методы.
В результате можно предположить, что неплохо было бы обзавестись следующими методами для приходящих пакетов:
1) Выделение из полученного пакета заголовка и тела, заполнение полей отвечающих за длины заголовка и тела.
2) Установка, получение длины сетевого пакета.
3) Установка, получение заголовка пакета
4) Установка, получение длины заголовка пакета.
5) Установка, получение тела пакета
6) Установка, получение длины тела  пакета.
7) Установка, получение ID пакета
8) Установка, получение длины ID пакета.

Что касается формирования отправляемого пакета данных, но свойства и методы не сильно отличаются от перечисленных выше.

Для примера разберем содержимое заголовочного файла rcvpckt.h, в котором объявим все, что необходимо для работы с приходящими пакетами:

const unsigned int mx_buff = 2048+2;
const unsigned int hdrSz = 4+1;
const unsigned int bdySz = mx_buff-hdrSz;

class rcvPckt
{
    char id[hdrSz];
    int idLen;
    char header[hdrSz];
    char bdy[bdySz];
    int hdrLen;
    int bdyLen;


public:
    char rPckt[mx_buff];

    rcvPckt()
    {
        hdrLen = hdrSz-1;
    }

    void setRcvdPcktLen(int len)
    {
         bdyLen = len - hdrLen+1;
    }

    int getRcvdPcktLen()
    {
        return hdrLen + bdyLen;
    }

    char *getRcvdPckt()
    {
        return &rPckt[0];
    }

    void setHdr(char *value, unsigned int len);

    char *getHdr()
    {
        return &header[0];
    }

    int getHdrLen()
    {
        return hdrLen;
    }

    void setBdy(char *value, unsigned int len);

    char *getBdy()
    {
        return &bdy[0];
    }

    int getBdyLen()
    {
        return bdyLen;
    }

    void setId(char *value, unsigned int len);

    char *getId()
    {
        return &id[0];
    }

    int getIdLen()
    {
        return idLen;
    }

    void fillRcvdPckt(char *value, unsigned int len);

};

И заголовочное файла sndpackt.h:

const unsigned int mx_buff = 2048+2;
const unsigned int hdrSz = 4+1;
const unsigned int bdySz = mx_buff-hdrSz;

class sndPckt
{
    char sPckt[mx_buff];
    char header[hdrSz];
    char bdy[mx_buff - hdrSz];
    int hdrLen;
    int bdyLen;

public:

    sndPckt()
    {
        hdrLen = hdrSz-1;
    }

    void setHdr(char *value, unsigned int len);

    char *getHdr()
    {
        return &header[0];
    }

    int getHdrLen()
    {
        return hdrLen;
    }

    void setBdy(char *value, unsigned int len);

    void setBdyNoth();

    char *getBdy()
    {
        return &bdy[0];
    }

    int getBdyLen()
    {
        return bdyLen;
    }

    char *sendPckt();

   
    int sendPcktLen()
    {
        return hdrLen + bdyLen;
    }
};

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

2 комментария:

  1. А я вот в ходе дипломного проектирования сервер пишу... Ну точнее пытаюсь начать писать >_<

    ОтветитьУдалить