[C#] vlákna & tcp

C++, C#, Visual Basic, Delphi, Perl a ostatní

Moderátor: Moderátoři Živě.cz

Odeslat příspěvekod BEZY 15. 3. 2011 13:08

Zdravím,

dělám si vlastní dvojici aplikací Klient - Server. Momentálně se zabývám Klientem (realizován jako vlastní třída) - jeho úkolem je připojit se k serveru a dle příkazů uživatele s ním komunikovat. Zároveň ale musím být schopen příjimat náhodně (pseudo) zprávy ze strany serveru.

Abych tohoto docílil, přímo v klientu si vytvořím druhé vlákno - jeho úkolem je právě čekat na náhodné zprávy ze strany serveru a zpracovat je.

Klienta používám ve Form aplikaci - už. klikne a Klient pošle zprávu serveru a čeká na odpověď.

A tady nastává můj problém (či neznalost) - jelikož už mi na zprávy od serveru čeká druhé vlákno, nevím jak docílit toho, aby odpověď na právě zaslaný dotaz přišlo stávajícímu vkláknu (a ne tomu druhému) - abych mohl na odpověď zareagovat a vrátit se z metody.

Napadlo mě to udělat pomocí Mutexů - viz ukázka:

Toto je kód druhého vlákna
Kód: Vybrat vše
            tcpReader.BaseStream.ReadTimeout = 500;
            while (! stopListening)
            {
                readMutex.WaitOne();
                try
                {
                    string msg = tcpReader.ReadLine();
                    TcpMessage tmsg = new TcpMessage(msg);
                }
                catch
                {
                    if (!tcpClient.Connected)
                    {
                        stopListening = true;
                        MessageBox.Show("Not connected anymore");
                    }
                }
                readMutex.ReleaseMutex();
            }


toto je kód volané metody, která musí být sycnhroní (vrací výsledek volání)
Kód: Vybrat vše
private TcpMessage SendMessage(TcpMessage message)
{
            readMutex.WaitOne(); // wait until doListening stops listening           
            tcpWriter.WriteLine(message.ToString());
            TcpMessage res = new TcpMessage(tcpReader.ReadLine());
            readMutex.ReleaseMutex();
            return res;
}


(TcpMessage je moje vlastní třída, zde nemá význam)

Můj nápad byl takový - v druhém vlákně nastavím timeout na půl sekundy a budu volat stále dokola ReadLine(). Pokud se zavolá metoda SendMessage(), spustí se Mutex, ten počká dokud ho druhé vlákno neuvolní (max. půl sekundy), vstoupí se do chráněného kódu (druhé vlákno se ve while smyčce zastaví na Mutexu), zde se pošlou a přijmou data, uvolní se Mutex a vrátí se výsledek - tím je zajištěno, že je tato metoda synchroní.

Problém je v tom, že při TimeOutu v druhém vlákně dojde k odpojení klienta - na čemž moje řešní padá.

Nevíte jak toto ošetřit (aby při timeoutu nedošlo k odpojení od serveru)? Případně úplně jiný způsob, jak vyřešit asycnhronní příjem zpráv od serveru kombinovaný se synchroními voláními?
BEZY
Junior
Uživatelský avatar

Odeslat příspěvekod Nargon 15. 3. 2011 16:11

Tohle asi nebude moc fungovat ani kdyz to nejak upravis. Cist odpovedi z jednoho tcp socketu ve vice vlaknech neni dobre. A tohle tvoje obchazeni taky moc nefunguje. Chce to cist ty data jen v jednom vlakne. Tj asi cist jen v tom vlakne, ktere ceka na ty nevyzadane zpravy. A kdyz prijde "ocekavana" odpoved tak ji predat tomu prvnimu vlaknu.
Tj cca tohle:
Kód: Vybrat vše
// v te tride u SendMessage:
        private TcpMessage odpoved;
        private AutoResetEvent odpovedEvent = new AutoResetEvent(false);
        private TcpMessage Odpoved //ten lock mozna neni nutny, protoze neocekavam ze by
        { //prisla dalsi vyzadana odpoved dokud ta metoda SendMessage nedobehne.
            get { odpovedEvent.WaitOne(); lock (odpovedEvent) { return odpoved; } }
            set { lock (odpovedEvent) { odpoved = value; } odpovedEvent.Set(); }
        }
        private TcpMessage SendMessage(TcpMessage message)
        {
            tcpWriter.WriteLine(message.ToString());
            TcpMessage res = Odpoved;
            return res;
        }
...

//A v tom druhem vlaknu:
            while (! stopListening)
            {
                try
                {
                    string msg = tcpReader.ReadLine();
                    TcpMessage tmsg = new TcpMessage(msg);
                    if (tmsg.JeOcekavanaOdpoved) //Tohle mozna bude trochu problem, jak rozlisit prichozi zpravy
                    { //ktere jsou odpovedi na tvoje odeslane zpravy, a ty ktere prijdou nahodne. Ale to snad nejak zvladnes.
                        Odpoved = tmsg;
                    }
                }
                catch
                {
                    if (!tcpClient.Connected)
                    {
                        stopListening = true;
                        MessageBox.Show("Not connected anymore");
                    }
                }
            }

Misto mutexu je tam pouzit AutoResetEvent. Mutex by asi taky sel pouzit, ale nejsem si jisty jak funguje a stejne je pro tohle pouziti az moc "slozity". Pri tehle implementaci ten "setOdpoved" uzamkne kritickou sekci, zapise tam data, odemkne. A pak oznami ze data prisla. Ta "getOdpoved" ceka na oznameni dokud tam data nebudou. A jak oznameni prijde, tak uzamkne kritickou sekci, precte data, odemkne kritickou sekci.
Desktop: Ryzen 7 1800X (3.95GHz, 1.35V), Asus Crosshair VI Hero, 16GB DDR4 Ram (3200MHz), 128GB SSD + 3TB HDD, Nvidia GTX 1080
Notebook: Asus UL50VT 15.6" (SU7300@1.7GHz, 4GB ram, 500GB HDD, Intel GMA 4500MHD + nVidia G210M, dlouha vydrz cca 7+ hod)
Nargon
Moderátor

Odeslat příspěvekod steelspace 15. 3. 2011 16:27

A nebylo by lepší použít třeba WCF s TCP přenosem?
steelspace
Junior

Odeslat příspěvekod BEZY 16. 3. 2011 13:15

Nargon> dík, vyzkouším, vypadá to použitelně ;). To která odpověď je vyžádaná a která ne rozlišim jednoduše, každá zpráva má svoji strukturu

steelspace> popravdě, C# se právě začínám učit, takže o WCF ani nemám tušení... Zatím si dělám jen Formsy, pak si chci vyzkoušet WPF, takže toho je fakt hodně :D
BEZY
Junior
Uživatelský avatar


Kdo je online

Uživatelé procházející toto fórum: Žádní registrovaní uživatelé a 0 návštevníků