windows - Langsom overførselshastighed på fjerncomputer

Indlæg af Hanne Mølgaard Plasc

Problem



Hej der StackOverflow folk!


Jeg laver en IOCP-server, og jeg har stryket de fleste problemer indtil videre, men man forbliver stadig, og jeg ved ikke, hvor jeg skal kigge på. Når jeg kører klienten/serveren på min maskine, er alt fint og dandy. Det passer til hastigheden af ​​Windows SDK-prøven kan være lidt hurtigere og bruger bestemt mindre CPU-cyklus. Men når jeg kører klienten fra en separat computer, overfør hastighedsdæksler på 37 KB/s og har en rundvisningstid på 200 ms (i modsætning til 0) . Nu, hvis jeg forbinder klienten med SDK Sample-serveren, har jeg ikke det problem, så der er noget galt med min kode. Så vidt jeg ved, initialiseres stikkene på samme måde med de samme muligheder. Jeg har også kørt min server i en profiler for at kontrollere flaskehalsen, men jeg kunne ikke finde nogen. Også de computere, jeg har prøvet det på, var forbundet med den samme gigabit-switch (med gigabit adapter) .Jeg ved, at dette er lidt vagt men det er fordi jeg ikke kunne finde ud af problemet hidtil, og jeg ville være evigt taknemmelig, hvis nogen af ​​jer kunne pege på mig i den rigtige retning.


Skål,


-Roxy


Edit2:
Efter at have fulgt Mike's rådgivning gjorde jeg nogle undersøgelser af koden og fandt ud af, at når en fjernklient forbinder til serveren, venter koden mest på GetQueuedCompletionStatus. Dette tyder på, at IO-anmodning simpelthen tager lang tid at afslutte men jeg forstår stadig ikke hvorfor. Dette sker først kun, når klienten er på en fjerncomputer. Jeg tror, ​​det har noget at gøre med, hvordan en opsætning stikkene eller hvordan jeg sender forespørgslen, men jeg ser ingen forskel med prøvekoden.


Nogle ideer?


EDIT (Tilføjet prøvekode):


Okay, her er det! Det er ikke temmelig!


Hvis du har installeret Windows SDK, kan du oprette forbindelse til det ved hjælp af iocpclient-prøven (Program Files \ Microsoft SDKs \ Windows \ v7.1 \ Samples \ netds \ winsock \ iocp \ klient) og ændre den s standardport på linje 73 til 5000.


Underlige ting, jeg lige har bemærket, når jeg prøver det selv, er, at det ser ud til, at prøven iocpclient ikke forårsager de samme kapper på 37KB/s problem ... Men det ser ud til at prøvekoden har en grænse på omkring 800KB/s. Jeg vil sende en klient, hvis det kan være til nogen hjælp.


#pragma comment(lib, "Ws2\_32.lib")

#include <WinSock2.h>
#include <stdio.h>

unsigned int connection = 0;
unsigned int upload = 0;
unsigned int download = 0;

#define IO\_CONTEXT\_COUNT 5

class NetClientHost
{
friend class gNetProtocolHost;
public:
enum Operation
{
    kOperationUnknown,
    kOperationRead,
    kOperationWrite,
};

struct ClientData
{
    SOCKET           socket;
};

struct IOContext
{
    WSAOVERLAPPED    overlapped;
    WSABUF           wsaReceiveBuf;
    WSABUF           wsaSendBuf;
    char            *buf;
    char            *TESTbuf;
    unsigned long    bytesReceived;
    unsigned long    bytesSent;
    unsigned long    flags;
    unsigned int     bytesToSendTotal;
    unsigned int     remainingBytesToSend;
    unsigned int     chunk;
    Operation        operation;
};

NetClientHost()
{
    memset((void *) &m\_clientData, 0, sizeof(m\_clientData));
}

NetClientHost::IOContext *NetClientHost::AcquireContext()
{
    while (true)
    {
        for (int i = 0; i < IO\_CONTEXT\_COUNT; ++i)
        {
            if (!(m\_ioContexts + i)->inUse)
            {
                InterlockedIncrement(&(m\_ioContexts + i)->inUse);
                //ResetEvent(*(m\_hContextEvents + i));

                if ((m\_ioContexts + i)->ioContext.TESTbuf == 0)
                    Sleep(1);

                return &(m\_ioContexts + i)->ioContext;
            }
        }
        //++g\_blockOnPool;
        //WaitForMultipleObjects(IO\_CONTEXT\_COUNT, m\_hContextEvents, FALSE, INFINITE);
    }   
}

const ClientData *NetClientHost::GetClientData() const
{
    return &m\_clientData;
};

void NetClientHost::Init(unsigned int bufferSize)
{   
    \_InitializeIOContexts(bufferSize ? bufferSize : 1024);
}

void NetClientHost::ReleaseContext(IOContext *ioContext)
{
    int i = sizeof(\_IOContextData), j = sizeof(IOContext);
    \_IOContextData *contextData = (\_IOContextData *) (((char *) ioContext) - (i - j));
    InterlockedDecrement(&contextData->inUse);
    //SetEvent(*(m\_hContextEvents + contextData->index));
}   

struct \_IOContextData
{
    unsigned int index;
    volatile long inUse;        
    IOContext ioContext;
};

ClientData                    m\_clientData;
\_IOContextData               *m\_ioContexts;
HANDLE                       *m\_hContextEvents;

void \_InitializeIOContexts(unsigned int bufferSize)
{
    m\_ioContexts = new \_IOContextData[IO\_CONTEXT\_COUNT];
    m\_hContextEvents = new HANDLE[IO\_CONTEXT\_COUNT];

    memset((void *) m\_ioContexts, 0, sizeof(\_IOContextData) * IO\_CONTEXT\_COUNT);

    for (int i = 0; i < IO\_CONTEXT\_COUNT; ++i)
    {
        (m\_ioContexts + i)->index = i;

        (m\_ioContexts + i)->ioContext.buf = new char[bufferSize];
        (m\_ioContexts + i)->ioContext.wsaReceiveBuf.len = bufferSize;
        (m\_ioContexts + i)->ioContext.wsaReceiveBuf.buf = (m\_ioContexts + i)->ioContext.buf;
        (m\_ioContexts + i)->ioContext.TESTbuf = new char[10000];
        (m\_ioContexts + i)->ioContext.wsaSendBuf.buf = (m\_ioContexts + i)->ioContext.TESTbuf;

        *(m\_hContextEvents + i) = CreateEvent(0, TRUE, FALSE, 0);
    }
}
void \_SetSocket(SOCKET socket)
{
    m\_clientData.socket = socket;
}
};



