Литмир - Электронная Библиотека
Содержание  
A
A

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

■ Стандартный поток ввода-вывода может быть создан из любого дескриптора при помощи вызова функции

fdopen
. Аналогично, имея стандартный поток ввода-вывода, мы можем получить соответствующий дескриптор, вызывая функцию
fileno
. С функцией
fileno
мы впервые встретились в листинге 6.1, когда мы хотели вызвать функцию
select
для стандартного потока ввода-вывода. Функция
select
работает только с дескрипторами, поэтому нам необходимо было получить дескриптор для стандартного потока ввода-вывода.

■ Сокеты TCP и UDP являются двусторонними. Стандартные потоки ввода- вывода также могут быть двусторонними: мы просто открываем поток типа

r+
, что означает чтение-запись. Но в таком потоке за функцией вывода не может следовать функция ввода, если между ними нет вызова функции
fflush
,
fseek
,
fsetpots
или
rewind
. Аналогично, за функцией вывода не может следовать функция ввода, если между ними нет вызова функции
fseek
,
fsetpots
,
rewind
, в том случае, когда при вводе не получен признак конца файла. Проблема с последними тремя функциями состоит в том, что все они вызывают функцию
lseek
, которая не работает с сокетами.

■ Простейший способ обработки подобной проблемы чтения-записи — это открытие двух стандартных потоков ввода-вывода для данного сокета: одного для чтения и другого для записи.

Пример: функция str_echo, использующая стандартный ввод-вывод

Сейчас мы модифицируем наш эхо-сервер TCP (см. листинг 5.2) для использования стандартного ввода-вывода вместо функций

readline
и
writen
. В листинге 14.6 представлена версия нашей функции
str_echo
, использующая стандартный ввод-вывод. (С этой версией связана проблема, которую мы вскоре опишем.)

Листинг 14.6. Функция str_echo, переписанная с использованием стандартного ввода-вывода

//advio/str_echo_stdiо02.с

 1 #include "unp.h"

 2 void

 3 str_echo(int sockfd)

 4 {

 5  char line[MAXLINE];

 6  FILE *fpin, *fpout;

 7  fpin = Fdopen(sockfd, "r");

 8  fpout = Fdopen(sockfd, "w");

 9  while (Fgets(line, MAXLINE, fpin) != NULL)

10  Fputs(line, fpout);

11 }

Преобразование дескриптора в поток ввода и поток вывода

7-10
 Функцией
fdopen
создаются два стандартных потока ввода-вывода: один для ввода и другой для вывода. Вызовы функций
readline
и
writen
заменены вызовами функций
fgets
и
fputs
.

Если мы запустим наш сервер с этой версией функции

str_echo
и затем запустим наш клиент, мы увидим следующее:

hpux % <b>tcpcli02 206.168.112.96</b>

<b>hello, world</b> <i>мы набираем эту строку, но не получаем отражения</i>

<b>and hi</b>       <i>и на эту строку нет ответа</i>

<b>hello??</b>      <i>и на эту строку нет ответа</i>

<b>^D</b>           <i>наш символ конца файла</i>

hello, world <i>затем выводятся три отраженные строки</i>

and hi

hello??

Здесь возникает проблема буферизации, поскольку сервер ничего не отражает, пока мы не введем наш символ конца файла. Выполняются следующие шаги:

■ Мы набираем первую строку ввода, и она отправляется серверу.

■ Сервер читает строку с помощью функции

fgets
и отражает ее с помощью функции
fputs
.

■ Но стандартный поток ввода-вывода сервера полностью буферизован стандартной библиотекой ввода-вывода. Это значит, что библиотека копирует отраженную строку в свой стандартный буфер ввода-вывода для этого потока, но не выдает содержимое буфера в дескриптор, поскольку буфер не заполнен.

■ Мы набираем вторую строку ввода, и она отправляется серверу.

■ Сервер читает строку с помощью функции

fgets
и отражает ее с помощью функции
fputs
.

■ Снова стандартная библиотека ввода-вывода сервера только копирует строку в свой буфер, но не выдает содержимое буфера в дескриптор, поскольку он не заполнен.

■ По тому же сценарию вводится третья строка.

■ Мы набираем наш символ конца файла, и функция

str_cli
(см. листинг 6.2) вызывает функцию
shutdown
, посылая серверу сегмент FIN.

■ TCP сервера получает сегмент FIN, который читает функция

fgets
, в результате чего функция
fgets
возвращает пустой указатель.

■ Функция

str_echo
возвращает серверу функцию
main
(см. листинг 5.9), и дочерний процесс завершается при вызове функции
exit
.

■ Библиотечная функция

exit
языка С вызывает стандартную функцию очистки ввода-вывода [110, с. 162-164], и буфер вывода, который был частично заполнен нашими вызовами функции
fputs
, теперь выводит скопившиеся в нем данные.

■ Дочерний процесс сервера завершается, в результате чего закрывается его присоединенный сокет, клиенту отсылается сегмент FIN и заканчивается последовательность завершения соединения TCP.

■ Наша функция

str_cli
получает и выводит три отраженных строки.

■ Затем функция

str_cli
получает символ конца файла на своем сокете, и клиент завершает свою работу.

Проблема здесь заключается в том, что буферизация на стороне сервера выполняется автоматически стандартной библиотекой ввода-вывода. Существует три типа буферизации, выполняемой стандартной библиотекой ввода-вывода.

162
{"b":"225366","o":1}