Передача большого объема данных по локальной сети

User avatar
Vlad G
Уже с Приветом
Posts: 596
Joined: 20 Jan 2002 10:01
Location: Israel

Передача большого объема данных по локальной сети

Post by Vlad G »

Есть у нас программа – читает данные из карда и рисует их. Начальству захотелось передавать еще эти данные на другой компьютер в локальной сети. Вроде все просто – пишется класс использующий TCP сокет, у сервера он пишет, у клиета он читает и передает рисовальщику. Но тут пошла проблема – скорость передачи данных, не удается передать больше 3МБ/сек, ни по локальной сети, ни через прямое соединение 2х компьютеров, ни даже локально (127.0.0.1). Начинает нормально, но через несколько секунд скорость падает.
Писал на MFC с наследованием от CSocket. Эксперементировал с нитками и с размерами пакетов, ни чего не помогает.
Может у кого есть пример перекачки большого объема данных по локальной сети через TCP или по другому?
Спасибо
Big Cheese
Уже с Приветом
Posts: 1211
Joined: 02 Jul 2000 09:01
Location: SFBA

Post by Big Cheese »

Для больших объемов очень рекомендуется смотреть в сторону overlapped I/O / IO completion ports. Посмотрите на codeproject.com / codeguru.com, вот, например, неплохая вроде реализация http://www.codeproject.com/internet/jbsocketserver1.asp
SlaMin
Уже с Приветом
Posts: 176
Joined: 21 Feb 2002 10:01
Location: KZ -> KY -> WA

Post by SlaMin »

Big Cheese wrote:Для больших объемов очень рекомендуется смотреть в сторону overlapped I/O / IO completion ports.

Overlapped IO это конечно классно, только у меня был печальный опыт на Windows 2000 - если клиент медленно получал данные, то сервер в итоге вешался напрочь, только ресет и спасал :) Я тогда грешил на баг в 2000 связанный с использованием nonpaged pool, хотя может и руки :) В любом случае я бы рекомендовал pipes, помоему проще и быстрее.
Big Cheese
Уже с Приветом
Posts: 1211
Joined: 02 Jul 2000 09:01
Location: SFBA

Post by Big Cheese »

SlaMin wrote:
Big Cheese wrote:Для больших объемов очень рекомендуется смотреть в сторону overlapped I/O / IO completion ports.

Overlapped IO это конечно классно, только у меня был печальный опыт на Windows 2000 - если клиент медленно получал данные, то сервер в итоге вешался напрочь, только ресет и спасал :) Я тогда грешил на баг в 2000 связанный с использованием nonpaged pool, хотя может и руки :) В любом случае я бы рекомендовал pipes, помоему проще и быстрее.

Любопытно, никогда про такое не слышал. Не могли бы Вы поподробнее рассказать? Это только на Win2K проявлялось, на других версиях (NT4, XP) все нормально было? Вы backlog как-нибудь ограничивали? Какой протокол использовался?
SlaMin
Уже с Приветом
Posts: 176
Joined: 21 Feb 2002 10:01
Location: KZ -> KY -> WA

Post by SlaMin »

Big Cheese wrote:Любопытно, никогда про такое не слышал. Не могли бы Вы поподробнее рассказать? Это только на Win2K проявлялось, на других версиях (NT4, XP) все нормально было? Вы backlog как-нибудь ограничивали? Какой протокол использовался?


Вот пример который я постил на rsdn почти год назад. К сожаленью, сейчас нет под рукой Win2000, но помнится тогда я смог повторить эту проблему на как минимум 5-ти машинах.

Вот такая проблема:
Есть сервер который посылает пакеты используя Overlapped operations. Есть достаточно тормозной клиент который получает пакеты медленнее чем сервер посылает. Так вот по истечении какого-то времени серверная машина виснет . Причем виснет интересно - ВСЕ замирает, и может через какое-то время развиснуть, а может и нет - вроде бы зависит от того как быстро закрыть клиента.
Повторяемо с точностью 100% на любой Win2000 с последними SP.
Все это происходит на Win2000, на WinXP работает нормально, в смысле WSASend возвращает ошибку WSAENOBUFS. На Win2000 такой ошибки не возникает.
Так как Overlapped operations используют невыгружаемую память - есть предположение что Win2000 не правильно с этим работает. На MS Support и в MSDN ничего по этому поводу не нашел.

Может кто сталкивался с такой проблемой?

Вот минимально необходимые коды сервера и клиента (может в кодах что наглючил?):

Сервер

Code: Select all

#include <winsock2.h>
#include <windows.h>
#include <iostream>
using namespace std;

void CALLBACK CompR(DWORD, DWORD, LPWSAOVERLAPPED, DWORD)
{
    return;
}

