Справочник по C# Герберт Шилдт ббк 32. 973. 26018 75 Ш57 удк 681 07 Издательский дом "Вильямс" Зав редакцией
Скачать 5.05 Mb.
|
Osborne McGraw-Hill/TITLE> 652 Часть II. Библиотека C# learning tools at Osborne McGraw-Hill"> it books, computer books, database books, programming Нажмите клавишу. books, networking books, certification books, computing books, computer application books, hardware books, information technology books, operating systems, web development, oracle press, communications, complete reference, how to do everything, yellow pages, book publisher, certification study guide, reference book, security, network security, ebusiness, e-business, a+, network+, i-net+, cisco ce Нажмите клавишу. Поскольку программа лишь посимвольно отображает принятое содержимое, она, в отличие от браузера, не форматирует его. Каждая строка этой программы заслуживает рассмотрения. Прежде всего обратите внимание на использование пространства имен System.Net . Как упоминалось выше, именно это пространство имен содержит классы, обеспечивающие сетевые возможности C#. Здесь также используется пространство имен System.IO . Его присутствие обусловлено использованием объекта класса stream , который позволяет считывать информацию с Web-сайта. Программа начинается с создания WebRequest -объекта, который содержит нужный URI-адрес. Обратите внимание на то, что для этого используется метод Create() , а не конструктор. Метод Create() — статический член класса WebRequest . Несмотря на то что класс WebRequest — абстрактный, мы имеем возможность вызывать его статические методы. Метод Create() возвращает WebRequest -объект, который содержит протокол, “настраиваемый” на основе префикса URI-адреса. В данном случае используется протокол HTTP. Поэтому метод Create() возвращает HttpWebRequest -объект. Если значение, возвращаемое методом Create() , присваивается HttpWebRequest -ссылке с именем req , то оно обязательно должно быть приведено (в явной форме) к типу HttpWebRequest . После выполнения этой инструкции наш запрос создан, но еще не отправлен по заданному URI-адресу. Чтобы отослать запрос, программа вызывает метод GetResponse() для объекта класса WebRequest . После отправки запроса метод GetResponse() ожидает ответа. По получении ответа метод GetResponse() возвращает WebResponse -объект, который инкапсулирует принятый ответ. Этот объект присваивается ссылке resp . Так как в данном случае в ответе используется протокол HTTP, результат вызова метода GetResponse() приводится к типу HttpWebResponse . Ответ содержит поток, который можно использовать для считывания данных с Web-сайта с заданным URI-адресом. Затем посредством вызова метода GetResponseStream() для объекта resp считывается входной поток, который представляет собой стандартный объект класса Stream , имеющий все атрибуты любого другого входного потока. Ссылка на этот поток присваивается переменной istrm . Используя переменную istrm , данные, расположенные по заданному URI-адресу, можно прочитать так же, как считываются данные из файла. Считанные данные программа отображает на экране. Поскольку объем данных может быть довольно большим, процесс отображения приостанавливается после вывода каждых 400 символов, и программа переходит в режим ожидания, который прервется Глава 23. Сетевые возможности и использование Internet 653 нажатием клавиши ReadByte() . Вспомните, что этот метод возвращает следующий символ из входного потока в виде int -значения, которое должно быть приведено к типу char . При достижении конца потока метод возвращает число — 1. Наконец, поток, содержащий ответ, закрывается посредством вызова метода Close() для объекта resp . При закрытии потока с ответом автоматически закрывается и входной поток. Важно закрывать поток, содержащий ответ, между запросами. В противном случае могут исчерпаться сетевые ресурсы, и следующее подключение к Internet может не состояться. Здесь важно отметить еще одну деталь. Для отображения гипертекстовой информации с Web-сайта Osborne.com необязательно использовать объект класса HttpWebRequest или HttpWebResponse . Поскольку в предыдущей программе не использовались никакие специфические HTTP-средства, для решения этой задачи было бы вполне достаточно стандартных методов, определенных в классах WebRequest и WebResponse . Поэтому вызовы методов Create() и GetResponse() можно переписать так: // Сначала создаем WebRequest-запрос по URI-адресу. WebRequest req = WebRequest.Create( "http://www.osborne.com"); // Затем отправляем запрос и получаем ответ. WebResponse resp = req.GetResponse(); Разработчики из компании Microsoft считают, что в случаях, когда необязательно использовать операцию приведения к типу реализации конкретного протокола, лучше обходиться средствами, предоставляемыми классами WebRequest и WebResponse . Это позволяет изменять протоколы, не внося изменений в программы. Но поскольку во всех примерах этой главы используется протокол HTTP (а в некоторых из них и специфические HTTP-средства), то в приведенных здесь программах задействованы классы HttpWebRequest И HttpWebResponse Обработка сетевых ошибок Несмотря на то что программа, приведенная в предыдущем разделе, вполне корректна, она совершенно беззащитна перед потенциальными “ударами судьбы”, которые могут внезапно оборвать ее “жизнь”. В реальных приложениях необходимо предусмотреть все возможные неприятности и обеспечить полную обработку сетевых исключений, которые может сгенерировать программа. Для этого нужно проконтролировать обращения к методам Create() , GetResponse() и GetResponseStream() . Все типы потенциальных ошибок рассматриваются в следующих разделах. Исключения, генерируемые методом Create() Метод Create() , определенный в классе WebRequest , может сгенерировать три исключения. Если протокол, заданный URI-префиксом, не поддерживается, генерируется исключение типа NotSupportedException . Если неверно задан формат URI- идентификатора, генерируется исключение типа UriFormatException . Метод Create() , вызванный с использованием нулевой ссылки, может сгенерировать также исключение типа ArgumentNullException , хотя эта ошибка не относится к тем, которые генерируются сетевыми средствами. 654 Часть II. Библиотека C# Исключения, генерируемые методом GetResponse() Если ошибка обнаружится в процессе получения ответа, т.е. при вызове метода GetResponse() , генерируется исключение типа WebException . Помимо членов, определенных для всех исключений, в классе WebException определены также два дополнительных свойства, которые связаны с сетевыми ошибками: Response и Status Внутри обработчика исключения с помощью свойства Response можно получить ссылку на объект класса WebResponse . Этот объект будет возвращен методом GetResponse() , если исключение не генерируется. Определение свойства Response выглядит так: public WebResponse Response { get; } Если ошибка все-таки возникла, то чтобы понять, что именно произошло, можно использовать свойство status . Его определение имеет следующий вид: public WebExceptionStatus Status { get; } WebExceptionStatus — это перечисление, которое содержит следующие значения: ConnectFailure ConnectionClosed KeepAliveFailure NameResolutionFailure Pending PipelineFailure ProtocolError ProxyNameResolutionFailure ReceiveFailure RequestCanceled SecureChannelFailure SendFailure ServerProtocolViolation Success Timeout TrustFailure После выяснения причины ошибки программа может предпринять соответствующие действия. Исключения, генерируемые методом GetResponseStream() Метод GetResponseStream() класса GetResponse может генерировать исключение типа ProtocolViolationException , которое в общем случае означает, что произошла ошибка, связанная с протоколом. А поскольку ее возникновение имеет отношение к методу GetResponseStream() , значит нет ни одного потока, содержащего ответную информацию. В процессе чтения потока может также быть сгенерировано исключение типа IOException Обработка исключений В следующую программу, которая основана на предыдущем примере, добавлены обработчики всех возможных сетевых исключений: // Обработка сетевых исключений. using System; using System.Net; using System.IO; class NetExcDemo { public static void Main() { int ch; try { Глава 23. Сетевые возможности и использование Internet 655 // Сначала создаем WebRequest-запрос по URI-адресу. HttpWebRequest req = (HttpWebRequest) WebRequest.Create("http://www.osborne.com"); // Затем отправляем запрос и получаем ответ. HttpWebResponse resp = (HttpWebResponse) req.GetResponse(); //Из ответа получаем входной поток. Stream istrm = resp.GetResponseStream(); /* А теперь считываем и отображаем html-документ, полученный от заданного URI. Текст "не улетит" с экрана, поскольку данные отображаются порциями объемом в 400 символов. Просмотрев очередные 400 символов, нажмите клавишу = istrm.ReadByte(); if(ch == -1) break; Console.Write((char) ch); if((i%400)==0) { Console.Write("\nНажмите клавишу."); Console.Read(); } } // Закрываем поток, содержащий ответ. При этом // автоматически закроется и входной поток istrm. resp.Close(); } catch(WebException exc) { Console.WriteLine("Сетевая ошибка: " + exc.Message + "\nКод состояния: " + exc.Status); } catch(ProtocolViolationException exc) { Console.WriteLine("Ошибка протокола: " + exc.Message); } catch(UriFormatException exc) { Console.WriteLine("Ошибка формата URI: " + exc.Message); } catch(NotSupportedException exc) { Console.WriteLine("Неизвестный протокол: " + exc.Message); } catch(IOException exc) { Console.WriteLine("I/O Error: " + exc.Message); } } } В этом варианте программы будут перехвачены исключения, потенциально генерируемые Internet-методами. Например, если используемое в программе обращение к методу Create() заменить следующим WebRequest.Create("http://www.osborne.com/moonrocket"); и перекомпилировать программу, а затем ее выполнить, вы непременно получите такое сообщение: Сетевая ошибка: The remote server returned an error: (404) Not Found. Код состояния: ProtocolError 656 Часть II. Библиотека C# Поскольку Web-сайт Osborne.com не имеет каталога с именем “moonrocket”, вполне естественно, что этот URI-адрес не обнаружен. Чтобы не загромождать примеры, большинство программ из этой главы не содержит полной обработки исключений. Но вы должны понимать, что в реальные приложения ее необходимо включать в полном объеме. Класс URI Возможно, вы обратили внимание на то, что в табл. 23.1 метод WebRequest.Create() представлен в двух различных версиях. Одна из них принимает URI в виде строки, и именно эта версия используется в предыдущих программах. Вторая принимает URI как экземпляр класса Uri . Класс Uri инкапсулирует URI-идентификатор. Используя класс Uri , можно создать такой URI-адрес, который будет принят второй версией метода Create() . При этом можно разбить URI-идентификатор на части. И хотя в случае простых Internet-операций можно обойтись без класса Uri , все же в более сложных случаях он окажется ценным подспорьем. В классе Uri определено несколько конструкторов. Два наиболее употребимые из них определяются так: public Uri(string uri ) public Uri(string base , string rel ) Первая форма позволяет создать Uri -объект на основе URI-адреса, заданного в виде строки. Вторая служит для создания Uri -объекта путем сложения относительного URI- идентификатора, заданного параметром rel , с базовым URI-идентификатором, заданным параметром base . Базовый URI-идентификатор определяет сам URI-адрес, а относительный — только сетевой маршрут. В классе Uri определено множество полей, свойств и методов, которые позволяют управлять URI-идентификаторами или предоставляют доступ к различным его частям. Чаще всего используются следующие свойства. Свойство Описание public string Host { get; } Получает имя сервера public string LocalPath { get; } Получает маршрут, определяющий местоположение файла public string PathAndQuery { get; } Получает маршрут и строку запроса public int Port { get; } Получает номер порта для заданного протокола Для HTTP номер порта равен 30 public string Query { get; } Получает строку запроса public string Scheme { get; } Получает протокол Эти свойства полезно применять при разбиении URI-идентификатора на составные части. Их использование демонстрирует следующая программа: // Использование класса Uri. using System; using System.Net; Глава 23. Сетевые возможности и использование Internet 657 class UriDemo { public static void Main() { Uri sample = new Uri( "http://MySite.com/soraefile.txt?SomeQuery"); Console.WriteLine("Хост: " + sample.Host); Console.WriteLine("Порт: " + sample.Port); Console.WriteLine("Протокол: " + sample.Scheme); Console.WriteLine("Локальный маршрут: " + sample.LocalPath); Console.WriteLine("Запрос: " + sample.Query); Console.WriteLine("Маршрут и запрос: " + sample.PathAndQuery); } } Вот результаты выполнения программы: Хост: mysite.com Порт: 80 Протокол: http Локальный маршрут: /somefile.txt Запрос: ?SomeQuery Маршрут и запрос: /somefile.txt?SomeQuery Доступ к дополнительной HTTP-информации Используя класс HttpWebResponse , можно получить доступ не только к содержимому заданного ресурса, но и к информации, которая включает, например, время последней URI-модификации и имя сервера. Эту информацию можно получить с помощью различных свойств, перечисленных в табл. 23.5 (четыре из них определены в классе WebResponse ). Об их использовании и пойдет речь в следующих разделах. Таблица 23.5. Свойства, определенные в классе HttpWebResponse Свойство Описание Public string CharacterSet { get; } Получает название используемого символьного набора Public string ContentEncoding { get; } Получает название схемы кодирования. Public long ContentLength { get; } Получает длину принимаемого содержимого. Если она недоступна, свойство содержит -1 Public string ContentType { get; } Получает описание содержимого Public CookieCollection Cookies { get; set; } Получает или устанавливает список cookie-данных, присоединенных к ответу Public WebHeaderCollection Headers{ get; } Получает коллекцию заголовков, присоединенных к ответу Public DateTime LastModified { get; } Получает время последней URI-модификации Public string Method { get; } Получает строку, которая задает способ ответа 658 Часть II. Библиотека C# Окончание табл. 23.5 Свойство Описание public Version Получает объект класса Version , который описывает версию ProtocolVersion { get; } протокола HTTP, используемую в транзакции public uri ReponseUri { get; } Получает URI-идентификатор, по которому сгенерирован ответ. Он может отличаться от запрошенного, если ответ был перенаправлен по другому URI-адресу public string Server { get; } Получает строку, которая представляет имя сервера public HttpStatusCode StatusCode { get; } Получает объект класса HttpStatusCode , который описывает состояние транзакции public string StatusDescription { get; } Получает строку, которая представляет состояние транзакции в форме, удобной для восприятия человеком Доступ к заголовку С помощью свойства Headers, которое определено в классе HttpWebResponse , можно получить доступ к заголовочной информации HTTP-ответа: public WebHeaderCollection Headers{ get; } HTTP-заголовок состоит из пар имя/значение, представленных в виде строк. Каждая такая пара хранится в объекте класса WebHeaderCollection . Это — специализированная коллекция, предназначенная для хранения пар ключ/значение, которую можно использовать подобно любой другой коллекции (см. главу 22). Строковый массив имен можно получить из свойства AllKeys . Значение, связанное с конкретным именем, можно получить с помощью индексатора. Индексатор здесь перегружен на прием либо числового индекса, либо имени. В следующей программе отображаются все заголовки, относящиеся к Web-сайту Osborne.com : // Отображение заголовков Web-сайта. using System; using System.Net; class HeaderDemo { public static void Main() { // Создаем WebRequest-запрос по URI-адресу. HttpWebRequest req = (HttpWebRequest) WebRequest.Create("http://www.osborne.com"); // Отправляем этот запрос и получаем ответ. HttpWebResponse resp = (HttpWebResponse) req.GetResponse(); // Получаем список имен. string[] names = resp.Headers.AllKeys; // Отображаем заголовок в виде пар имя/значение. Console.WriteLine("{0,-20}{1}\n", "Имя", "Значение"); foreach(string n in names) Console.WriteLine("{0,-20}{1]", n, resp.Headers[n]); // Закрываем поток, содержащий ответ. Глава 23. Сетевые возможности и использование Internet 659 resp.Close(); } } Вот какие были получены результаты (не забывайте, что заголовочная информация Web-сайта Osbome.com со временем может измениться, поэтому результаты, полуденные вами, могут несколько отличаться): Имя Значение Date Mon, 14 Jan 2002 17:45:50 GMT Server Apache/1.3.9 (Unix) PHP/3.0.14 Keep-Alive timeout=30, max=500 Connection Keep-Alive Transfer-Encoding chunked Content-Type text/html Доступ к cookie-данным Доступ к cookie-данным, связанным с HTTP-ответом, можно получить с помощью свойства Cookies , которое определено в классе HttpWebResponse . Cookie-данные, которые хранятся браузером, состоят из пар имя/значение. Они способны упростить определенные типы операций Web-доступа. Вот как выглядит определение свойства Cookies : public CookieCollection Cookies { get; set; } Класс CookieCollection реализует интерфейсы ICollection и IEnumerable , и его можно использовать подобно любой другой коллекции (см. главу 22). Он имеет индексатор, который позволяет получить cookie-данные по заданному индексу или его имени. Коллекция типа CookieCollection предназначена для хранения объектов типа Cookie . В классе Cookie определено несколько свойств, которые предоставляют доступ к различным частям cookie-данных. Нас интересуют свойства Name и Value : public string Name { get; set; } public string Value { get; set; } Нетрудно догадаться, что cookie-имя содержится в свойстве Name , а значение — в свойстве Value Чтобы получить список cookie-составляющих, связанных с ответом, необходимо воспользоваться cookie-контейнером. Для этого в классе HttpWebRequest определено свойство CookieContainer: public CookieContainer CookieContainer { get; set; } В классе CookieContainer определены различные поля, свойства и методы, которые позволяют хранить cookie-данные. Однако при создании многих приложений нет необходимости в непосредственном использовании свойства CookieContainer . Обычно используют коллекцию типа CookieCollection , получаемую из ответа. Назначение класса CookieContainer — обеспечить механизм хранения cookie-данных. Следующая программа отображает имена и значения cookie-разделов, связанных с URI-идентификатором, заданным в командной строке. При этом важно помнить, что не все Web-сайты используют cookie-данные, поэтому не стоит удивляться, если вам попадется именно такой. /* Отображение cookie-данных. Чтобы узнать, какие cookie-данные использует интересующий вас Web-сайт, укажите его имя 660 Часть II. Библиотека C# в командной строке. Например, если эту программу назвать Cookie, то после выполнения команды Cookie http://MSN.COM будут отображены cookie-данные, связанные с Web-сайтом MSN.COM. */ using System; using System.Net; class CookieDemo { public static void Main(string[] args) { if(args.Length != 1) { Console.WriteLine("Usage: CookieDemo } // Создаем WebRequest-запрос по заданному URI-адресу. HttpWebRequest req = (HttpWebRequest) WebRequest.Create(args[0]); // Получаем пустой cookie-контейнер. req.CookieContainer = new CookieContainer(); // Отправляем запрос и получаем ответ. HttpWebResponse resp = (HttpWebResponse) req.GetResponse(); // Отображаем cookie-данные. Console.WriteLine("Количество cookie-разделов: " + resp.Cookies.Count); Console.WriteLine("40,-20){1}", "Имя", "Значение"); for(int i=0; i < resp.Cookies.Count; i++) Console.WriteLine("{0, -20}{1}", resp.Cookies[i].Name, resp.Cookies[i].Value); // Закрываем поток, содержащий ответ. resp.Close(); } } Использование Свойства LastModified Иногда нужно узнать, когда в последний раз обновлялся Web-сайт с заданным URI- адресом. С помощью класса HttpWebResponse это не составляет труда, поскольку в нем определено свойство LastModified : public DateTime LastModified { get; } Свойство LastModified получает время последней URI-модификации. При выполнении следующей программы отображается время и дата последней модификации Web-сайта Microsoft.com : Глава 23. Сетевые возможности и использование Internet 661 // Использование свойства LastModified. using System; using System.Net; class HeaderDemo { public static void Main() { HttpWebRequest req = (HttpWebRequest) WebRequest.Create("http://www.Microsoft.com"); HttpWebResponse resp = (HttpWebResponse) req.GetResponse(); Console.WriteLine( "Время и дата последней модификации: " + resp.LastModified); resp.Close(); } } Учебный проект: программа MiniCrawler Чтобы показать, насколько Internet-программирование упрощается благодаря использованию классов WebRequest и WebReponse , обратимся к разработке Web-такси. Web-такси — это программа (здесь она называется MiniCrawler ), которая просто перемещается от одной Web-страницы к другой. Web-такси в различных средствах поиска используется для составления каталога содержимого. MiniCrawler — довольно простая программа. Она начинает работать с заданного вами URI-адреса, а затем считывает его содержимое в расчете найти ссылку на какой-нибудь Web-документ. Если такая ссылка найдется, “водитель” Web-такси поинтересуется вашим мнением о дальнейших действиях: перейти по этой ссылке, искать другую (на той же странице) или завершить работу. Программа MiniCrawler имеет ряд ограничений. Во-первых, поиску подлежат только абсолютные ссылки, т.е. те, которые задаются с использованием гипертекстовой команды href= . Относительные ссылки игнорируются. Во-вторых, здесь не предусмотрена возможность вернуться назад, т.е. к предыдущей ссылке, по которой был сделан переход. В- третьих, эта программа отображает только одни гиперссылки без сопутствующего содержимого. Но, несмотря на эти ограничения, Web-такси вполне функционально. К тому же, при желании вы можете расширить его возможности. И в самом деле, почему бы вам не добавить в MiniCrawler какое-нибудь полезное средство? Ведь подобная попытка позволит лучше познакомиться с сетевыми классами и сетевыми C#-возможностями в целом. Итак, рассмотрим код программы MiniCrawler : // MiniCrawler: Web-такси. using System; using System.Net; using System.IO; class MiniCrawler { 662 Часть II. Библиотека C# // Находим гиперссылку в строке. static string FindLink(string htmlstr, ref int startloc) { int i; int start, end; string uri = null; string lowcasestr = htmlstr.ToLower(); i = lowcasestr.IndexOf("href=\"http", startloc); if(i != -1) { start = htmlstr.IndexOf('"', i) + 1; end = htmlstr.IndexOf('"', start); uri = htmlstr.Substring(start, end-start); startloc = end; } return uri; } public static void Main(string[] args) { string link = null; string str; string answer; int curloc; // Содержит текущую позицию в ответе. if(args.Length != 1) { Console.WriteLine("Usage: MiniCrawler } string uristr = args[0]; // Содержит текущий URI-адрес. try { do { Console.WriteLine("Переход по адресу " + uristr); // Создаем WebRequest-запрос по заданному URI. HttpWebRequest req = (HttpWebRequest) WebRequest.Create(uristr); uristr = null; // Запрещаем дальнейшее // использование этого URI-адреса. // Отсылаем этот запрос и получаем ответ. HttpWebResponse resp = (HttpWebResponse) req.GetResponse(); // Из потока, содержащего ответ, получаем // входной поток. Stream istrm = resp.GetResponseStream(); // Представляем входной поток // в виде StreamReader-объекта. StreamReader rdr = new StreamReader(istrm); // Считываем целую страницу. Глава 23. Сетевые возможности и использование Internet 663 str = rdr.ReadToEnd(); curloc = 0; do { // Находим следующий URI-адрес для перехода // по гиперссылке. link = FindLink(str, ref curloc); if(link != null) { Console.WriteLine("Гиперссылка найдена: " + link); Console.Write("Ссылка, Дальше, Выход?"); answer = Console.ReadLine(); if(string.Compare(answer, "C", true) == 0) { uristr = string.Copy(link); break; } else if(string.Compare( answer, "B", true) == 0) { break; } else if(string.Compare( answer, "Д", true) == 0) { Console.WriteLine("Поиск следующей ссылки."); } } else { Console.WriteLine("Больше ссылок не найдено."); break; } } while(link.Length > 0); // Закрываем поток, содержащий ответ. resp.Close(); } while(uristr != null); } catch(WebException exc) { Console.WriteLine("Сетевая ошибка: " + exc.Message + "\nКод состояния: " + exc.Status); } catch(ProtocolViolationException exc) { Console.WriteLine("Ошибка протокола: " + exc.Message); } catch(UriFormatException exc) { Console.WriteLine("Ошибка в формате URI: " + exc.Message); } catch(NotSupportedException exc) { Console.WriteLine("Неизвестный протокол: " + exc.Message); } catch(IOException exc) { Console.WriteLine("I/O Error: " + exc.Message); } Console.WriteLine("Завершение программы MiniCrawler."); } } Вот как выглядит фрагмент сеанса работы с программой MiniCrawler : 664 Часть II. Библиотека C# C:>MiniCrawler http://osborne.com Переход по адресу http://osborne.com Гиперссылка найдена: http://www.osborne.com/aboutus/aboutus.shtml Ссылка, Дальше, Выход? Д Поиск следующей ссылки. Гиперссылка найдена: http://www.osborne.com/downloads/downloads.shtml Ссылка, Дальше, Выход? С Переход по адресу http://www.osborne.com/downloads/downloads.shtml Теперь рассмотрим, как работает эта программа. URI-адрес, с которого MiniCrawler начинает “извоз”, задается в командной строке. В методе Main() этот URI- адрес запоминается в строке uristr . По нему создается запрос, а переменная uristr устанавливается равной значению null , чтобы нулевая ссылка служила индикатором того, что этот URI-адрес уже использован. Затем запрос отсылается, и на него ожидается ответ. Содержимое полученного ответа считывается посредством вызова метода GetResponseStream() , а результат этого вызова (входной поток) представляется в виде streamReader -объекта. После этого вызывается метод ReadToEnd() , который возвращает полное содержимое потока в виде строки. В полученной строке программа выполняет поиск гиперссылки. Это реализуется с помощью статического метода FindLink() , который также определен в классе MiniCrawler . Методу FindLink() , помимо строкового содержимого потока, также передается стартовая позиция, с которой необходимо начать поиск. Параметры, которые принимают эти значения, имеют имена htmlstr и startloc , соответственно. Обратите внимание на то, что startloc является ref -параметром. Метод FindLink() сначала создает копию “потоковой” строки в “строчном” написании, а затем ищет подстроку href="http , которая означает гиперссылку. Если такая подстрока обнаружится, URI- адрес копируется в строку uri , а значение параметра startloc обновляется и устанавливается равным конечной позиции найденной гиперссылки. Поскольку startloc — ref -параметр, то соответствующий аргумент будет обновлен и в методе Main() , в результате чего следующий поиск начнется с той позиции, на которой остановился предыдущий. В конечном итоге метод FindLink() возвращает значение строки uri Если гиперссылка не обнаружится, то, поскольку uri была инициализирована null - значением, метод и вернет эту нулевую ссылку, которая “засвидетельствует” неудачный исход. Метод Main() при удачном выполнении метода FindLink() отображает значение найденной гиперссылки, и предлагает пользователю выбрать следующее действие из трех возможных. Пользователь может выбрать переход по ссылке (нажав клавишу <С>), заказать дальнейший поиск другой ссылки (нажав клавишу <Д>) или выйти из программы (нажав клавишу <В>). Если пользователь нажмет клавишу <С>, будет выполнен новый запрос по известному адресу с получением содержимого соответствующий страницы. И теперь в новом гипертексте будет разыскиваться очередная ссылка. Этот процесс продолжается до тех пор, пока не будут найдены все гиперссылки. Возможно, вам захочется усилить описанную выше программу MiniCrawler Например, попробуйте автоматизировать Web-такси, заставив его посещать каждую найденную гиперссылку, не спрашивая мнения пользователя. Другими словами, укажите какую-нибудь начальную страницу и отправьте Web-такси в путь, к первой ссылке, которую он найдет. Затем (уже в новой странице) пусть оно снова отправится по первой найденной ссылке и т.д. В том случае, если ваше Web-такси попадет в “тупик”, оно должно возвратиться назад на один уровень, найти (в предыдущей странице) следующую гиперссылку и действовать по уже описанной схеме. Чтобы реализовать эту Глава 23. Сетевые возможности и использование Internet 665 схему, для хранения URI-адресов и текущей позиции поиска внутри URI-строки следует использовать стек. Поэтому можете воспользоваться коллекцией типа Stack . Можно еще усложнить задачу: попробуйте найденные гиперссылки отобразить в виде дерева. Использование класса WebClient “На десерт” кратко рассмотрим класс WebClient . Как упоминалось в начале этой главы, если вам нужно загрузить на удаленный компьютер (или из него) файл, то вместо классов WebRequest и WebResponse можно для этого использовать класс WebClient Достоинство класса WebClient состоит в том, что он выполняет многие действия сам, освобождая таким образом вас. В классе WebClient определен один конструктор: public WebClient() В нем также определены весьма полезные свойства (см. табл. 23.6) и методы (см. табл. 23.7). Все методы генерируют исключение типа UriFormatException , если заданный URI-адрес окажется недействительным, и исключение типа WebException , если во время передачи данных возникнет ошибка. Таблица 23.6. Свойства, определенные в классе WebClient Свойство Описание public string BaseAddress { get; set; } Получает или устанавливает базовый адрес нужного URI, По умолчанию это свойство имеет null -значение. Если это свойство установлено, то адреса, заданные методами класса WebClient , должны интерпретироваться относительно этого базового адреса public ICredentials Credentials { get; set; } Получает или устанавливает регистрационную информацию. Это свойство по умолчанию имеет значение null public WebHeaderCollection Headers{ get; set;} Получает или устанавливает коллекцию заголовков запроса public NameValueCollection QueryString { get; set;} Получает или устанавливает строку запроса, состоящую из пар имя/значение, которые могут быть присоединены к запросу. Строка запроса отделяется от URI символом " ? ". Если таких пар больше одной, то каждая из них отделяется символом " @ " public WebHeaderCollection ResponseHeaders{ get;} Получает коллекцию заголовков ответа Таблица 23.7. Методы, определенные в классе WebClient Метод Описание public byte[] DownloadData( string uri ) Загружает информацию с Web-страницы, URI-адрес которой задается параметром uri . Возвращает результат в виде массива байтов public void DownloadFile( string uri , string fname ) Загружает информацию с Web-страницы, URI-адрес которой задается параметром uri , и сохраняет результат в файле, имя которого задается параметром fname public Stream OpenRead( string uri ) Возвращает входной поток, информацию из которого можно прочитать с использованием URI-адреса, заданного параметром uri . После завершения считывания этот поток необходимо закрыть 666 Часть II. Библиотека C# Окончание табл. 23.7 Метод Описание public Stream OpenWrite( string uri ) Возвращает выходной поток, в который можно записать информацию с помощью URI-адреса, заданного параметром uri . После завершения записи этот поток необходимо закрыть public Stream OpenWrite( string uri , string how ) Возвращает выходной поток, в который можно записать информацию с использованием URI-адреса, заданного параметром uri . После завершения записи этот поток необходимо закрыть. Строка, переданная в параметре how , задает способ записи этой информации public byte[] UploadData( string uri , byte[] info ) Записывает информацию, заданную параметром info , по URI-адресу, заданному параметром uri . Возвращает ответ public byte[] UploadData( string uri , string how , byte[] info ) Записывает информацию, заданную параметром info , по URI-адресу, заданному параметром uri . Возвращает ответ. Строка, переданная в параметре how , задает способ записи этой информации public byte[] UploadFile( string uri , string fname ) Записывает информацию из файла имя которого задается параметром fname , по URI-адресу, заданному параметром uri . Возвращает ответ public byte[] UploadFile( string uri , string how , string fname ) Записывает информацию из файла, имя которого задается параметром fname , по URI-адресу, заданному параметром uri . Возвращает ответ. Строка, переданная в параметре how , задает способ записи этой информации public byte[] UploadValues( string uri , HameValueCollection vals ) Записывает значения, хранимые в коллекции, заданной параметром vals , по URI-адресу, заданному параметром uri . Возвращает ответ public byte[] UploadValues( string uri , string how , NameValueCollection vals ) Записывает значения, хранимые в коллекции, заданной параметром vals , по URI-адресу, заданному параметром uri . Возвращает ответ Строка, переданная в параметре how , задает способ записи этой информации Использование класса WebClient для загрузки данных в файл демонстрируется в следующей программе: // Использование класса WebClient для загрузки // информации в файл. using System; using System.Net; using System.IO; class WebClientDemo { public static void Main() { WebClient user = new WebClient(); string uri = "http://www.osborne.com"; string fname = "data.txt"; try { Console.WriteLine( "Загрузка данных из Web-страницы " + uri + " в файл " + fname); user.DownloadFile(uri, fname); Глава 23. Сетевые возможности и использование Internet 667 } catch(WebException exc) { Console.WriteLine(exc); } catch(UriFormatException exc) { Console.WriteLine(exc); } Console.WriteLine("Загрузка завершена."); } } Эта программа загружает информацию с Web-сайта Osborne.com и помещает ее в файл с именем data.txt . Обратите внимание на то, что все это реализуется очень небольшим количеством строк кода, в число которых включены и те, которые обеспечивают обработку возможных исключений. Приведенная программа позволяет загрузить информацию с любого URI-адреса: для этого достаточно изменить соответствующим образом строку, заданную переменной uri Несмотря на то что классы WebReguest и WebResponse дают программисту большие возможности для управления и доступ к более обширной информации, средств, инкапсулированных классом WebClient , вполне достаточно для потребностей многих приложений. Он особенно полезен в том случае, если приложение должно обеспечить лишь загрузку информации из Web-пространства. Например, с помощью класса WebClient вы могли бы организовать получение обновленной документации по интересующим вас продуктам. Полный справочник по Часть III Применение языка C# В части III представлены три C#-приложения. Первое иллюстрирует создание компонентов и управление ими. Поскольку многие средства C# были разработаны специально для того, чтобы упростить их создание, нетрудно предположить, что компоненты составляют важную часть программирования на C#. Во втором приложении демонстрируется создание Windows- программы с помощью классов, определенных в пространстве имен System.Windows.Forms. Наконец, третье приложение представляет собой пример “чистого кода”: речь идет о применении синтаксического анализа методом рекурсивного спуска к вычислению алгебраических выражений. |