bool WriteChunk(const NetClientHost *clientHost, NetClientHost::IOContext *ioContext)
{
int status;

status = WSASend(clientHost->GetClientData()->socket, &ioContext->wsaSendBuf, 1, &ioContext->bytesSent, ioContext->flags, &ioContext->overlapped, 0);
if (status == SOCKET\_ERROR && WSAGetLastError() != WSA\_IO\_PENDING)
{
    // ...
    return false;
}

return true;
}

bool Write(NetClientHost *clientHost, void *buffer, unsigned int size, unsigned int chunk)
{
//\_\_ASSERT(m\_clientHost);
//\_\_ASSERT(m\_clientHost->GetClientData()->remainingBytesToSend == 0);

NetClientHost::IOContext *ioContext = clientHost->AcquireContext();

if (!chunk)
    chunk = size;

ioContext->wsaSendBuf.buf = ioContext->TESTbuf;

ioContext->operation                = NetClientHost::kOperationWrite;
ioContext->flags                    = 0;
ioContext->wsaSendBuf.buf = new char[size];
memcpy((void *) ioContext->wsaSendBuf.buf, buffer, chunk);
ioContext->wsaSendBuf.len           = chunk;    
ioContext->chunk                    = chunk;
ioContext->bytesToSendTotal         = size;
ioContext->remainingBytesToSend     = size;

return WriteChunk(clientHost, ioContext);
}



void Read(NetClientHost *clientHost)
{   
NetClientHost::IOContext *ioContext = clientHost->AcquireContext();
int status;

memset((void *) ioContext, 0, sizeof(NetClientHost::IOContext));
ioContext->buf = new char[1024];
ioContext->wsaReceiveBuf.len = 1024;
ioContext->wsaReceiveBuf.buf = ioContext->buf;

ioContext->flags = 0;
ioContext->operation = NetClientHost::kOperationRead;

status = WSARecv(clientHost->GetClientData()->socket, &ioContext->wsaReceiveBuf, 1, &ioContext->bytesReceived, &ioContext->flags, &ioContext->overlapped, 0);
int i = WSAGetLastError();
if (status == SOCKET\_ERROR && WSAGetLastError() != WSA\_IO\_PENDING)
{
    // ...
}   
}

bool AddSocket(HANDLE hIOCP, SOCKET socket)
{
++connection;

int bufSize = 0;
LINGER lingerStruct;
lingerStruct.l\_onoff = 1;
lingerStruct.l\_linger = 0;
setsockopt(socket, SOL\_SOCKET, SO\_SNDBUF, (char *) &bufSize, sizeof(int));
setsockopt(socket, SOL\_SOCKET, SO\_RCVBUF, (char *) &bufSize, sizeof(int));
setsockopt(socket, SOL\_SOCKET, SO\_LINGER, (char *) &lingerStruct, sizeof(lingerStruct) ); 

NetClientHost *clientHost = new NetClientHost;

clientHost->\_InitializeIOContexts(1024);
clientHost->Init(0);
clientHost->\_SetSocket(socket);

// Add this socket to the IO Completion Port
CreateIoCompletionPort((HANDLE) socket, hIOCP, (DWORD\_PTR) clientHost, 0);

Read(clientHost);
return true;
}

int read = 0, write = 0;