int main(int argc, char* argv[])
{
    WORD wVer;
    WSADATA wsaData;
    wVer = MAKEWORD( 2, 2 );
    if (WSAStartup( wVer, &wsaData ) != 0)
        return -1;

    SOCKET hSocket = socket(AF_INET, SOCK_STREAM, 0);
    if (hSocket == INVALID_SOCKET)
        return -1;
    SOCKADDR_IN sa;
    sa.sin_addr.S_un.S_addr = INADDR_ANY;
    sa.sin_family = AF_INET;
    sa.sin_port = htons(9000);
    if (bind(hSocket, (const sockaddr*)&sa, sizeof(sa)) == SOCKET_ERROR)
        return -1;
    if (listen(hSocket, 5) == SOCKET_ERROR)
        return -1;
    sockaddr sac;
    int sz = sizeof(sac);
    SOCKET hClient = accept(hSocket, &sac, &sz);
    if (hClient == INVALID_SOCKET)
        return -1;
    WSABUF wsabuf;
    wsabuf.buf = new char[1024];
    wsabuf.len = 1024;
    DWORD sent;
    WSAOVERLAPPED ov;
    memset(&ov, 0, sizeof(ov));
    for (;) {
        if (WSASend(hClient, &wsabuf, 1, &sent, 0, &ov, CompR) != 0) {
            if (WSAGetLastError() != WSA_IO_PENDING) {
                cout << WSAGetLastError() << endl;
                return -2;
            }
        }
    }
    return 0;
}


Клиент:

Code: Select all

#include <iostream>
#include <winsock2.h>
#include <windows.h>
using namespace std;

int main(int argc, char* argv[])
{
    WORD wVer;
    WSADATA wsaData;
    wVer = MAKEWORD( 2, 2 );
    if (WSAStartup( wVer, &wsaData ) != 0)
        return -1;

    SOCKET hSocket = socket(AF_INET, SOCK_STREAM, 0);
    if (hSocket == INVALID_SOCKET)
        return -1;
    SOCKADDR_IN sa;
    sa.sin_addr.S_un.S_addr = 0x0100007F; // 127.0.0.1 - подставить хост сервера
    sa.sin_family = AF_INET;
    sa.sin_port = htons(9000);
    if (connect(hSocket, (const sockaddr*)&sa, sizeof(sa)) == SOCKET_ERROR)
        return -1;
    char buf[1024];
    int count = 0;
    while (recv(hSocket, buf, 1024, 0) != SOCKET_ERROR) {
        count++;
        if (count % 100 == 0) // возможно понадобится большая задержка (count % 10) - зависит от скорости машины
            cout << count << endl;
    }
    return 0;
}
Big Cheese
Уже с Приветом
Posts: 1211
Joined: 02 Jul 2000 09:01
Location: SFBA

Post by Big Cheese »

Что сразу бросается в глаза - у Вас сокет создается через plain socket() call, тогда как по спеку (насколько я помню) должен создаваться через WASSocket(blah-blah-blah, WSA_FLAG_OVERLAPPED) - может быть, проблема в этом, т.е. (wild guess) - Win2k-реализация winsock по умолчанию создает не-оверлаппед сокет, а WInXP - оверлаппед. Как время будет, попробую изменить в Вашем примере socket->WSASocket и запустить.
SlaMin
Уже с Приветом
Posts: 176
Joined: 21 Feb 2002 10:01
Location: KZ -> KY -> WA

Post by SlaMin »

Очень скоро у меня будет время проверить, но помоему проблема не в этом. Вот выдержки из KB181611
The default SO_OPENTYPE option value is 0, which sets the overlapped attribute. All non-zero option values make the socket synchronous and make it so that you cannot use a completion function.

In Winsock 2, you create an overlapped socket using WSASocket with the WSA_FLAG_OVERLAPPED flag, or simply using the socket API. You can use the above Win32 file I/O APIs or Winsock 2 WSASend, WSASendTo, WSARecv, and WSARecvFrom to initiate an overlapped I/O operation.
alex_127
Уже с Приветом
Posts: 7723
Joined: 29 Mar 2000 10:01
Location: Kirkland,WA

Post by alex_127 »

SlaMin wrote:Так как Overlapped operations используют невыгружаемую память - есть предположение что Win2000 не правильно с этим работает. На MS Support и в MSDN ничего по этому поводу не нашел.

Может кто сталкивался с такой проблемой?


I think you have hit a known issue with window size and overlapped io - basically when the client receive rate is much smaller then server send rate and TCP is used as communication protocol sooner or later client tcp receive window will shrink to 0 and at this moment server will stop sending new data. If your server app is using nonbuffered overlapped io (if I remember correctly) and keeps pumping data it will consume as much of nonpaged pool as it can and may hung the system if quota is not in place.
Easy way to do it right (again, this was some time back) = a) attempt sync write. b) if it will report that it will block (happens <1% of cases), use overlapped io c) never have more then one io outstatanding - you'll never need it d) throttle producer until io completes.

I had couple of fights with WinSock dev's over similar issue in the past :-)

In my experience best throughtput was achieved with 5-10 connections and big window size (to reduce effect of network delays). Nagle algorithm may play some tricks if your data split in small chunks (~<1.5k)
SlaMin
Уже с Приветом
Posts: 176
Joined: 21 Feb 2002 10:01
Location: KZ -> KY -> WA

Post by SlaMin »

Дело в том что на Windows 2000 и Windows XP, эти программы ведут себя по разному, на XP я получаю WSAENOBUFS в момент когда nonpaged pool переполнен, на Windows 2000 думаю в это же самое время машина виснет. Т.е. поведение ХР я считаю правильным, юзер, в данном случае программист, должен получать ошибку, а не подвисание системы.
А если держать очередь на сервере и посылать только после вызова completion routine, то на мой взгляд теряется все преимущество overlapped io.
Тогда, год назад, я перешел на "стандартный" способ работы с сокетами, т.е. пул потоков, блокирующий select, ...
Еще ни разу об этом не пожалел :)

Return to “Вопросы и новости IT”