LINUX.ORG.RU

Может ли send() вернуть не -1 и не length?

 ,


0

2

Чтение документации opengroup наводят меня на мысль, что send() всегда возвращает либо -1, либо указанный размер буфера, и никакого partial send существовать не может в принцие, вне зависимости от протокола.

Есть ли опыт, доказывающий обрпаное на какой-либо posix-системе?

★★★★★

Последнее исправление: sv75 (всего исправлений: 1)

send() всегда возвращает либо -1, либо указанный размер буфера

Если это не обещано четко, рассчитывать на это не следует. Тем более, что:

With zero flags argument, send() isequivalent to write(2)

tailgunner ★★★★★
()
Ответ на: комментарий от tailgunner

да, эта ремарка несколько неприятна. Однако в самом описании send() четко говорится, что если места нет и не будет, то -1, иначе блокируемся да посылки.

Просто иначе получается, что корректный send всегда должен быть в цикле со сдвигом указателя посылки. Правда, корректный send всегда должен быть в цикле и так из-за EINTR...

sv75 ★★★★★
() автор топика
#include <stdio.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/ip.h>

static char buf[1024*1024*1024];

int main(void)
{
    struct sockaddr_in addr;

    int s = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
    if (s < 0) {
        perror("socket");
        return -1;
    }

    addr.sin_family = AF_INET;
    addr.sin_port = htons(80);
    addr.sin_addr.s_addr = htonl(0xc0a80001);

    if (connect(s, (struct sockaddr *)&addr, sizeof(addr))) {
        perror("connect");
        close(s);
        return -1;
    }

    ssize_t res = send(s, buf, sizeof(buf), 0);
    printf("send %zu --> %zd\n", sizeof(buf), res);

    close(s);
    return 0;
}
$ gcc -o /tmp/send /tmp/send.c
$ /tmp/send 
send 1073741824 --> 60498888
$ /tmp/send 
send 1073741824 --> 64790760
$ /tmp/send 
send 1073741824 --> 68402072
Deleted
()
Ответ на: комментарий от Deleted

Учитывая величины цифр, есть соблазн трактовать сей опыт хочется трактовать как кривую реализацию стандартов posix при фантастических length.

sv75 ★★★★★
() автор топика

memcpy

Для чего тебе это надо? Если для развития кругозора - то да, стоит поразмыслить и сделать выводы. Но если экономии трех букв в коде, типа «так быстрее, потому-что я знаю как работает ф-я», то настоятельно рекомендую ознакомиться с притчей о старике по имени adobe, и его слуге по имени memcpy. Очень поучительная история.

nanoolinux ★★★★
()
Ответ на: комментарий от tailgunner

Это конечно так. Однако, send ничего про это не говорит )

sv75 ★★★★★
() автор топика
Ответ на: memcpy от nanoolinux

Историю знаю, но там они явно нарушили явное требование. Хочу знать, что сказать детям. Видимо, «блокирующий send всегда надо оборачивать циклом, для верности» (или как минимум при объеме данных более килобайта).

sv75 ★★★★★
() автор топика
Ответ на: комментарий от sv75

Если честно, я вообще то не совсем понимаю, что именно у тебя вызвало сомнения. В man page ясно написано:

On success, these calls return the number of characters sent. On error, -1 is returned, and errno is set appropriately.

так что в любом случае правильный (принципиальный) код будет такой:

int bytes_sent = 0;
int ret;
while (bytes_sent < buff_len) {
   ret = send(buffer+bytes_sent, buff_len-bytes_sent);
   if (ret < 0) {
       return ret;
   }
   bytes_sent += ret;
}
вне зависимости меньше килобайта или не меньше. Учи детей писать правильный код изначально, не ломай им мозги.

nanoolinux ★★★★
()
Ответ на: комментарий от sv75

В man page её и не будет. Это требование стандарта. Технически причина в том, что внутренних буферов ярда для пакетов может не хватать. При правильно написанном модуле может быть банальная нехватка памяти например. Тысячи причин могут быть. Стандарт это не оговаривает и не может оговорить, т.к. это уже детали реализации.

nanoolinux ★★★★
()
Ответ на: комментарий от nanoolinux

так что в любом случае правильный (принципиальный) код будет такой

Э, а вышеупомянутый errno=EINTR? Хотелось бы дать send'у в этом случае ещё шанс.

Да и bytes_sent должно быть типа size_t, как и buff_len

size_t bytes_sent = 0;
int ret;
while (bytes_sent < buff_len) {
   ret = send(buffer+bytes_sent, buff_len-bytes_sent);
   if (ret < 0 && errno != EINTR) {
       return ret;
   }
   else if (ret > 0) {
      bytes_sent += ret;
   }
}
Уверен, что и тут можно ещё найти, к чему прицепиться.

gag ★★★★★
()
Ответ на: комментарий от nanoolinux

Я так и написал, а потом задувмался, не слишком ли я все усложняю....

Да, кстати, надо же еще проверять EINTR при возврате -1 как особый случай, это не повод же бросать попытки )

Учи детей писать правильный код изначально, не ломай им мозги.

Мне кажется тут " и" вместое ", не" скорее

sv75 ★★★★★
() автор топика
Ответ на: комментарий от gag

Уверен, что и тут можно ещё найти, к чему прицепиться.

да, с posix никогда нельзя быть уверенным окончательно...

sv75 ★★★★★
() автор топика
Ответ на: комментарий от gag

Не внимательно читаешь. Я специально в скобочках написал слово «принципиальный». А этот кусок кода даже не откомпилируется, да.

nanoolinux ★★★★
()
Ответ на: комментарий от sv75

Причём -1 и EINTR будет только в случае, когда совсем не было переданно данных, иначе и возвращается количество фактически переданных байт на момент получения сигнала. Это в случае с stream-протоколами.

И если удалённая сторона закроет tcp-сокет, то тоже будет возвращено кол-во переданных байт, и только следующий send() вернёт -1 и ошибку сокета.

mky ★★★★★
()
Вы не можете добавлять комментарии в эту тему. Тема перемещена в архив.