DWORD WINAPI WorkerThread(LPVOID param)
{
LPOVERLAPPED overlapped;
NetClientHost *clientHost;
HANDLE hIOCP = (HANDLE) param;
DWORD ioSize;
BOOL status;

while (true)
{
    status = GetQueuedCompletionStatus(hIOCP, &ioSize, (PULONG\_PTR) &clientHost, (LPOVERLAPPED *) &overlapped, INFINITE);

    if (!(status || ioSize))
    {
        --connection;
        //\_CloseConnection(clientHost);
        continue;
    }

    NetClientHost::IOContext *ioContext = (NetClientHost::IOContext *) overlapped;

    switch (ioContext->operation)
    {
    case NetClientHost::kOperationRead:
        download += ioSize;
        Write(clientHost, ioContext->wsaReceiveBuf.buf, ioSize, 0);
        write++;
        clientHost->ReleaseContext(ioContext);
        break;

    case NetClientHost::kOperationWrite:
        upload += ioSize;
        if (ioContext->remainingBytesToSend)
        {
            ioContext->remainingBytesToSend -= ioSize;
            ioContext->wsaSendBuf.len = ioContext->chunk <= ioContext->remainingBytesToSend ? ioContext->chunk : ioContext->remainingBytesToSend; // equivalent to min(clientData->chunk, clientData->remainingBytesToSend);
            ioContext->wsaSendBuf.buf += ioContext->wsaSendBuf.len;
        }

        if (ioContext->remainingBytesToSend)
        {       
            WriteChunk(clientHost, ioContext);
        }
        else
        {
            clientHost->ReleaseContext(ioContext);              
            Read(clientHost);
            read++;
        }
        break;
    }
}

return 0;
}

DWORD WINAPI ListenThread(LPVOID param)
{
SOCKET sdListen = (SOCKET) param;

HANDLE hIOCP = CreateIoCompletionPort(INVALID\_HANDLE\_VALUE, 0, 0, 0);
CreateThread(0, 0, WorkerThread, hIOCP, 0, 0);
CreateThread(0, 0, WorkerThread, hIOCP, 0, 0);
CreateThread(0, 0, WorkerThread, hIOCP, 0, 0);
CreateThread(0, 0, WorkerThread, hIOCP, 0, 0);

while (true)
{
    SOCKET as = WSAAccept(sdListen, 0, 0, 0, 0);
    if (as != INVALID\_SOCKET)
        AddSocket(hIOCP, as);
}
}

int main()
{
SOCKET      sdListen;
SOCKADDR\_IN si\_addrlocal;   
int         nRet;   
int         nZero = 0;   
LINGER      lingerStruct;   

WSADATA wsaData;
WSAStartup(0x202, &wsaData);

sdListen = WSASocket(AF\_INET, SOCK\_STREAM, IPPROTO\_IP, NULL, 0, WSA\_FLAG\_OVERLAPPED);    
si\_addrlocal.sin\_family = AF\_INET;   
si\_addrlocal.sin\_port = htons(5000);   
si\_addrlocal.sin\_addr.s\_addr = htonl(INADDR\_ANY);          
nRet = bind(sdListen, (struct sockaddr *)&si\_addrlocal, sizeof(si\_addrlocal));   
nRet = listen(sdListen, 5);

nZero = 0;   
nRet = setsockopt(sdListen, SOL\_SOCKET, SO\_SNDBUF, (char *) &nZero, sizeof(nZero));   
nZero = 0;   
nRet = setsockopt(sdListen, SOL\_SOCKET, SO\_RCVBUF, (char *)&nZero, sizeof(nZero));
lingerStruct.l\_onoff = 1;   
lingerStruct.l\_linger = 0; 
nRet = setsockopt(sdListen, SOL\_SOCKET, SO\_LINGER, (char *)&lingerStruct, sizeof(lingerStruct) );

CreateThread(0, 0, ListenThread, (LPVOID) sdListen, 0, 0);

HANDLE console = GetStdHandle(STD\_OUTPUT\_HANDLE);
while (true)
{
    COORD c = {0};
    SetConsoleCursorPosition(console, c);
    printf("Connections: \%i                      
Upload: \%iKB/s               
Download: \%iKB/s              ", connection, upload * 2 / 1024, download * 2 / 1024);
    upload = 0;
    download = 0;
    Sleep(500);
}



return 0;
}

Bedste reference


Denne slags asynkrone system skal kunne køre med fuld datalinkhastighed. Problemer jeg har fundet forkert er som:



  • timeout indstillinger forårsager unødvendige genudsendelser

  • i modtagelsesprocessen kan modtaget besked A udløse en databaseopdatering, sådan at den modtagne besked B skal vente, hvilket forårsager unødvendig forsinkelse af svaret på besked B tilbage til afsenderen, når DB-opdateringen faktisk kan gøres i tomgangstid.



Der er noget der hedder wireshark , der kan give dig en vis synlighed i meddelelsestrafik.
Jeg plejede at gøre det på den hårde måde med tidsstemplede meddelelseslogfiler.


BTW: Jeg vil først bruge denne metode på de enkelte processer, for at rense eventuelle flaskehalser, før du udfører asynkron analyse. Hvis du ikke har gjort dette, kan du satse på, at de er derinde.
Kun enhver gammel profiler er ikke pålidelig. Der er gode, herunder Zoom. [3]