connection reset by peer

Fórumok

Sziasztok!
Kerdes: UNIX domain socket-ek ko"re'ben a `connection reset by peer` (ECONNRESET) pontosan mit takar, mikor fordulhat elo"? Ha ket program kozul az egyik (konkretan a kliens) szepen bezarja, majd kilep, szabalyosan (main() leall), akkor a szerver oldalon a read() nem 0-val hanem -1 + ECONNRESET-tel te'r vissza. Semmi extra konfig (setsockopt?) nem volt a szerver/kliens socket-en, a szerveroldali algoritmusban semmi nagy durranas nincs, a valgrind nem jelez semmi problemat, es ha kezzel beleteszem a protokolba, hogy a kliens "koszonjon el" es a szerver bontsa a kapcsolatot, akkor meg minden fasza (alatamasztando, hogy nem az algoritmusba kerult pl. hibas pointerkezeles altal pl. felulirt memoria kavar be).
Szoval a konkret problema ez, de altalanossagban is erdekel, hogy mi kozhat conn. reset-et egy ilyen (legalabbis elsore) egyszerunek tuno objektumon mint unix socket. Mondjuk ha az egyik oldal aborta'l vagy sigsegv hatasara all le, akkor oke, ugy el tudom kepzelni, de itt semmi ilyenrol nincs szo...
koszi, A.
szerk: c/c++ kornyezet nem tudom mennyit szamit, csak azert valasztottam ezt a kategoriat mert a problema ebben a kornyezetben merult fel (C progi), de az alap-kerdes persze nyelv-fuggetlen...

Hozzászólások

Tapasztalatom az, hogy ha a szerver, tehát amelyik accepteld, az zárja le close()-val a socketet, akkor a kliens read()-ja nullával tér vissza, ha a kliens, tehát amelyik connect()-elt, zárja le close()-val a kapcsolatot, akkor a szerveren ECONNRESET lesz az eredmény.
De a szerver ezt egy if()-fel simán tudja kezelni.
Ha saját "elköszönési" protokoll nélkül akarod a kliensről lezárogatni a socket()-et, akkor próbáld ki, hogy a close() előtt shutdown()-olsz a socketen. Hátha akkor nem lesz ECONNRESET, most nem emlékszem így hirtelen.

Hm, ezt most kiprobaltam egy gyors peldan (az socket parancs unix socket kezelesere peccselt verziojaban ill. siman inet socket-ekre), es mintha ott mukodne. Szoval lehet hogy megsem teljesen ez, bar ott lehetnek me'g fura dolgok.

Azert me'g nyomozok... koszi, ez a shutdown() mindenkepp probara erdemes.

Figyelj, olyan nagy feneket ne keríts azért a dolgonak (nem érdemes - sok eltérő rendszer, eltérő implementáció, lehet, hogy vannak pici eltérések), inkább valahogy így írd meg a programot, így tutira mész. :)


int readval;
...
...
readval = read(....);
if (!readval || (readval==-1 && errno == ECONNRESET)) {
printf("socket closed, client logout.\n");
...
...
return;
} else if (readval==-1) {
printf("FATAL ERROR: read() error: %d %s\n",errno,strerror(errno));
...
...
return;
}

process_data_from_socket(...);
...
...

(Azért javaslom, hogy close() előtt mindig hívj shutdown()-nt, az kiküldi a pending buffereket - lehet, hogy a FIN flag csak ekkor megy ki egy TCP keretben, mert egyébként nincs éppen kimenő csomag, amire ráültetné a TCP/IP stack, és ez jelezné a másik oldalon, hogy lezárták a socketet, és a read ekkor adna vissza nullát. - Persze ez csak az én gondolatom, ki tudja, hogy vannak öszekókányolva a TCP/IP stackek. :) )

(Bocsesz, space-vel húztam be a sorokat, mégis eltünt a formázásom. :( )

/usr/src/linux/net/unix/af_unix.c:


#define unix_peer(sk) (unix_sk(sk)->peer)

static void unix_dgram_disconnected(struct sock *sk, struct sock *other)
{

    if (!skb_queue_empty(&sk->sk_receive_queue)) {
        [...]
        /* If one link of bidirectional dgram pipe is disconnected,
         * we signal error. Messages are lost. Do not make this,
         * when peer was not connected to us.
         */
        if (!sock_flag(other, SOCK_DEAD) && unix_peer(other) == sk) {
            other->sk_err = ECONNRESET;
            other->sk_error_report(other);
        }
    }
}


static int unix_release_sock (struct sock *sk, int embrion)
{
[...]
    skpair=unix_peer(sk);
    if (skpair!=NULL) {
        [...]
        if (!skb_queue_empty(&sk->sk_receive_queue) || embrion)
            skpair->sk_err = ECONNRESET;  
        [...]
    }
}

úgytűnik ezzel van összefüggésben:

skb_queue_empty(&sk->sk_receive_queue)

Aha, de ez nem datagram socket-ekre vonatkozik? Az, hogy az address family az unix, a socket maga me'g stream. Igen, es ennek tobb ertelme is van, marmint hogy egy datagram-nal ha a masik fe'l bezarja maga't (es persze af_unix, tehat a kernel kvazi tisztaban van ezzel), akkor nem tehet mast, mint econnreset-et dob. Stream socket-eknel (meg ugy enbloc, file-oknal, pipe-oknal, ...) meg az kell legyen a normalis, legjobb tudomasom szerint, hogyha a masik fe'l zar (ami egy szabalyos dolog, szoval szabalyosan za'r), akkor az egyik fe'l 0 hosszusagu szabalyos adatot olvas ki (tehat nem connreset). Plusz persze select()/poll() is visszate'r, ha az fd a read set-ben van. Ha erre persze me'g 1x raolvasna valaki, akkor mar lehet hibaval is visszajonni, de eloszor nem.

Az erintett kodreszlet ez, ahol a szerver socke-tet megcsinalja a cucc:


        [...]
        sock=socket(PF_UNIX,SOCK_STREAM,0);

        if ( sock<0 )
                return(-1);

        unaddr.sun_family=AF_UNIX;
        strncpy(unaddr.sun_path,ctrlport,UNIX_PATH_MAX-1);
        unaddr.sun_path[UNIX_PATH_MAX]=0;

        if ( ! stat(unaddr.sun_path,&st) && S_ISSOCK(st.st_mode) )
                unlink(unaddr.sun_path);

        ret=bind(sock,(struct sockaddr *)(&unaddr),(socklen_t)(sizeof(unaddr)));
        if ( ret<0 )
               [...]
        [...]

A.

Az első kód az nyilván datagram-os socket, de a unix_release_sock szerintem dgram-os és stream-es is: (de az is lehet, hogy egész rossz helyen keresgélek ... :) )


skpair=unix_peer(sk);

	if (skpair!=NULL) {
		if (sk->sk_type == SOCK_STREAM || sk->sk_type == SOCK_SEQPACKET) {
			unix_state_lock(skpair);
			/* No more writes */
			skpair->sk_shutdown = SHUTDOWN_MASK;
			if (!skb_queue_empty(&sk->sk_receive_queue) || embrion)
				skpair->sk_err = ECONNRESET;
			unix_state_unlock(skpair);
			skpair->sk_state_change(skpair);
			read_lock(&skpair->sk_callback_lock);
			sk_wake_async(skpair, SOCK_WAKE_WAITD, POLL_HUP);
			read_unlock(&skpair->sk_callback_lock);
		}
		sock_put(skpair); /* It may now die */
		unix_peer(sk) = NULL;
	}