Эффективное программирование TCP-IP

       

Разрабатывайте и применяйте каркасы приложений


| | |

Большинство приложений TCP/IP попадают в одну из четырех категорий:

  • TCP-сервер;
  • TCP-клиент;
  • UDP-сервер;
  • UDP-клиент.
  • В приложениях одной категории обычно встречается почти одинаковый «стартовый» код, который инициализирует все, что связано с сетью. Например,TCP- сервер должен поместить в поля структуры sockaddr_in адрес и порт получателя, получить от системы сокет типа SOCK_STREAM, привязать к нему выбранный адрес и номер порта, установить опцию сокета SO_REUSEADDR (совет 23), вызвать listen, а затем быть готовым к приему соединения (или нескольких соединений) с помощью системного вызова accept.

    На каждом из этих этапов следует проверять код возврата. А часть программы, занимающаяся преобразованием адресов, должна иметь дело как с числом так и с символическими адресами и номерами портов. Таким образом, в любом TCP-сервере есть порядка 100 почти одинаковых строк кода для выявления всех перечисленных выше задач. Один из способов решения этой проблемы - поместить стартовый код в одну или несколько библиотечных функций которые приложение может вызвать. Эта стратегия использована в книге. Но иногда приложению нужна слегка видоизмененная последовательность инициализации. В таком случае придется либо написать ее с нуля, либо извлечь нужный фрагмент кода из библиотеки и подправить его.

    Чтобы справиться и с такими ситуациями, можно построить каркас приложения, в котором уже есть весь необходимый код. Затем скопировать этот каркас, внести необходимые изменения, после чего заняться логикой самого приложения. Не имея каркаса, легко поддаться искушению и срезать некоторые углы, например, жестко «зашить» в приложение адреса (совет 29) или сделать еще что-то сомнительное. Разработав каркас, вы сможете убрать все типичные функции в библиотеку, а каркас оставить только для необычных задач.

    Чтобы сделать программы переносимыми, следует определить несколько макросов, в которых скрыть различия между API систем UNIX и Windows. Например, в UNIX системный вызов для закрытия сокета называется close, а в Windows - closesocket. Версии этих макросов для UNIX показаны в листинге 2.1. Версии для Windows аналогичны, приведены в приложении 2. Доступ к этим макросам из каркасов осуществляется путем включения файла skel.h.

    Листинг 2.1. Заголовочный файл skel.h

    1    #ifndef __SKEL_H__

    2    #define __SKEL_H__

    3    /*версия для UNIX */

    4    #define INIT() ( program_name = \

    5                     strrchr ( argv[ 0 ], '/' ) ) ? \

    6                     program_name++ : \

    7                    ( program_name = argv[ 0 ] )

    8    #define EXIT(s) exit( s )

    9    #define CLOSE(s) if ( close( s ) ) error( 1, errno, \

    10                        "ошибка close " )



    11   #define set_errno(e) errno = ( e )

    12   #define isvalidsock(s) ( ( s ) >= 0 )

    13   typedef int SOCKET;

    14   #endif /* __SKEL_H__ */



    Содержание раздела