Добавил:
Upload Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:
СистПриклПрогЗабез(Хихловская).doc
Скачиваний:
25
Добавлен:
10.02.2016
Размер:
961.02 Кб
Скачать

Tcp как потоковый протокол

Данные доставляются получателю в виде потока, в котором нет понятия сообщения или границы сообщения. В этом отношении чтение данных похоже на чтение из последовательного порта - заранее неизвестно, сколько байтов будет возвращено после выполнения функции чтения. Для TCP–приложения нет понятия «пакет», и хотя данные передаются вIP-пакетах, размер пакета не связан напрямую с количеством данных, переданныхTCPпри вызовеsend. У принимающего приложения нет надёжного способа определения, как именно данные распределены по пакетам, так как между соседними вызовамиrecvможет прийти несколько пакетов. Если один пакет потерян, а последующие пришли нормально,TCP“задерживает поступившие данные”, пока не будет повторно передан и корректно принят пропавший пакет.TCPследит за количеством байтов, посланных и подтвержденных, но не за их распределением по пакетам. Если пользоваться для чтения данных стандартной библиотечной функциейfgets, то она сама будет разбивать поток байтов на строки. Самый простой случай-это сообщение фиксированной длины. Тогда нужно прочесть заранее известное число байтов из потока. Для этого недостаточно выполнить простое однократное чтение:

Recv(s,msg,sizeof(msg),o);

Так как при этом можно получить меньше, чем sizeof(msg) байт

Функция readn

  1. in readn(SOCKET fd,char*bp,size_t len)

  2. {

  3. int ent;

  4. int rc;

  5. ent=len;

  6. while(ent>0)

  7. {

  8. rc=recv(fd,bp,ent,0);

  9. if(rc<0) /’’ошибка чтения?*/

  10. {

  11. if(errno==ENTER)/*Вызов прерван?*/

  12. continue;/*повторить чтение*/

  13. return-1/*вернуть код ошибки*/

  14. }

  15. if(rc==0)/*конец файла*?/

  16. returnlen-ent/*вернуть неполный счётчик*/

  17. bp+=rc

  18. ent-=rc;

  19. }

  20. return len;

  21. }

Функция readnне возвращает управление, пока не будет прочитаноlenбайт или не получен конец файла, или не возникнет ошибка. Её прототип выглядит следующим образом :

# include <etcp.h>

int readn(SOCKET s,char*buf,size_tlen);

возвращаемое значение - число прочитанных байтов или –1 в случае ошибки.

Оператор if

If (errno==ENTER)

Continue;

(Errno-глобальная переменная ,ENTER -прерванный вызов функции) возобновляет выполнение вызова recv, если он прерван сигналом.

В случае сообщений переменной длины есть два метода разделения записей:

  1. с помощью специальных маркеров с использованием fgetsдля разбиения потока на строки, например, с помощью разделителя-символа новой строки/. Тогда, если оно применяется в теле, то нужно здесь заменить его на //. На приёмной стороне происходит обратный процесс.

  2. Каждое сообщение снабжается заголовком, содержащим, как минимум длину следующего за ним тела.

ДЛИНА ЗАПИСИ

ДРУГИЕ ДАННЫЕ ЗАГОЛОВКА

ДАННЫЕ ПЕРЕМЕННОЙ ДЛИНЫ

В процедуре fgetsпроисходит считывание символов из потока в буфер пока не встретится символ/.

Принимающее приложение читает сообщение в два приёма: заголовок фиксированной длины и из него извлекается переменная длина тела сообщения, а затем самое тело.

  1. intreadvrec(SOCKETfd,char*bp,size_tlen),гдеSOCKET-тип данных,t-длина буфера.

  2. {

  3. uint32_t reclen;

  4. Int rc;

  5. /*прочитать длину записи*/

  6. rc=readn(fd,(char*)&reclen ,size of(u_int32_t));

  7. if(rc!=sizeof(u_int32_t))

  8. return rc<0?-1:0;

  9. reclen=ntohl(reclen);/*сетевой в машинный*/

  10. if(reclen>len)

  11. {

  12. /*

  13. *Не хватает места в буфере для размещения

  14. *данных – отбросить их и вернуть код ошибки

  15. */

  16. while(reclen>0)

  17. {

  18. rc=readn(fd,bp,len);

  19. if(rc!=len)

  20. returnrc<0?-1:0;

  21. reclen==len;/*reclen=reclen-len*/

  22. if(reclen<len)

  23. len=reclen;

  24. }

  25. set_errno(EMSGSIZE);

  26. return-1;

  27. }

  28. /*прочитать саму запись*/

  29. rc=readn(fd,bp,reclen);

  30. if(rc!=reclen)

  31. return rc<0?-1:0;

  32. return rc;

  33. }