2009-05-19 13 views
9

Piszę mały program dla zabawy, który przenosi pliki przez TCP w C na Linux. Program odczytuje plik z gniazda i zapisuje go do pliku (lub odwrotnie). Początkowo używałem odczytu/zapisu i program działał poprawnie, ale potem dowiedziałem się o splice i chciałem spróbować.Czy splice Linux (2) działają podczas łączenia z gniazda TCP?

Kod, który napisałem ze splice działa doskonale podczas odczytu ze stdin (przekierowanego pliku) i zapisywania do gniazda TCP, ale natychmiast kończy się niepowodzeniem z ustawieniem zmiennej errno na EINVAL podczas odczytu z gniazda i zapisania na standardowe wyjście. Strona man stwierdza, że ​​EINVAL jest ustawiony, gdy żaden deskryptor nie jest potokiem (nie jest to przypadek), offset jest przekazywany dla strumienia, który nie może szukać (nie ma przesunięć) lub system plików nie obsługuje łączenia, co prowadzi do na moje pytanie: czy to oznacza, że ​​TCP może splajtować z rurą, ale nie na?

Załączam poniższy kod (bez kodu obsługi błędów) w nadziei, że właśnie zrobiłem coś złego. Opiera się głównie na Wikipedia example for splice.

static void splice_all(int from, int to, long long bytes) 
{ 
    long long bytes_remaining; 
    long result; 

    bytes_remaining = bytes; 
    while (bytes_remaining > 0) { 
     result = splice(
      from, NULL, 
      to, NULL, 
      bytes_remaining, 
      SPLICE_F_MOVE | SPLICE_F_MORE 
     ); 

     if (result == -1) 
      die("splice_all: splice"); 

     bytes_remaining -= result; 
    } 
} 

static void transfer(int from, int to, long long bytes) 
{ 
    int result; 
    int pipes[2]; 

    result = pipe(pipes); 

    if (result == -1) 
     die("transfer: pipe"); 

    splice_all(from, pipes[1], bytes); 
    splice_all(pipes[0], to, bytes); 

    close(from); 
    close(pipes[1]); 
    close(pipes[0]); 
    close(to); 
} 

Na marginesie, myślę, że powyższe będzie blokował na pierwszym splice_all gdy plik jest na tyle duża, ze względu na wypełnienie rury się (?), Więc mam również wersję kodu, który fork s do odczytu i zapisu z potoku w tym samym czasie, ale ma taki sam błąd jak ta wersja i jest trudniejszy do odczytania.

EDIT: Moja wersja jądra 2.6.22.18-ko-0.7.3 (coLinux działa na XP).

+0

Z powodzeniem użyłem 'splice' w Haskell: http://stackoverflow.com/questions/10080670/using-gnu-linux-system-call-splice-for-zero-copy-socket-to-socket-data- transfe też :) –

Odpowiedz

8

Jaka wersja jądra to jest? Linux ma wsparcie dla splicingu z gniazda TCP od wersji 2.6.25 (zatwierdzenie 9c55e01c0), więc jeśli używasz wcześniejszej wersji, masz pecha.

+0

To smutne, aby usłyszeć. Czy znasz jakieś alternatywy? –

+1

Alternatywą jest pętla do odczytu/zapisu :) Jeśli korzystasz z/do/gniazda, sendfile() będzie działał, ale będziesz musiał to zrobić w staroświecki sposób. – bdonlan

+0

Dobra, dziękuję =) –

2

Trzeba splice_all z pipes[0] do to za każdym razem robisz pojedynczy splice z from do pipes[1] (the splice_all jest na kwotę bajtów tylko odczytany przez ostatniego pojedynczego splotu). Powód: pipe reprezentuje skończony bufor pamięci jądra. Więc jeśli bajty są więcej, będziesz blokować na zawsze w swoim splice_all(from, pipes[1], bytes).

Powiązane problemy