Оказывается до сих пор в заповедниках живут программисты, которые уверенны что tcp гарантирует им доставку. Хотя на самом деле tcp гарантирует немного другое: _если_ данные будут доставлены, то они будут доставлены без искажений и в том же порядке.
Возник вопрос из описания вот этого бага в ejabberd:
http://www.jabber.ru/bugzilla/show_bug.cgi?id=275В процессе обсуждения выяснилось, что некоторые уверенны что если send(2) на блокирующем сокете не вернул ошибку, значит данные гарантировано доставлены на принимающую сторону. Для проверки нарисовал вот такие две простеньких программы:
send.c:
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <errno.h>
#include <stdio.h>
#include <string.h>
#include <unistd.h>
int main(int argc, char** argv)
{
struct sockaddr_in addr;
memset(&addr,0,sizeof(addr));
addr.sin_family=AF_INET;
addr.sin_port=htons(2020);
addr.sin_addr.s_addr=inet_addr("10.1.1.82");
int sock;
sock = socket(PF_INET, SOCK_STREAM, 0);
if( sock <= 0 )
{
fprintf(stderr, "socket(): %s\n", strerror(errno));
return 1;
}
if( connect(sock,(struct sockaddr *)&addr,sizeof(addr)) )
{
fprintf(stderr, "connect(): %s\n", strerror(errno));
return 1;
}
unsigned char i;
for( i=0 ; i < 255 ; i++ )
{
time_t tm = time(0);
ssize_t count = send(sock,&i,sizeof(i), 0);
printf("%d (%d): %d\n", i, tm, count);
if( count <= 0 )
{
fprintf(stderr, "send(): %s\n", strerror(errno) );
return 1;
}
sleep(1);
}
shutdown(sock,0);
close(sock);
return 0;
}
recv.c
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <errno.h>
#include <stdio.h>
#include <string.h>
int main(int argc, char** argv)
{
struct sockaddr_in addr;
memset(&addr,0,sizeof(addr));
addr.sin_family=AF_INET;
addr.sin_port=htons(2020);
addr.sin_addr.s_addr=inet_addr("10.1.1.82");
int sock, c_sock;
sock = socket(PF_INET, SOCK_STREAM, 0);
if( sock <= 0 )
{
fprintf(stderr, "socket(): %s\n", strerror(errno));
return 1;
}
if( bind(sock,(struct sockaddr *)&addr,sizeof(addr)) )
{
fprintf(stderr, "bind(): %s\n", strerror(errno));
return 1;
}
if( listen(sock,5) )
{
fprintf(stderr, "listend(): %s\n", strerror(errno));
return 1;
}
for(;;)
{
c_sock=accept(sock,NULL,NULL);
if( c_sock <= 0)
{
fprintf(stderr, "accept(): %s\n", strerror(errno));
}
unsigned char i;
for(;;)
{
time_t tm = time(0);
ssize_t count = recv(c_sock,&i,sizeof(i), 0);
printf("%d (%d): %d\n", i, tm, count);
if( count <= 0 )
{
fprintf(stderr, "recv(): %s\n", strerror(errno) );
return 1;
}
}
shutdown(c_sock,0);
close(c_sock);
}
return 0;
}
На одном компьютере запущен recv, потом на втором запущен send , потом через некоторое время работы на первом компьютере сделано
ipfw add deny tcp from any to any 2020
В результате recv вывел
0 (1225361963): 1
1 (1225361963): 1
2 (1225361964): 1
..
59 (1225362022): 1
и остановился в ожидании новых данных, а send вывел
0 (1225361963): 1
1 (1225361964): 1
2 (1225361965): 1
...
141 (1225362105): 1
и вывалился (очевидно получив по голове SIGPIPE)
Вот так.
P.S. для тех, кто думает что если мелкими блоками фиксированного размера слать, то и читать можно так же:
http://dbg.livejournal.com/76148.html