[ros-dev] Loopback problem
Ge van Geldorp
gvg at reactos.org
Wed Dec 21 00:31:17 CET 2005
I've been tracking down a problem with ibrowser being extremely slow for me
(it took 64 sec to load http://www.reactos.org). It turns out to be a
problem associated with the loopback interface. The attached test program
(gcc -o loop.exe loop.c -lws2_32) is the minimum test program to demonstrate
the problem. It occurs when you send a small amount of data over the
loopback interface when there is no pending recv (so you send, then start a
recv for the data, then another send, then another recv, no recv waiting
while you send).
Sequence of events:
- Start first send
- TCP/IP stack queues the data at the receiving end
- First send returns
- Start first recv
- Queued data is retrieved, but it is determined that there's plenty of
space in the TCP window left, so there is no ACK sent back
- First recv returns
- Start second send
- tcp_output determines that the connection is not idle (there is some
un-ACKed data) and queues the data at the sending end
- Second send returns
- Start second recv
- Since there is no data waiting at the receiving end, recv just sits there
- An internal timer with a period of 2.5 sec expires
- The timer proc notices that there is an ACK pending
- ACK is sent back to the sending end
- Sending end determines connection is idle now and sends the queued data to
the receiving end
- Timer proc terminates and reschedules itself
- Data has now arrived at the receiving end and can be retrieved by the
waiting recv. Again no ACK is sent back
- Second recv returns
- Third send starts, queues its data at sending end and returns
- Third recv starts, has to wait 2.5 sec for the internal timer to timeout
and then returns
- etc.
Although I understand what's wrong, I have a bit of difficulty trying to
figure out how to fix it. Attached is a proposed fix, which basically
attacks the problem at the sending end by removing the check if the
connection is idle or not. With that fix the loop.c program works as I
expect and http://www.reactos.org loads in a much more reasonable 2-3 sec in
ibrowser. The problem that I have with the fix is that it's a change in code
we borrowed from the BSD stack. I can hardly imagine that a piece of
software so heavily used as the BSD stack would have such a fundamental
problem.
Any thoughts?
GvG
-------------- next part --------------
#include <stdio.h>
#include <windows.h>
#define DATASIZE 1
int
main(int argc, char *argv[])
{
WSADATA wsaData;
SOCKET Listen, Send, Receive;
struct sockaddr_in ListenAddr, TalkAddr;
int AddrLen;
char Data[DATASIZE];
unsigned Count;
DWORD Time1, Time2;
int BytesToReceive, BytesReceived;
if (0 != WSAStartup(MAKEWORD(1, 1), &wsaData))
{
fprintf(stderr, "WSAStartup failed\n");
exit(1);
}
Listen = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
if (INVALID_SOCKET == Listen)
{
fprintf(stderr, "socket() for Listen failed\n");
exit(1);
}
ListenAddr.sin_family = AF_INET;
ListenAddr.sin_port = 0;
ListenAddr.sin_addr.s_addr = htonl(INADDR_LOOPBACK);
if (0 != bind(Listen, (struct sockaddr *) &ListenAddr, sizeof(struct sockaddr_in)))
{
fprintf(stderr, "bind() for Listen failed\n");
closesocket(Listen);
exit(1);
}
AddrLen = sizeof(struct sockaddr_in);
if (0 != getsockname(Listen, (struct sockaddr *) &ListenAddr, &AddrLen))
{
fprintf(stderr, "getsockname() failed\n");
closesocket(Listen);
exit(1);
}
if (0 != listen(Listen, 1))
{
fprintf(stderr, "listen() failed\n");
closesocket(Listen);
exit(1);
}
Send = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
if (INVALID_SOCKET == Send)
{
fprintf(stderr, "socket() for Send failed\n");
closesocket(Listen);
exit(1);
}
TalkAddr.sin_family = AF_INET;
TalkAddr.sin_port = 0;
TalkAddr.sin_addr.s_addr = htonl(INADDR_LOOPBACK);
if (0 != bind(Send, (struct sockaddr *) &TalkAddr, sizeof(struct sockaddr_in)))
{
fprintf(stderr, "bind() for Send failed\n");
closesocket(Send);
closesocket(Listen);
exit(1);
}
if (0 != connect(Send, (struct sockaddr *) &ListenAddr, sizeof(struct sockaddr_in)))
{
fprintf(stderr, "connect() failed\n");
closesocket(Send);
closesocket(Listen);
exit(1);
}
Receive = accept(Listen, NULL, NULL);
if (INVALID_SOCKET == Receive)
{
fprintf(stderr, "accept() failed\n");
closesocket(Send);
closesocket(Listen);
exit(1);
}
closesocket(Listen);
for (Count = 0; Count < 5; Count++)
{
memset(Data, Count & 0xff, DATASIZE);
Time1 = GetTickCount();
if (DATASIZE != send(Send, Data, DATASIZE, 0))
{
fprintf(stderr, "send() failed\n");
closesocket(Receive);
closesocket(Send);
exit(1);
}
Time2 = GetTickCount();
printf("send took %u ms\n", (unsigned) (Time2 - Time1));
BytesToReceive = DATASIZE;
while (0 != BytesToReceive)
{
BytesReceived = recv(Receive, Data + DATASIZE - BytesToReceive, BytesToReceive, 0);
if (BytesReceived <= 0)
{
fprintf(stderr, "recv() failed\n");
closesocket(Receive);
closesocket(Send);
exit(1);
}
BytesToReceive -= BytesReceived;
}
printf("recv took %u ms\n\n", (unsigned) (GetTickCount() - Time2));
}
closesocket(Receive);
closesocket(Send);
return 0;
}
-------------- next part --------------
A non-text attachment was scrubbed...
Name: tcp_output.diff
Type: application/octet-stream
Size: 508 bytes
Desc: not available
Url : http://www.reactos.org/pipermail/ros-dev/attachments/20051221/48d65e0d/tcp_output.obj
More information about the Ros-dev
mailing list