(fwd) Re: Проблемы с сокетами

Andrey Gerzhov (kittle@routki.ki.yurteh.net)
Wed, 4 Mar 1998 12:37:57 +0200 (EET)

-- forwarded message --
Path: routki.ki.yurteh.net!carrier.kiev.ua!demos!ddt.demos.su!fido-news-server
From: "Vladimir A. Jakovenko" <vovik@ntu-kpi.kiev.ua>
Newsgroups: fido7.ru.unix
Subject: Re: =?KOI8-R?B?8NLPwszFzdk=?= =?KOI8-R?B?INM=?=
=?KOI8-R?B?INPPy8XUwc3J?=
Date: 28 Feb 1998 01:29:42 +0300
Organization: NTUU KPI
Lines: 172
Approved: <gateway@fido7.ru>
Distribution: fido7
Message-ID: <6d79fh$d7s$1@Oberon.NTU-KPI.Kiev.UA>
References: <888082346@f407.n5030.z2.ftn> <1330701575@p37.f23.n5020.z2.ftn> <6d3v7q$um$3@news.lucky.net>
NNTP-Posting-Host: ddt.demos.su
Mime-Version: 1.0
Content-Type: text/plain; charset=KOI8-R
Content-Transfer-Encoding: 8bit
X-BeforeModerator-Path: Oberon.NTU-KPI.Kiev.UA!vovik
X-BeforeModerator-NNTP-Posting-Host: news.ntu-kpi.kiev.ua
X-Newsreader: TIN [UNIX 1.3 unoff BETA 970930; i386 FreeBSD 2.2.5-STABLE]
Xref: routki.ki.yurteh.net fido7.ru.unix:3076

Valentin Nechayev <netch@lucky.net> wrote:
> Hello Ivan!

> At Thu, 26 Feb 98 01:45:10 +0300, Ivan Frolcov wrote:

> >>> Через сокет, объявленный таким образом, передается не более 2 Кб !8-0
> >>> ПОЧЕМУ ?!!!
> AK>> Как известно, протокол tcp/ip не сохраняет границ сообщений. Модуль
> AK>> протокола сам решает, когда следует сформировать пакет и отправить
> AK>> его в сеть/прикладному процессу. Т.о. вызов read на стороне сервера
> AK>> считывает первый tcp пакет. Длина которого оказывается 2К, что
> AK>> вполне разумно. Резюме: надо делать много read пока не прочитаешь
> AK>> нужного числа байт.
> IF> select + FIONREAD?

> Хреново. Пошлется сообщение на 128K, модуль приемки скажет "не выбирает у
> тебя - жди" и подвиснем... поэтому правильно так:

> int mymsgread( int s, char* buf, int size )
> {
> int pos = 0, rest = size;
> while( rest > 0 ) {
> int got = read( s, buf + pos, rest );
> if( got == 0 ) return pos;
> if( got < 0 ) return -errno;
> pos += got; rest -= got;
> }
> return pos;
> }

> А вообще грустно - правильно бы было для read() или recv() иметь соотв. ключ
> - "читать заданное количество байт и не меньше, если не EOF и не ошибка".

"Unix Network Programming", volume 1, second edition, by W. Richard Stevens
sections 3.9, page 77

"Stream sockets (e.g., TCP sockets) exhibit a behavior with read() and write()
functions that differs from normail file I/O. A read() or write() on a
stream socket might input or output fewer bytes than requested, but this is
not error condition. The reason is that buffer limits might be reached for
the sockets in the kernel. All that is required is for the caller to invoke
the read() or write() function again, to input or output the remaining
bytes. (some version of Unix also exhibit this behavior when writing more
than 4096 bytes to a pipe.) This scenario is always a possibility on stream
socket with read(), but is normaly seen with write() only if the socket is
nonblocking."

Далее идут функции которые предлагает Stievens (кстати говоря очень
похоже не netch-евый вариант, токо еще EINTR отрабатывается) -- см. в конце
письма.
И еще на странице 79:

"In Section 13.3 we mention that the MSG_WAITALL flag can be used
with recv() function to replace the need for a separate readn() function."

Смотрим на 13.3 (страница 354):

" MSG_WAITALL This flag was introduced in 4.3BSD Reno. It tells the kernel
not to return from a read operation until the requested number
of bytes have been read. If the system supports this flag, we
can then omit the readn() function (Figure 3.14) and replace
it with macro

#define readn(fd, ptr, n) recv( fd, ptr, n, MSG_WAITALL)

Even if we specify MSG_WAITALL, the function can still return
fewer than requested number of bytes if (a) a signal is caught,
(b) the connection is terminated, or (c) an error is pending
for the socket."

Ну и еще на странице 428 по поводу ioctl-ей над socket-ами:

" FIONREAD Return in the integer pointed to by the third argument to
ioctl() the number of bytes currently in the socket receive
buffer. This feature also works for files, pipes, and
terminals. We said more about this request in Section 13.7."

Вот кусок из section 13.7:

"... This value is the total number of bytes queued, which for a UDP socket
includes all queued datagrams. Also be aware that the count returned for a
UDP socket by Berkeley-derived implementations includes the space required
for the socket address structure containing the sender's IP address and port
for each datagram (16 bytes for IPv4; 24 bytes for IPv6)."

К сожалению в книге слабо освещен Posix.1 asynchronous I/O, но AFAIK тут оно
и небыло нужно ....

In any case http://www.kohala.com/~rstevens/ и там много чего интересного на
эту тему есть :-)

> --
> Valentin Nechayev
> netch@lucky.net
> 2:463/1966.300

-- 
Regards,
Vladimir.

/* include readn */ #include "unp.h"

ssize_t /* Read "n" bytes from a descriptor. */ readn(int fd, void *vptr, size_t n) { size_t nleft; ssize_t nread; char *ptr;

ptr = vptr; nleft = n; while (nleft > 0) { if ( (nread = read(fd, ptr, nleft)) < 0) { if (errno == EINTR) nread = 0; /* and call read() again */ else return(-1); } else if (nread == 0) break; /* EOF */

nleft -= nread; ptr += nread; } return(n - nleft); /* return >= 0 */ } /* end readn */

ssize_t Readn(int fd, void *ptr, size_t nbytes) { ssize_t n;

if ( (n = readn(fd, ptr, nbytes)) < 0) err_sys("readn error"); return(n); }

/* include writen */ #include "unp.h"

ssize_t /* Write "n" bytes to a descriptor. */ writen(int fd, const void *vptr, size_t n) { size_t nleft; ssize_t nwritten; const char *ptr;

ptr = vptr; nleft = n; while (nleft > 0) { if ( (nwritten = write(fd, ptr, nleft)) <= 0) { if (errno == EINTR) nwritten = 0; /* and call write() again */ else return(-1); /* error */ }

nleft -= nwritten; ptr += nwritten; } return(n); } /* end writen */

void Writen(int fd, void *ptr, size_t nbytes) { if (writen(fd, ptr, nbytes) != nbytes) err_sys("writen error"); } -- end of forwarded message --