Programování v C# Síťová komunikace Petr Vaněček 1 / 33
Obsah přednášky Jednoduchá komunikace Sockety 2 / 33
System.Net Namespace poskytuje jednoduché rozhraní ke standardním protokolům Třída WebClient příjmání a odesílání dat pro různé protokoly http, https, ftp, file směrování pomocí URI (Uniform Resource Identifier) Třídy *WebRequest třídy pro vznesení požadavku Třídy *WebResponse třídy pro získání odpovědi Třída HttpListener jednoduchý posluchač HTTP protokolu 3 / 33
System.Net Další podpůrné třídy pro práci se sítí Třída Cookies podpora pro práci s cookies Třída DNS práce s Domain Name Systémem Třída *EndPoint umožňuje popsat cílový bod síťové komunikace 4 / 33
Třída WebRequest Abstraktní třída HttpWebRequest (http://, https://) FtpWebRequest (ftp://) FileWebRequest (file:// souborový sytém) Pro vytvoření instance WebRequest Create(URI) typ, který se vrátí určuje URI Při chybě se vyvolá chyba WebException Výsledek se získá pomocí odezvy WebResponse GetResponse() Možnost asynchronního přenosu metody BeginGetResponse a EndGetResponse 5 / 33
Třída WebResponse Abstraktní třída HttpWebResponse FtpWebResponse FileWebResponse Pro vytvoření instance WebRequest.GetResponse() Výsledek čitelný pomocí proudu Stream GetResponseStream() 6 / 33
Synchronní komunikace WebRequest pozadavek = WebRequest. Create (" http :// home.pf.jcu.cz /~ vanecek /"); HttpWebResponse odpoved = ( HttpWebResponse ) pozadavek. GetResponse (); Encoding encode = System. Text. Encoding. GetEncoding (" windows -1250 "); Stream proud = odpoved. GetResponseStream (); StreamReader ctenar = new StreamReader ( proud, encode ); string stranka = ctenar. ReadToEnd (); Console. Write ( stranka ); odpoved. Close (); ctenar. Close (); 7 / 33
Třída DNS Statická třída pro práci s Internet Domain Name System Metody IPHostEntry GetHostByName(string) IPHostEntry GetHostByIP(IPAdress) IPHostEntry Resolve(string) vrací DNS informace string GetHostName() vrací jméno lokálního počítače Příklad IPHostEntry hostinfo = Dns. GetHostByName (" home.pf.jcu.cz"); Console. WriteLine (" Host name : " + hostinfo. HostName ); Console. WriteLine ("IP address List : " + hostinfo. AddressList [0]); 8 / 33
Namespace System.Net.Socket Umožňuje vyšší kontrolu kontrolu síťového přístupu Přenos dat jedním z protokolů ProtocolType IP, IPv6, TCP, UDP,... Synchronní i asynchronní komunikace Odlehčené verze TcpClient, TcpListener, UdpClient, IrDAClient, IrDAListener jen synchronní verze (blokující) jednodušší interface 9 / 33
Třída Socket Socket (AddressFamily, SocketType, ProtocolType) AddressFamily adresovací schéma InterNetwork, InterNetworkV6, IrDa SocketType typ socketu Stream, Dgram, Raw,... ProtocolType viz. předchozí slide Musí být správná kombinace adresy, typu a protokolu SocketException 10 / 33
Typy socketů Datagram krátké zprávy pevné délky nepotřebují navázané spojení při posílání není zajištěna bezpečnost protokol UDP 11 / 33
Typy socketů Datagram krátké zprávy pevné délky nepotřebují navázané spojení při posílání není zajištěna bezpečnost protokol UDP Stream navazované obousměrné spojení před odesláním musí být spojení navázané protokol TCP 11 / 33
Typy socketů Datagram krátké zprávy pevné délky nepotřebují navázané spojení při posílání není zajištěna bezpečnost protokol UDP Stream navazované obousměrné spojení před odesláním musí být spojení navázané protokol TCP Raw nutno doplnit IP hlavičku protkoly ICMP, IGMP 11 / 33
Způsob komunikace Synchronní obsluha jedním vláknem blokující Send, Receive, Accept,... Asynchronní obsluha v odděleném vlákně IAsyncResult Begin...(..., AsyncCallback, object) delegát pro obsluhu + uživatelský parametr...end...(iasyncreslut) blokující 12 / 33
Navázání komunikace void Listen(int) neblokující pouze pro spojově orientované protokoly nastaví socket do naslouchacího režimu parametr určuje délku fronty požadavků o spojení void Bind(EndPoint) před voláním Listen se musí volat propojí socket s lokálním koncovým bodem spojení 13 / 33
Navázání komunikace Socket Accept() přijme požadavek o připojení z fronty blokující socket čeká na požadavek v případě prázdné fronty neblokující socket vyvolá výjimku SocketException v případě prázdné fronty void Connect(EndPoint) vytvoří spojení se vzdáleným koncem EndPoint metoda se musí volat pro spojované protokoly 14 / 33
Příklad navázání komunikace server Socket s = new Socket ( AddressFamily. InterNetwork, SocketType. Stream, ProtocolType. IP ); IPEndPoint local = new IPEndPoint ( IPAddress. Parse (" 127.0.0.1 "), 6666); // bude se poslouchat na portu 6666 s. Bind ( local ); // délka fronty je 100 s. Listen (100); // čeká dokud se někdo nepřipojí Socket connection = s. Accept (); 15 / 33
Příklad navázání komunikace klient Socket s = new Socket ( AddressFamily. InterNetwork, SocketType. Stream, ProtocolType. IP ); IPEndPoint local = new IPEndPoint ( IPAddress. Parse (" 127.0.0.1 "), 6666); s. Connect ( local ); 16 / 33
Posílání dat int Send(byte[]) pošle data připojenému socketu vrací počet odeslaných bytů pokud se používá nespojovaný protokol, pak každému volání musí předcházet volání Connect pro spojované protokoly stačí volat Connect nebo Accept jen jednou pro každou relaci int SendTo(byte[], EndPoint) zaměřeno pro nespojované protokoly nemusí se volat Connect není třeba volat Bind použije se nejvhodnější nastavení lze použít i pro spojované protokoly musí se volat Connect EndPoint se ignoruje 17 / 33
Příjímání dat int Recieve(byte[]) přijme data od připojeného socketu funguje pro oba typy protokolů při použití spojovaného protokolu musí být před voláním navázáno spojení Connect nebo Accept při použití nespojovaného protokolu se přečte datagram poslaný ze zdroje uvedeného v Connect blokující soket čeká na data, neblokující vyhodí SocketException property Available obsahuje počet dostupných bytů vrací počet přečtených bytů 18 / 33
Příjímání dat int RecieveFrom(byte[], ref EndPoint) přijme data a zdroj dat uloží do EndPoint je zamýšlena pro nespojované protokoly metodou Connect lze vytvořit implicitního vzdáleného hostitele a data pak budou přijímána pouze od něj lze použít i pro spojované protokoly nutno vytvořit spojení metodou Connect nebo Accept vrací počet přečtených bytů blokující soket čeká na data, neblokující vyhodí SocketException property Available obsahuje počet dostupných bytů pokud buffer nestačí - SocketException 19 / 33
Posílání/příjímání dat klient byte [] msg = Encoding. ASCII. GetBytes (" Posilam ti zpravu "); Console. WriteLine ( Encoding. ASCII. GetString ( msg )); int i = s. Send ( msg ); serever byte [] msg = new byte [100]; connection. Receive ( msg ); Console. WriteLine ( Encoding. ASCII. GetString ( msg )); 20 / 33
Ukončení spojení void Shutdown(SocketShutdown) ukončení komunikace (pouze send, pouze recieve, obojí) void Close() uvolnění systémových prostředků 21 / 33
Asynchronní komunikace Metody Begin... a End... Volá se delegát, který je automaticky spuštěn v jiném vlákně Příklad... { Socket s = new Socket ( AddressFamily. InterNetwork, SocketType. Stream, ProtocolType. IP ); s. Bind ( local ); s. Listen (100); Socket connection = s. Accept (); } msg = new byte [100]; connection. BeginReceive (msg,0,100, SocketFlags.None, new AsyncCallback ( Prijem ), connection );... 22 / 33
Asynchronní komunikace Příklad (pokračování) static void Prijem ( IAsyncResult ar) { Socket socket = ar. AsyncState as Socket ; int bytesread = socket. EndReceive ( ar ); Console. WriteLine ( Encoding. ASCII. GetString ( msg )); } 23 / 33
Použití streamu Slouží pro posílání dat sockety typu stream v blokujícím režimu Podporuje int Read(byte[], int, int) void Write(byte[], int, int) Nepodporuje náhodný přístup k datům property CanSeek má hodnotu false K fungování vyžaduje spojený socket NetworkStream ns = new NetworkStream(Socket); Je potomkem Stream lze aplikovat třídy Stream/Binnary/Reader/Writer 24 / 33
Použití streamu klient s. Connect ( local ); NetworkStream ns = new NetworkStream (s, true ); StreamWriter sw = new StreamWriter ( ns ); while (c!= ( char )13) { c = Console. ReadKey (). KeyChar ; sw. Write (c); sw. Flush (); } sw. Close (); server Socket connection = s. Accept (); NetworkStream ns = new NetworkStream ( connection ); StreamReader reader = new StreamReader ( ns, true ); while (c!= ( char )13) { c = ( char ) reader. Read (); Console. WriteLine (c); } 25 / 33
Socket TCP Třída TCPClient obsahuje jednoduché metody pro posílání a příjímání pomocí protokolu TCP void Connect(IPEndPoint) spojení může být navázáno i v konstruktoru NetworkStream GetStream() získá stream určený pro příjmání i posílání dat musí být navázané spojení metody Write i Read jsou blokující blokující Read lze obejít testováním DataAvailable void Close() uzavře sopjení a uvolní zdroje 26 / 33
Socket TCP Třída TcpListenner nastavení lokálního koncového bodu se provádí již při konstrukci void Start() začne naslouchat void Stop() končí naslouchání bool Pending() vrátí zda se někdo pokouší o spojení TcpClient AcceptTcpClient() naváže spojení 27 / 33
Socket TCP server IPEndPoint local = new IPEndPoint ( IPAddress. Parse (" 127.0.0.1 "), 6666); TcpListener tcplistener = new TcpListener ( local ); tcplistener. Start (); while (! tcplistener. Pending ()) ; TcpClient client = tcplistener. AcceptTcpClient (); NetworkStream s = client. GetStream (); StreamReader r = new StreamReader ( s); StreamWriter w = new StreamWriter ( s); w. Write (c); c=r. Read (); client. Close (); tcplistener. Stop (); 28 / 33
Socket TCP klient IPEndPoint local = new IPEndPoint ( IPAddress. Parse (" 127.0.0.1 "), 6666); TcpClient client = new TcpClient (); client. Connect ( local ); NetworkStream s = client. GetStream (); StreamReader r = new StreamReader ( s); StreamWriter w = new StreamWriter ( s); w. Write (c); c=r. Read (); client. Close (); 29 / 33
Práce s IrDA zařízeními Třída IrDAClient podobně jako TCPClient umožňuje zjistit jméno vzdáleného zařízení RemoteMachineName Třída IrDAListener podobně jako TCPListener Třída IrDADeviceInfo informace o dostupných zařízeních jméno id znaková sada typ počítač, tiskárna, telefon,... 30 / 33
UDP protokol Třída UDPClient nespojovaný protokol metody Connect, Send a Receive navíc možnost používat multicast metoda void JoinMulticastGroup(IPAddress) přidá klienta k multicastové skupině metoda void DropMulticastGroup(IPAddress) opustí multicast skupinu 31 / 33
UDP protokol posluchač UdpClient client = new UdpClient (6666); client. JoinMulticastGroup ( IPAddress. Parse (" 127.0.0.1 " ),2); IPEndPoint ep = null ; byte [] buffer = client. Receive ( ref ep ); Console. WriteLine ( Encoding. ASCII. GetString ( buffer )); odesílatel IPEndPoint local = new IPEndPoint ( IPAddress. Parse (" 127.0.0.1 "), 6666); UdpClient client = new UdpClient (); client. Send ( Encoding. ASCII. GetBytes (" ahoj "),4, local ); 32 / 33
Konec 33 / 33