Чтение онлайн

на главную - закладки

Жанры

Полное руководство. С# 4.0
Шрифт:

Получить перечислитель, устанавливаемый в начало коллекции, вызвав для этой коллекции метод GetEnumerator.

Организовать цикл, в котором вызывается метод MoveNext. Повторять цикл до тех пор, пока метод MoveNext возвращает логическое значение true.

Получить в цикле каждый элемент коллекции с помощью свойства Current. Ниже приведен пример программы, в которой реализуется данная процедура. В этой программе используется класс ArrayList, но общие принципы циклического обращения к элементам коллекции с помощью перечислителя остаются неизменны ми для коллекций любого типа, в том числе и обобщенных. // Продемонстрировать применение перечислителя. using System; using System.Collections; class EnumeratorDemo { static void Main { ArrayList list = new ArrayList(1); for (int i=0; i < 10; i++) list.Add(i); // Использовать перечислитель для доступа к списку. IEnumerator etr = list.GetEnumerator; while(etr.MoveNext) Console.Write(etr.Current + " "); Console.WriteLine; // Повторить перечисление списка. etr.Reset; while(etr.MoveNext) Console.Write(etr.Current + " "); Console.WriteLine; } } Вот к какому результату приводит выполнение этой программы. 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 Вообще говоря, для циклического обращения к элементам коллекции цикл foreach оказывается более удобным, чем перечислитель. Тем не менее перечисли тель предоставляет больше возможностей для управления, поскольку его можно при желании всегда установить в исходное положение. Применение перечислителя типа IDictionaryEnumerator Если для организации коллекции в виде словаря, например типа Hashtable, реализуется необобщенный интерфейс IDictionary, то для циклического обра щения к элементам такой коллекции следует использовать перечислитель типа IDictionaryEnumerator вместо перечислителя типа IEnumerator. Интерфейс IDictionaryEnumerator наследует от интерфейса IEnumerator и имеет три допол нительных свойства. Первым из них является следующее свойство. DictionaryEntry Entry { get; } Свойство Entry позволяет получить пару "ключ-значение" из перечислителя в форме структуры DictionaryEntry. Напомним, что в структуре DictionaryEntry определяются два свойства, Key и Value, с помощью которых можно получать доступ к ключу или значению, связанному с элементом коллекции. Ниже приведены два дру гих свойства, определяемых в интерфейсе IDictionaryEnumerator. object Key { get; } object Value { get; } С помощью этих свойств осуществляется непосредственный доступ к ключу или значению. Перечислитель типа IDictionaryEnumerator используется аналогично обычному перечислителю, за исключением того, что текущее значение в данном случае получа ется с помощью свойств Entry, Key или Value, а не свойства Current. Следовательно, приобретя перечислитель типа IDictionaryEnumerator, необходимо вызвать метод MoveNext, чтобы получить первый элемент коллекции. А для получения остальных ее элементов следует продолжить вызовы метода MoveNext. Этот метод возвращает логическое значение false, когда в коллекции больше нет ни одного элемента. В приведенном ниже примере программы элементы коллекции типа Hashtable перечисляются с помощью перечислителя типа IDictionaryEnumerator. // Продемонстрировать применение перечислителя типа IDictionaryEnumerator. using System; using System.Collections; class IDicEnumDemo { static void Main { // Создать хеш-таблицу. Hashtable ht = new Hashtable; // Добавить элементы в таблицу. ht.Add("Кен", "555-7756"); ht.Add("Мэри", "555-9876"); ht.Add("Том", "555-3456"); ht.Add("Тодд", "555-3452"); // Продемонстрировать применение перечислителя. IDictionaryEnumerator etr = ht.GetEnumerator; Console.WriteLine("Отобразить информацию с помощью свойства Entry."); while(etr.MoveNext) Console.WriteLine(etr.Entry.Key + ": " + etr.Entry.Value); Console.WriteLine ; Console.WriteLine("Отобразить информацию " + "с помощью свойств Key и Value."); etr.Reset; while(etr.MoveNext) Console.WriteLine(etr.Key + ": " + etr.Value); } } Ниже приведен результат выполнения этой программы. Отобразить информацию с помощью свойства Entry. Мэри: 555-9876 Том: 555-3456 Тодд: 555-3452 Кен: 555-7756 Отобразить информацию с помощью свойств Key и Value. Мэри: 555-9876 Том: 555-3456 Тодд: 555-3452 Кен: 555-7756 Реализация интерфейсов IEnumerable и IEnumerator Как упоминалось выше, для циклического обращения к элементам коллекции за частую проще (да и лучше) организовать цикл foreach, чем пользоваться непосред ственно методами интерфейса IEnumerator. Тем не менее ясное представление о принципе действия подобных интерфейсов важно иметь по еще одной причине: если требуется создать класс, содержащий объекты, перечисляемые в цикле foreach, то в этом классе следует реализовать интерфейсы IEnumerator и IEnumerable. Иными словами, для того чтобы обратиться к объекту определяемого пользователем класса в цикле foreach, необходимо реализовать интерфейсы IEnumerator и IEnumerable в их обобщенной или необобщенной форме. Правда, сделать это будет нетрудно, по скольку оба интерфейса не очень велики. В приведенном ниже примере программы интерфейсы IEnumerator и IEnumerable реализуются в необобщенной форме, с тем чтобы перечислить содер жимое массива, инкапсулированного в классе MyClass. // Реализовать интерфейсы IEnumerable и IEnumerator. using System; using System.Collections; class MyClass : IEnumerator, IEnumerable { char[] chrs = { 'А', 'В', 'C', 'D' }; int idx = -1; // Реализовать интерфейс IEnumerable. public IEnumerator GetEnumerator { return this; } // В следующих методах реализуется интерфейс IEnumerator // Возвратить текущий объект. public object Current { get { return chrs[idx]; } } // Перейти к следующему объекту. public bool MoveNext { if(idx == chrs.Length-1) { Reset; // установить перечислитель в конец return false; } idx++; return true; } // Установить перечислитель в начало. public void Reset { idx = -1; } } class EnumeratorImplDemo { static void Main { MyClass me = new MyClass; // Отобразить содержимое объекта me. foreach(char ch in me) Console.Write(ch + " "); Console.WriteLine; // Вновь отобразить содержимое объекта me. foreach(char ch in me) Console.Write(ch + " "); Console.WriteLine; } } Эта программа дает следующий результат. А В С D А В С D В данной программе сначала создается класс MyClass, в котором инкапсулируется небольшой массив типа char, состоящий из символов А-D. Индекс этого массива хра нится в переменной idx, инициализируемой значением -1. Затем в классе MyClass ре ализуются оба интерфейса, IEnumerator и IEnumerable. Метод GetEnumerator возвращает ссылку на перечислитель, которым в данном случае оказывается текущий объект. Свойство Current возвращает следующий символ в массиве, т.е. объект, ука зываемый по индексу idx. Метод MoveNext перемещает индекс idx в следующее положение. Этот метод возвращает логическое

значение false, если достигнут конец коллекции, в противном случае — логическое значение true. Напомним, что перечис литель оказывается неопределенным вплоть до первого вызова метода MoveNext. Следовательно, метод MoveNext автоматически вызывается в цикле foreach перед обращением к свойству Current. Именно поэтому первоначальное значение пере менной idx устанавливается равным -1. Оно становится равным нулю на первом шаге цикла foreach. Обобщенная реализация рассматриваемых здесь интерфейсов будет действовать по тому же самому принципу. Далее в методе Main создается объект mc типа MyClass, и содержимое этого объекта дважды отображается в цикле foreach. Применение итераторов Как следует из предыдущих примеров, реализовать интерфейсы IEnumerator и IEnumerable нетрудно. Но еще проще воспользоваться итератором, который пред ставляет собой метод, оператор или аксессор, возвращающий по очереди члены со вокупности объектов от ее начала и до конца. Так, если некоторый массив состоит из пяти элементов, то итератор данного массива возвратит все эти элементы по очереди. Реализовав итератор, можно обращаться к объектам определяемого пользователем класса в цикле foreach. Обратимся сначала к простому примеру итератора. Приведенная ниже программа является измененной версией предыдущей программы, в которой вместо явной реали зации интерфейсов IEnumerator и IEnumerable применяется итератор. // Простой пример применения итератора. using System; using System.Collections; class MyClass { char[] chrs = { 'A', 'B', 'C', 'D' }; // Этот итератор возвращает символы из массива chrs. public IEnumerator GetEnumerator { foreach(char ch in chrs) yield return ch; } } class ItrDemo { static void Main { MyClass me = new MyClass; foreach(char ch in me) Console.Write(ch + " "); Console.WriteLine; } } При выполнении этой программы получается следующий результат. А В С D Как видите, содержимое массива mc.chrs перечислено. Рассмотрим эту программу более подробно. Во-первых, обратите внимание на то, что в классе MyClass не указывается IEnumerator в качестве реализуемого интерфей са. При создании итератора компилятор реализует этот интерфейс автоматически. И во-вторых, обратите особое внимание на метод GetEnumerator, который ради удобства приводится ниже еще раз. // Этот итератор возвращает символы из массива chrs. public IEnumerator GetEnumerator { foreach(char ch in chrs) yield return ch; } Это и есть итератор для объектов класса MyClass. Как видите, в нем явно реализу ется метод GetEnumerator, определенный в интерфейсе IEnumerable. А теперь перейдем непосредственно к телу данного метода. Оно состоит из цикла foreach, в котором возвращаются элементы из массива chrs. И делается это с помощью опе ратора yield return. Этот оператор возвращает следующий объект в коллекции, которым в данном случае оказывается очередной символ в массиве chrs. Благодаря этому средству обращение к объекту mc типа MyClass организуется в цикле foreach внутри метода Main. Обозначение yield служит в языке C# в качестве контекстного ключевого слова. Это означает, что оно имеет специальное назначение только в блоке итератора. А вне этого блока оно может быть использовано аналогично любому другому идентификатору. Следует особо подчеркнуть, что итератор не обязательно должен опираться на мас сив или коллекцию другого типа. Он должен просто возвращать следующий элемент из совокупности элементов. Это означает, что элементы могут быть построены дина мически с помощью соответствующего алгоритма. В качестве примера ниже приведе на версия предыдущей программы, в которой возвращаются все буквы английского алфавита, набранные в верхнем регистре. Вместо массива буквы формируются в цикле for. // Пример динамического построения значений, // возвращаемых по очереди с помощью итератора. using System; using System.Collections; class MyClass { char ch = 'A'; // Этот итератор возвращает буквы английского // алфавита, набранные в верхнем регистре. public IEnumerator GetEnumerator { for(int i=0; i < 26; i++) yield return (char) (ch + i); } } class ItrDemo2 { static void Main { MyClass me = new MyClass; foreach(char ch in me) Console.Write(ch + " "); Console.WriteLine; } } Вот к какому результату приводит выполнение этой программы. A B C D E F G H I J K L M N O P Q R S T U V W X Y Z Прерывание итератора Для преждевременного прерывания итератора служит следующая форма опера тора yield. yield break; Когда этот оператор выполняется, итератор уведомляет о том, что достигнут конец коллекции. А это, по существу, останавливает сам итератор. Приведенная ниже программа является версией предыдущей программы, изме ненной с целью отобразить только первые десять букв английского алфавита. // Пример прерывания итератора. using System; using System.Collections; class MyClass { char ch = 'A'; // Этот итератор возвращает первые 10 букв английского алфавита. public IEnumerator GetEnumerator { for(int i=0; i < 26; i++) { if(i == 10) yield break; // прервать итератор преждевременно yield return (char) (ch + i); } } } class ItrDemo3 { static void Main { MyClass mc = new MyClass; foreach(char ch in mc) Console.Write(ch + " "); Console.WriteLine; } } Эта программа дает следующий результат. A B C D E F G H I J Применение нескольких операторов yield В итераторе допускается применение нескольких операторов yield. Но каждый такой оператор должен возвращать следующий элемент в коллекции. В качестве при мера рассмотрим следующую программу. // Пример применения нескольких операторов yield. using System; using System.Collections; class MyClass { // Этот итератор возвращает буквы А, В, С, D и Е. public IEnumerator GetEnumerator { yield return 'A'; yield return 'B'; yield return 'C'; yield return 'D'; yield return 'E'; } } class ItrDemo5 { static void Main { MyClass me = new MyClass ; foreach(char ch in mc) Console.Write(ch + " "); Console.WriteLine; } } Ниже приведен результата выполнения этой программы. А В С D Е В данной программе внутри метода GetEnumerator выполняются пять опера торов yield. Следует особо подчеркнуть, что они выполняются по очереди и каждый раз, когда из коллекции получается очередной элемент. Таким образом, на каждом шаге цикла foreach в методе Main возвращается только один символ. Создание именованного итератора В приведенных выше примерах был продемонстрирован простейший способ ре ализации итератора. Но ему имеется альтернатива в виде именованного итератора. В данном случае создается метод, оператор или аксессор, возвращающий ссылку на объект типа IEnumerable. Именно этот объект используется в коде для предоставле ния итератора. Именованный итератор представляет собой метод, общая форма кото рого приведена ниже: public IEnumerable имяитератора(списокпараметров) { // ... yield return obj; } где имяитератора обозначает конкретное имя метода; списокпараметров — от нуля до нескольких параметров, передаваемых методу итератора; obj — следующий объект, возвращаемый итератором. Как только именованный итератор будет создан, его можно использовать везде, где он требуется, например для управления циклом foreach. Именованные итераторы оказываются весьма полезными в некоторых ситуациях, поскольку они позволяют передавать аргументы итератору, управляющему процес сом получения конкретных элементов из коллекции. Например, итератору можно передать начальный и конечный пределы совокупности элементов, возвращаемых из коллекции итератором. Эту форму итератора можно перегрузить, расширив ее функ циональные возможности. В приведенном ниже примере программы демонстриру ются два способа применения именованного итератора для получения элементов кол лекции. В одном случае элементы перечисляются в заданных начальном и конечном пределах, а в другом — элементы перечисляются с начала последовательности и до указанного конечного предела. // Использовать именованные итераторы. using System; using System.Collections; class MyClass { char ch = 'A'; // Этот итератор возвращает буквы английского алфавита, // начиная с буквы А и кончая указанным конечным пределом. public IEnumerable MyItr(int end) { for(int i=0; i < end; i++) yield return (char) (ch + i); } // Этот итератор возвращает буквы в заданных пределах. public IEnumerable MyItr(int begin, int end) { for(int i=begin; i < end; i++) yield return (char) (ch + i); } } class ItrDemo4 { static void Main { MyClass mc = new MyClass; Console.WriteLine("Возвратить по очереди первые 7 букв:"); foreach(char ch in mc.MyItr(7)) Console.Write(ch + " "); Console.WriteLine("\n"); Console.WriteLine("Возвратить по очереди буквы от F до L:"); foreach(char ch in mc.MyItr(5, 12)) Console.Write(ch + " "); Console.WriteLine; } } Эта программа дает следующий результат. Возвратить по очереди первые 7 букв: А В С D Е F G Возвратить по очереди буквы от F до L: F G Н I J К L Создание обобщенного итератора В приведенных выше примерах применялись необобщенные итераторы, но, конеч но, ничто не мешает создать обобщенные итераторы. Для этого достаточно возвратить объект обобщенного типа IEnumerator или IEnumerable. Ниже приведен пример создания обобщенного итератора. // Простой пример обобщенного итератора. using System; using System.Collections.Generic; class MyClass { T[] array; public MyClass(T[] a) { array = a; } // Этот итератор возвращает символы из массива chrs. public IEnumerator GetEnumerator { foreach(T obj in array) yield return obj; } } class GenericItrDemo { static void Main { int[] nums = { 4, 3, 6, 4, 7, 9 }; MyClass me = new MyClass(nums); foreach(int x in mc) Console.Write(x + " "); Console.WriteLine; bool[] bVals = { true, true, false, true }; MyClass mc2 = new MyClass(bVals); foreach(bool b in mc2) Console.Write(b + " "); Console.WriteLine; } } Вот к какому результату приводит выполнение этой программы. 4 3 6 4 7 9 True True False True В данном примере массив, состоящий из возвращаемых по очереди объектов, пере дается конструктору класса MyClass. Тип этого массива указывает в качестве аргумен та типа в конструкторе класса MyClass. Метод GetEnumerator оперирует данными обобщенного типа т и возвраща ет перечислитель типа IEnumerator. Следовательно, итератор, определенный в классе MyClass, способен перечислять данные любого типа. Инициализаторы коллекций В C# имеется специальное средство, называемое инициализатором коллекции и упро щающее инициализацию некоторых коллекций. Вместо того чтобы явно вызывать ме тод Add, при создании коллекции можно указать список инициализаторов. После этого компилятор организует автоматические вызовы метода Add, используя значе ния из этого списка. Синтаксис в данном случае ничем не отличается от инициализа ции массива. Обратимся к следующему примеру, в котором создается коллекция типа List, инициализируемая символами С, А, Е, В, D и F. List lst = new List { 'С', 'А', 'Е', 'В', 'D', 'F' }; После выполнения этого оператора значение свойства lst.Count будет равно 6, поскольку именно таково число инициализаторов. А после выполнения следующего цикла foreach: foreach(ch in lst) Console.Write(ch + " "); получится такой результат: С A E В D F Для инициализации коллекции типа LinkedList, в которой хра нятся пары "ключ-значение", инициализаторы приходится предоставлять парами, как показано ниже. SortedList lst = new SortedList { {1, "один"}, {2, "два" }, {3, "три"} }; Компилятор передаст каждую группу значений в качестве аргументов методу Add. Следовательно, первая пара инициализаторов преобразуется компилятором в вызов Add(1, "один"). Компилятор вызывает метод Add автоматически для ввода инициализаторов в коллекцию, и поэтому инициализаторы коллекций можно использовать только в кол лекциях, поддерживающих открытую реализацию метода Add. Это означает, что инициализаторы коллекций нельзя использовать в коллекциях типа Stack, Stack, Queue или Queue, поскольку в них метод Add не поддерживается. Их нельзя применять также в тех коллекциях типа LinkedList, где метод Add предостав ляется как результат явной реализации соответствующего интерфейса.

ГЛАВА 26. Сетевые средства подключения к Интернету

Я зык С# предназначен для программирования в совре менной вычислительной среде, где Интернету, есте ственно, принадлежит весьма важная роль. Одной из главных целей разработки С# было внедрение в этот язык программирования средств, необходимых для до ступа к Интернету. Такой доступ можно было осуществить и в предыдущих версиях языков программирования, вклю чая С и C++, но поддержка операций на стороне сервера, загрузка файлов и получение сетевых ресурсов в этих язы ках не вполне отвечали потребностям большинства про граммистов. Эта ситуация коренным образом изменилась в С#. Используя стандартные средства C# и среды .NET Framework, можно довольно легко сделать приложения совместимыми с Интернетом и написать другие виды про грамм, ориентированных на подключение к Интернету. Поддержка сетевого подключения осуществляется через несколько пространств имен, определенных в среде .NET Framework, и главным среди них является пространство имен System.Net. В нем определяется целый ряд высо коуровневых, но простых в использовании классов, поддер живающих различные виды операций, характерных для работы с Интернетом. Для этих целей доступен также ряд пространств, вложенных в пространство имен System.Net. Например, средства низкоуровневого сетевого управления через сокеты находятся в пространстве имен System.Net. Sockets, поддержка электронной почты — в простран стве имен System.Net.Mail, а поддержка защищенных сетевых потоков — в пространстве имен System.Net. Security. Дополнительные функциональные возможно сти предоставляются в ряде других вложенных пространств имен. К числу других не менее важных пространств имен, 26 связанных с сетевым подключением к Интернету, относится пространство System. Web. Это и вложенные в него пространства имен поддерживают сетевые приложения на основе технологии ASP.NET. В среде .NET Framework имеется достаточно гибких средств и возможностей для се тевого подключения к Интернету. Тем не менее для разработки многих приложений более предпочтительными оказываются функциональные возможности, доступные в пространстве имен System.Net. Они и удобны, и просты в использовании. Именно по этому пространству имен System.Net будет уделено основное внимание в этой главе. Члены пространства имен System.Net

Пространство имен System.Net довольно обширно и состоит из многих членов. Полное их описание и обсуждение всех аспектов программирования для Интернета выходит далеко за рамки этой главы. (На самом деле для подробного рассмотрения всех вопросов, связанных с сетевым подключением к Интернету и его поддержкой в С#, потребуется отдельная книга.) Однако целесообразно хотя бы перечислить члены пространства имен System.Net, чтобы дать какое-то представление о том, что именно доступно для использования в этом пространстве. Ниже перечислены классы, определенные в пространстве имен System.Net. AuthenticationManager Authorization Cookie CookieCollection CookieContainer CookieException CredentialCache Dns DnsEndPoint DnsPermission DnsPermissionAttribute DownloadDataCompletedEventArgs DownloadProgressChangedEventArgs DownloadstringCompletedEventArgs EndPoint EndpointPermission FileWebRequest FileWebResponse FtpWebRequest FtpWebResponse HttpListener HttpListenerBasicIdentity HttpListenerContext HttpListenerException HttpListenerPrefixCollection HttpListenerRequest HttpListenerResponse HttpVersion HttpWebRequest HttpWebResponse IPAddress IPEndPoint IPEndPointCollection IPHostEntry IrDAEndPoint NetworkCredential OpenReadCompletedEventArgs OpenWriteCompletedEventArgs ProtocolViolationException ServicePoint ServicePointManager SocketAddress SocketPermission SocketPermissionAttribute TransportContext UploadDataCompletedEventArgs UploadFileCompletedEventArgs UploadProgressChangedEventArgs UploadstringCompletedEventArgs UploadValuesCompletedEventArgs WebClient WebException WebHeaderCollection WebPermission Помимо этого, в пространстве имен System.Net определен ряд делегатов. Несмотря на то что в пространстве имен System.Net определено немало членов, лишь немногие из них на самом деле требуются при решении наиболее типичных задач программирования для Интернета. Основу сетевых программных средств составляют аб страктные классы WebRequest и WebResponse. От этих классов наследуют все классы, поддерживающие конкретные сетевые протоколы. (Протокол определяет правила пере дачи данных по сети.) Например, к производным классам, поддерживающим стандарт ный сетевой протокол HTTP, относятся классы HttpWebRequest и HttpWebResponse. Классы HttpWebRequest и HttpWebResponse довольно просты в использовании. Тем не менее решение некоторых задач можно еще больше упростить, применяя под ход, основанный на классе WebClient. Так, если требуется только загрузить или вы грузить файл, то для этой цели лучше всего подойдет класс WebClient. Универсальные идентификаторы ресурсов В основу программирования для Интернета положено понятие универсального иден тификатора ресурса (URI), иногда еще называемого унифицированным указателем инфор мационного ресурса (URL). Этот идентификатор описывает местоположение ресурса в сети. В корпорации Microsoft принято пользоваться сокращением URI при описании членов пространства имен System.Net, и поэтому в данной книге выбрано именно это сокращение для обозначения универсального идентификатора ресурса. Иденти фикаторы URI, без сомнения, известны каждому, кто хотя бы раз пользовался браузе ром для поиска информации в Интернете. По существу, это адрес информационного ресурса, который указывается в соответствующем поле окна браузера. Ниже приведена общая форма идентификатора URI: Протокол://Идентификационныйномерсервера/Путькфайлу?Запрос где Протокол — это применяемый протокол, например HTTP; Идентификацион ныйномерсервера — конкретный сервер, например mhprofessional.com или WebPermissionAttribute WebProxy WebRequest WebRequestMethods WebRequestMethods.File WebRequestMethods.Ftp WebRequestMethods.Http WebResponse WebUtility Кроме того, в пространстве имен System.Net определены перечисленные ниже интерфейсы. AuthenticationModule IcertificatePolicy ICredentialPolicy ICredentials IcredentialsByHost IWebProxy IWebProxyScript IWebRequestCreate В этом пространстве имен определяются также приведенные ниже перечисления. AuthenticationSchemes DecompressionMethods FtpStatusCode HttpRequestHeader HttpResponseHeader HttpStatusCode NetworkAccess SecurityProtocolType TransportType WebExceptionStatus HerbSchildt.com; Путькфайлу — путь к конкретному файлу. Если же Путьк файлу не указан, то получается страница, доступная на указанном сервере по умолча нию. И наконец, Запрос обозначает информацию, отправляемую на сервер. Указывать Запрос необязательно. В C# идентификаторы URI инкапсулированы в класс Uri, рас сматриваемый далее в этой главе. Основы организации доступа к Интернету В классах, находящихся в пространстве имен System.Net, поддерживается модель взаимодействия с Интернетом по принципу запроса и ответа. При таком подходе пользовательская программа, являющаяся клиентом, запрашивает информацию у сервера, а затем переходит в состояние ожидания ответа. Например, в качестве запроса программа может отправить на сервер идентификатор URI некоторого веб-сайта. В от вет она получит гипертекстовую страницу, соответствующую указанному идентифи катору URI. Такой принцип запроса и ответа удобен и прост в применении, поскольку большинство деталей сетевого взаимодействия реализуются автоматически. На вершине иерархии сетевых классов находятся классы WebRequest и WebResponse, реализующие так называемые подключаемые протоколы. Как должно быть известно большинству читателей, для передачи данных в сети применяется не сколько разнотипных протоколов. К числу наиболее распространенных в Интернете относятся протокол передачи гипертекстовых файлов (HTTP), а также протокол пере дачи файлов (FTP). При создании идентификатора URI его префикс обозначает приме няемый сетевой протокол. Например, в идентификатореcom используется префикс http, обозначающий протокол передачи гипертекстовых файлов (HTTP). Как упоминалось выше, классы WebRequest и WebResponse являются абстракт ными, а следовательно, в них определенны в самом общем виде операции запроса и ответа, типичные для всех протоколов. От этих классов наследуют более конкретные производные классы, в которых реализуются отдельные протоколы. Эти производные классы регистрируются самостоятельно, используя для этой цели статический метод RegisterPrefix, определенный в классе WebRequest. При создании объекта типа WebRequest автоматически используется протокол, указываемый в префиксе URI, если, конечно, он доступен. Преимущество такого принципа "подключения" протоко лов заключается в том, что большая часть кода пользовательской программы остается без изменения независимо от типа применяемого протокола. В среде выполнения .NET Runtime протоколы HTTP, HTTPS и FTP определяются ав томатически. Так, если указать идентификатор URI с префиксом HTTP, то будет авто матически получен HTTP-совместимый класс, который поддерживает протокол HTTP. А если указать идентификатор URI с префиксом FTP, то будет автоматически получен FTP-совместимый класс, поддерживающий протокол FTP. При сетевом подключении к Интернету чаще всего применяется протокол HTTP, поэтому именно он и рассматривается главным образом в этой главе. (Тем не менее аналогичные приемы распространяются и на все остальные поддерживаемые протоко лы.) Протокол HTTP поддерживается в классах HttpWebRequest и HttpWebResponse. Эти классы наследуют от классов WebRequest и WebResponse, а кроме того, име ют собственные дополнительные члены, применимые непосредственно к протоколу HTTP. В пространстве имен System.Net поддерживается как синхронная, так и асинхрон ная передача данных. В Интернете предпочтение чаще всего отдается синхронным транзакциям, поскольку ими легче пользоваться. При синхронной передаче данных пользовательская программа посылает запрос и затем ожидает ответа от сервера. Но для некоторых разновидностей высокопроизводительных приложений более подхо дящей оказывается асинхронная передача данных. При таком способе передачи дан ных пользовательская программа продолжает обработку данных, ожидая ответа на переданный запрос. Но организовать асинхронную передачу данных труднее. Кроме того, не во всех программах можно извлечь выгоды из асинхронной передачи данных. Например, когда требуется получить информацию из Интернета, то зачастую ниче го другого не остается, как ожидать ее. В подобных случаях потенциал асинхронной передачи данных используется не полностью. Вследствие того что синхронный доступ к Интернету реализуется проще и намного чаще, именно он и будет рассматриваться в этой главе. Далее речь пойдет прежде всего о классах WebRequest и WebResponse, поскольку именно они положены в основу сетевых программных средств, доступных в простран стве имен System.Net. Класс WebRequest Класс WebRequest управляет сетевым запросом. Он является абстрактным, по скольку в нем не реализуется конкретный протокол. Тем не менее в нем определяются те методы и свойства, которые являются общими для всех сетевых запросов. В табл. 26.1 сведены методы, определенные в классе WebRequest и поддерживающие синхронную передачу данных, а в табл. 26.2 — свойства, объявляемые в классе WebRequest. Уста навливаемые по умолчанию значения свойств задаются в производных классах. Откры тые конструкторы в классе WebRequest не определены. Для того чтобы отправить запрос по адресу URI, необходимо сначала создать объект класса, производного от класса WebRequest и реализующего требуемый про токол. С этой целью вызывается статический метод Create, определенный в клас се WebRequest. Метод Create возвращает объект класса, наследующего от класса WebRequest и реализующего конкретный протокол. Таблица 26.1. Методы, определенные в классе WebRequest Метод Описание public static WebRequest Create(string requestUriString) Создает объект типа WebRequest для иден тификатора URI, указываемого в строке requestUriString. Возвращаемый объект реализует протокол, заданный префиксом иден тификатора URI. Следовательно, возвращаемый объект будет экземпляром класса, производного от класса WebRequest. Если затребованный про токол недоступен, то генерируется исключение NotSupportedException. А если недействите лен указанный формат идентификатора URI, то ге нерируется исключение UriFormatException Окончание табл. 26.1 Таблица 26.2. Свойства, определенные в классе WebRequest Метод Описание public static WebRequest Create(Uri requestUri) Создает объект типа WebRequest для иденти фикатора URI, указываемого с помощью пара метра requestUri. Возвращаемый объект реализует протокол, заданный префиксом иден тификатора URI. Следовательно, возвращаемый объект будет экземпляром класса, производного от класса WebRequest. Если затребованный про токол недоступен, то генерируется исключение NotSupportedException public virtual Stream GetRequestStream Возвращает поток вывода, связанный с запрошен ным ранее идентификатором URI public virtual WebResponse GetResponse Отправляет предварительно сформированный за прос и ожидает ответа. Получив ответ, возвращает его в виде объекта класса WebReponse. Этот объ ект используется затем в программе для получения информации по указанному адресу URI Свойство Описание public AuthenticationLevel AuthenticationLevel{ get; set; } Получает или устанавливает уровень аутентифи кации public virtual RequestCachePolicy CachePolicy { get; set; } Получает или устанавливает правила использо вания кеша, определяющие момент получения ответа из кеша public virtual string ConnectionGroupName { get; set; } Получает или устанавливает имя группы подклю чения. Группы подключения представляют собой способ создания ряда запросов. Они не нужны для простых транзакций в Интернете public virtual long ContentLength { get; set; } Получает или устанавливает длину передаваемо го содержимого public virtual string ContentType { get; set; } Получает или устанавливает описание переда ваемого содержимого public virtual Icredentials Credentials { get; set; } Получает или устанавливает мандат, т.е. учетные данные пользователя public static RequestCachePolicy DefaultCachePolicy { get; set; } Получает или устанавливает правила использо вания кеша по умолчанию, определяющие мо мент получения ответа из кеша public static IWebProxy DefaultWebProxy { get; set; } Получает или устанавливает используемый по умолчанию прокси-сервер public virtual WebHeaderCollection Headers{ get; set; } Получает или устанавливает коллегию заголовков public TokenImpersonationLevel ImpersonationLevel { get; set; } Получает или устанавливает уровень анонимно го воплощения Класс WebResponse В классе WebResponse инкапсулируется ответ, получаемый по запросу. Этот класс является абстрактным. В наследующих от него классах создаются отдельные его вер сии, поддерживающие конкретный протокол. Объект класса WebResponse обыч но получается в результате вызова метода GetResponse, определенного в классе WebRequest. Этот объект будет экземпляром отдельного класса, производного от класса WebResponse и реализующего конкретный протокол. Методы, определенные в классе WebResponse, сведены в табл. 26.3, а свойства, объявляемые в этом классе, — в табл. 26.4. Значения этих свойств устанавливаются на основании каждого запроса в отдельности. Открытые конструкторы в классе WebResponse не определяются. Таблица 26.3. Наиболее часто используемые методы, определенные в классе WebResponse Метод Описание public virtual void Close Закрывает ответный поток. Закрывает также поток ввода ответа, возвращаемый методом GetResponseStream public virtual Stream GetResponseStream Возвращает поток ввода, связанный с запрашивае мым URI. Из этого потока могут быть введены дан ные из запрашиваемого URI Окончание табл. 26.2 Свойство Описание public virtual string Method { get; set; } Получает или устанавливает протокол public virtual bool PreAuthenticate { get; set; } Если принимает логическое значение true, то в отправляемый запрос включается информация для аутентификации. А если принимает логиче ское значение false, то информация для аутен тификации предоставляется только по требова нию адресата URI public virtual IWebProxy Proxy { get; set; } Получает или устанавливает прокси-сервер. При менимо только в тех средах, где используется прокси-сервер public virtual Uri RequestUri { get; } Получает идентификатор URI конкретного запроса public virtual int Timeout { get; set; } Получает или устанавливает количество милли секунд, в течение которых будет ожидаться ответ на запрос. Для установки бесконечного ожида ния используется значение Timeout.Infinite public virtual bool UseDefaultCredential { get; set; } Получает или устанавливает значение, которое определяет, используется ли для аутентификации устанавливаемый по умолчанию мандат. Если имеет логическое значение true, то использу ется устанавливаемый по умолчанию мандат, т.е. учетные данные пользователя, в противном случае этот мандат не используется Классы HttpWebRequest и HttpWebResponse Оба класса, HttpWebRequest и HttpWebResponse, наследуют от классов WebRequest и WebResponse и реализуют протокол HTTP. В ходе этого процесса в обоих классах вводится ряд дополнительных свойств, предоставляющих подробные сведения о транзакции по протоколу HTTP. О некоторых из этих свойств речь пойдет далее в настоящей главе. Но для выполнения простых операций в Интернете эти до полнительные свойства, как правило, не требуются. Первый простой пример Доступ к Интернету организуется на основе классов WebRequest и WebResponse. Поэтому, прежде чем рассматривать этот процесс более подробно, было бы полезно обратиться к простому примеру, демонстрирующему порядок доступа к Интернету по принципу запроса и ответа. Глядя на то, как эти классы применяются на практике, легче понять, почему они организованы именно так, а не как-то иначе. В приведенном ниже примере программы демонстрируется простая, но весьма типичная для Интернета операция получения гипертекстового содержимого из кон кретного веб-сайта. В данном случае содержимое получается из веб-сайта издатель ства McGraw-Hill по адресу www.McGraw-Hill.com, но вместо него можно подставить адрес любого другого веб-сайта. В этой программе гипертекстовое содержимое выво дится на экран монитора отдельными порциями по 400 символов, чтобы полученную информацию можно было просматривать, не прибегая к прокрутке экрана. // Пример доступа к веб-сайту. using System; Таблица 26.4. Свойства, определенные в классе WebResponse Свойство Описание public virtual long ContentLength { get; set; } Получает или устанавливает длину принимаемого со держимого. Устанавливается равным -1, если дан ные о длине содержимого недоступны public virtual string ContentType { get; set; } Получает или устанавливает описание принимаемого содержимого public virtual WebHeaderCollection Headers { get; } Получает или устанавливает коллекцию заголовков, связанных с URI public virtual bool IsFromCache { get; } Принимает логическое значение true, если запрос получен из кэша. А если запрос доставлен по сети, то принимает логическое значение false public virtual bool IsMutuallyAuthenticated { get; } Принимает логическое значение true, если клиент и сервер опознают друг друга, а иначе — принимает логическое значение false public virtual Uri ResponseUri { get; } Получает URI, по которому был сформирован ответ. Этот идентификатор может отличаться от запрашивае мого, если ответ был переадресован по другому URI using System.Net; using System.IO; class NetDemo { static void Main { int ch; // Сначала создать объект запроса типа WebRequest по указанному URI. HttpWebRequest req = (HttpWebRequest) WebRequest.Create("http://www.McGraw-Hill.com"); // Затем отправить сформированный запрос и получить на него ответ. HttpWebResponse resp = (HttpWebResponse) req.GetResponse; // Получить из ответа поток ввода. Stream istrm = resp.GetResponseStream; / А теперь прочитать и отобразить гипертекстовое содержимое, полученное по указанному URI. Это содержимое выводится на экран отдельными порциями по 400 символов. После каждой такой порции следует нажать клавишу , чтобы вывести на экран следующую порцию из 400 символов. / for(int i=1; ; i++) { ch = istrm.ReadByte ; if(ch == -1) break; Console.Write((char) ch); if((i%400)==0) { Console.Write("\nНажмите

клавишу ."); Console.ReadLine; } } // Закрыть ответный поток. При этом закрывается также поток ввода istrm. resp.Close; } } Ниже приведена первая часть получаемого результата. (Разумеется, это содержи мое может со временем измениться в связи с обновлением запрашиваемого веб-сайта, и поэтому у вас оно может оказаться несколько иным.)

. Итак, выше приведена часть гипертекстового содержимого, полученного из веб сайта издательства McGraw-Hill по адресу www.McGraw-Hill.com. В рассматривае мом здесь примере программы это содержимое просто выводится в исходном виде на экран посимвольно и не форматируется в удобочитаемом виде, как это обычно дела ется в окне браузера. Проанализируем данную программу построчно. Прежде всего обратите внимание на использование в ней пространства имен System.Net. Как пояснялось ранее, в этом пространстве имен находятся классы сетевого подключения к Интернету. Обратите также внимание на то, что в данную программу включено пространство имен System. 10, которое требуется для того, чтобы прочитать полученную на веб-сайте информа цию, используя объект типа Stream. В начале программы создается объект типа WebRequest, содержащий требуемый URI. Как видите, для этой цели используется метод Create, а не конструктор. Это статический член класса WebRequest. Несмотря на то что класс WebRequest является абстрактным, это обстоятельство не мешает вызывать статический метод данного клас са. Метод Create возвращает объект типа HttpWebRequest. Разумеется, его зна чение требуется привести к типу HttpWebRequest, прежде чем присвоить его пере менной req ссылки на объект типа HttpWebRequest. На этом формирование запроса завершается, но его еще нужно отправить по указанному URL Для того чтобы отправить запрос, в рассматриваемой здесь программе вызыва ется метод GetResponse для объекта типа WebRequest. Отправив запрос, метод GetResponse переходит в состояние ожидания ответа. Как только ответ будет по лучен, метод GetResponse возвратит объект типа WebResponse, в котором инкап сулирован ответ. Этот объект присваивается переменной resp. Но в данном случае от вет принимается по протоколу HTTP, и поэтому полученный результат приводится к типу HttpWebResponse. Среди прочего в ответе содержится поток, предназначаемый для чтения данных из источника по указанному URL Далее поток ввода получается в результате вызова метода GetResponseStream для объекта resp. Это стандартный объект класса Stream со всеми атрибутами и сред ствами, необходимыми для организации потока ввода. Ссылка на этот поток присваи вается переменной istrm, с помощью которой данные могут быть прочитаны из ис точника по указанному URI, как из обычного файла. После этого в программе выполняется чтение данных из веб-сайта издательства McGraw-Hill по адресу www.McGraw-Hill.com и последующий их вывод на экран. А поскольку этих данных много, то они выводятся на экран отдельными порциями по 400 символов, после чего в программе ожидается нажатие клавиши , чтобы продолжить вывод. Благодаря этому выводимые данные можно просматривать без прокрутки экрана. Обратите внимание на то, что данные читаются посимвольно с по мощью метода ReadByte. Напомним, что этот метод возвращает очередной байт из потока ввода в виде значения типа int, которое требуется привести к типу char. По достижении конца потока этот метод возвращает значение -1. И наконец, ответный поток закрывается при вызове метода Close для объекта resp. Вместе с ответным потоком автоматически закрывается и поток ввода. Ответный поток следует закрывать в промежутках между последовательными запросами. В про тивном случае сетевые ресурсы могут быть исчерпаны, препятствуя очередному под ключению к Интернету. И в заключение анализа рассматриваемого здесь примера следует обратить особое внимание на следующее: для отображения гипертекстового содержимого, получаемо го от сервера, совсем не обязательно использовать объект типа HttpWebRequest или HttpWebResponse. Ведь для решения этой задачи в данной программе оказалось до статочно стандартных методов, определенных в классах WebRequest и WebResponse, и не потребовалось прибегать к специальным средствам протокола HTTP. Следова тельно, вызовы методов Create и GetResponse можно было бы написать сле дующим образом. // Сначала создать объект запроса типа WebRequest по указанному URI. WebRequest req = WebRequest.Create("http://www.McGraw-Hill.com"); // Затем отправить сформированный запрос и получить на него ответ. WebResponse resp = req.GetResponse; В тех случаях, когда не требуется приведение к конкретному типу реализации про токола, лучше пользоваться классами WebRequest и WebResponse, так как это дает возможность менять протокол, не оказывая никакого влияния на код программы. Но поскольку во всех примерах, приведенных в этой главе, используется протокол HTTP, то в ряде примеров демонстрируются специальные средства этого протокола из клас сов HttpWebRequest и HttpWebResponse. Обработка сетевых ошибок Программа из предыдущего примера составлена верно, но она совсем не защищена от простейших сетевых ошибок, которые способны преждевременно прервать ее вы полнение. Конечно, для программы, служащей в качестве примера, это не так важно, как для реальных приложений. Для полноценной обработки сетевых исключений, которые могут быть сгенерированы программой, необходимо организовать контроль вызовов методов Create, GetResponse и GetResponseStream. Следует осо бо подчеркнуть, что генерирование конкретных исключений зависит от используемого протокола. И ниже речь пойдет об ошибках, которые могут возникнуть при использо вании протокола HTTP, поскольку средства сетевого подключения к Интернету, доступ ные в С#, рассматриваются в настоящей главе на примере именно этого протокола. Исключения, генерируемые методом Create Метод Create, определенный в классе WebRequest, может генерировать четыре исключения. Так, если протокол, указываемый в префиксе URI, не поддерживается, то генерируется исключение NotSupportedException. Если формат URI оказывает ся недействительным, то генерируется исключение UriFormatException. А если у пользователя нет соответствующих полномочий для доступа к запрашиваемому сете вому ресурсу, то генерируется исключение System.Security.SecurityException. Кроме того, метод Create генерирует исключение ArgumentNullException, если он вызывается с пустой ссылкой, хотя этот вид ошибки не имеет непосредственного отношения к сетевому подключению. Исключения, генерируемые методом GetResponse При вызове метода GetResponse для получения ответа по протоколу HTTP может произойти целый ряд ошибок. Эти ошибки представлены следующими ис ключениями: InvalidOperationException, ProtocolViolationException, NotSupportedException и WebException. Наибольший интерес среди них вызывает исключение WebException. У исключения WebException имеются два свойства, связанных с сетевыми ошиб ками: Response и Status. С помощью свойства Response можно получить ссылку на объект типа WebResponse в обработчике исключений. Для соединения по протоколу HTTP этот объект описывает характер возникшей ошибки. Свойство Response объ является следующим образом. public WebResponse Response { get; } Когда возникает ошибка, то с помощью свойства Status типа WebException мож но выяснить, что именно произошло. Это свойство объявляется следующим образом: public WebExceptionStatus Status {get; } где WebExceptionStatus — это перечисление, которое содержит приведенные ниже значения. CacheEntryNotFound ConnectFailure ConnectionClosed KeepAliveFailure MessageLengthLimitExceeded NameResolutionFailure Pending PipelineFailure ProtocolError ProxyNameResolutionFailure ReceiveFailure RequestCanceled RequestProhibitedByCachePolicy RequestProhibitedByProxy SecureChannelFailure SendFailure ServerProtocolViolation Success Timeout TrustFailure UnknownError Как только будет выяснена причина ошибки, в программе могут быть предприня ты соответствующие действия. Исключения, генерируемые методом GetResponseStream Для соединения по протоколу HTTP метод GetResponseStream из класса WebResponse может сгенерировать исключение ProtocolViolationException, ко торое в целом означает, что в работе по указанному протоколу произошла ошибка. Что же касается метода GetResponseStream, то это означает, что ни один из дей ствительных ответных потоков недоступен. Исключение ObjectDisposedException генерируется в том случае, если ответ уже утилизирован. А исключение IOException, конечно, генерируется при ошибке чтения из потока, в зависимости от того, как орга низован ввод данных. Обработка исключений В приведенном ниже примере программы демонстрируется обработка всевозмож ных сетевых исключений, которые могут возникнуть в связи с выполнением програм мы из предыдущего примера, в которую теперь добавлены соответствующие обработ чики исключений. // Пример обработки сетевых исключений. using System; using System.Net; using System.IO; class NetExcDemo { static void Main { int ch; try { // Сначала создать объект запроса типа WebRequest по указанному URI. HttpWebRequest req = (HttpWebRequest) WebRequest.Create("http://www.McGraw-Hill.com"); // Затем отправить сформированный запрос и получить на него ответ. HttpWebResponse resp = (HttpWebResponse) req.GetResponse; // Получить из ответа поток ввода. Stream istrm = resp.GetResponseStream ; / А теперь прочитать и отобразить гипертекстовое содержимое, полученное по указанному URI. Это содержимое выводился на экран отдельными порциями по 400 символов. После каждой такой порции следует нажать клавишу , чтобы вывести на экран следующую порцию, состоящую из 400 символов. / for (int i=1; ; i++) { ch = istrm.ReadByte; if(ch == -1) break; Console.Write((char) ch); if((i%400)==0) { Console.Write ("\nНажмите клавишу ."); Console.ReadLine; } } // Закрыть ответный поток. При этом закрывается // также поток ввода 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("Ошибка ввода-вывода: " + exc.Message); } catch(System.Security.SecurityException exc) { Console.WriteLine("Исключение в связи с нарушением безопасности: " + exc.Message); } catch(InvalidOperationException exc) { Console.WriteLine("Недопустимая операция: " + exc.Message); } } } Теперь перехватываются все исключения, которые могут быть сгенерированы сете выми методами. Так, если изменить вызов метода Create следующим образом: WebRequest.Create("http://www.McGraw-Hill.com/moonrocket"); а затем перекомпилировать и еще раз выполнить программу, то в результате может быть выдано приведенное ниже сообщение об ошибке. Сетевая ошибка: Удаленный сервер возвратил ошибку: (404) Не найден. Код состояния: ProtocolError На веб-сайте по адресу www.McGraw-Hill.com отсутствует раздел moonrocket, и поэтому он не найден по указанному URI, что и подтверждает приведенный выше ре зультат. Ради краткости и ясности в программах большинства примеров из этой главы от сутствует полноценная обработка исключений. Но в реальных приложениях она про сто необходима. Класс Uri Как следует из табл. 26.1, метод WebRequest.Create существует в двух вариан тах. В одном варианте он принимает идентификатор URI в виде строки. Именно этот вариант и был использован в предыдущих примерах программ. А во втором вариан те этот метод принимает идентификатор URI в виде экземпляра объекта класса Uri, определенного в пространстве имен System. Класс Uri инкапсулирует идентифика тор URL Используя класс Uri, можно сформировать URI, чтобы затем передать этот идентификатор методу Create. Кроме того, идентификатор URI можно разделить на части. Для выполнения многих простых операций в Интернете класс Uri малопри годен. Тем не менее он может оказаться весьма полезным в более сложных ситуациях сетевого подключения к Интернету. В классе Uri определяется несколько конструкторов. Ниже приведены наиболее часто используемые конструкторы этого класса. public Uri(string uriString) public Uri(Uri baseUri, string relativeUri) В первой форме конструктора объект класса Uri создается по идентификатору URI, заданному в виде строки uriString. А во второй форме конструктора он создается по относительному URI, заданному в виде строки relativeUri относительно абсолютно го URI, обозначаемого в виде объекта baseUri типа Uri. Абсолютный URI определяет полный адрес URI, а относительный URI — только путь к искомому ресурсу. В классе Uri определяются многие поля, свойства и методы, оказывающие помощь в управлении идентификаторами URI или в получении доступа к различным частям URI. Особый интерес представляют приведенные ниже свойства. Перечисленные выше свойства полезны для разделения URI на составные части. При менение этих свойств демонстрируется в приведенном ниже примере программы. // Пример применения свойств из класса Uri. using System; using System.Net; class UriDemo { static void Main { Uri sample = new Uri("http://HerbSchildt.com/somefile.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); } } Эта программа дает следующий результат. Хост: HerbSchildt.com Порт: 80 Протокол: http Локальный путь: /somefile.txt Запрос: ?ScmeQuery Путь и запрос: /somefile.txt?SomeQuery Доступ к дополнительной информации, получаемой в ответ по протоколу HTTP С помощью сетевых средств, имеющихся в классе HttpWebResponse, можно по лучить доступ к другой информации, помимо содержимого указываемого ресурса. К этой информации, в частности, относится время последней модификации ресурса, а также имя сервера. Она оказывается доступной с помощью различных свойств, свя занных с подучаемым ответом. Все эти свойства, включая и те что, определены в классе WebResponse, сведены в табл. 26.5. В приведенных далее примерах программ демон стрируется применение этих свойств на практике. Свойство Описание public string Host { get; } Получает имя сервера public string LocalPath { get; } Получает локальный путь к файлу public string. PathAndQuery { get; } public int Port { get; } Получает абсолютный путь и строку запроса Получает номер порта для указанного про токола. Так, для протокола HTTP номер порта равен 80 public string Query { get; } Получает строку запроса public string Scheme { get; } Получает протокол Доступ к заголовку Для доступа к заголовку с информацией, получаемой в ответ по протоколу HTTP, служит свойство Headers, определенное в классе HttpWebResponse. public WebHeaderCollection Headers{ get; } Заголовок протокола HTTP состоит из пар "имя-значение", представленных строка ми. Каждая пара "имя-значение" хранится в коллекции класса WebHeaderCollection. Эта коллекция специально предназначена для хранения пар "имя-значение" и приме Таблица 26.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 bool IsFromCache { get; } Принимает логическое значение true, если запрос получен из кеша. А если запрос достав лен по сети, то принимает логическое значе ние false public bool IsMutuallyAuthenticated { get; } Принимает логическое значение true, если клиент и сервер опознают друг друга, а ина че — принимает логическое значение false public DateTime LastModified { get; } Получает время последней модификации ре сурса public string Method { get; } Получает строку, которая задает способ ответа public Version ProtocolVersion { get; } Получает объект типа Version, описываю щий версию протокола HTTP, используемую в транзакции public Uri ReponseUri { get; } Получает URI, по которому был сформирован ответ. Этот идентификатор может отличаться от запрашиваемого, если ответ был переадресо ван по другому URI public string Server { get; } Получает строку, обозначающую имя сервера public HttpStatusCode StatusCode { get; } Получает объект типа HttpStatusCode, опи сывающий состояние транзакции public string StatusDescription { get; } Получает строку, обозначающую состояние транзакции в удобочитаемой форме няется аналогично любой другой коллекции (подробнее об этом см. в главе 25). Стро ковый массив имен может быть получен из свойства AllKeys, а отдельные значения — по соответствующему имени при вызове метода GetValues. Этот метод возвращает массив строк, содержащий значения, связанные с заголовком, передаваемым в качестве аргумента. Метод GetValues перегружается, чтобы принять числовой индекс или имя заголовка. В приведенной ниже программе отображаются заголовки, связанные с сетевым ре сурсом, доступным по адресу www.McGraw-Hill.com. // Проверить заголовки. using System; using System.Net; class HeaderDemo { static void Main { // Создать объект запроса типа WebRequest по указанному URI. HttpWebRequest req = (HttpWebRequest) WebRequest.Create("http://www.McGraw-Hill.com"); // Отправить сформированный запрос и получить на него ответ. HttpWebResponse resp = (HttpWebResponse) req.GetResponse; // Получить список имен. string[] names = resp.Headers.AllKeys; // Отобразить пары "имя-значение" из заголовка. Console.WriteLine("{0,-20}(1}\n", "Имя", "Значение"); foreach(string n in names) { Console.Write("{0,-20}", n); foreach(string v in resp.Headers.GetValues(n)) Console.WriteLine(v); } // Закрыть ответный поток. resp.Close; } } Ниже приведен полученный результат. Не следует забывать, что информация в заго ловке периодически меняется, поэтому у вас результат может оказаться несколько иным. Имя Значение Transfer-encoding chunked Content-Type text/html Date Sun, 06 Dec 2009 20:32:06 GMT Server Sun-ONE-Web-Server/6.1 Доступ к cookie-наборам Для доступа к cookie-наборам, получаемым в ответ по протоколу HTTP, слу жит свойство Cookies, определенное в классе HttpWebResponse. В cookie-наборах содержится информация, сохраняемая браузером. Они состоят из пар "имя-значение" и упрощают некоторые виды доступа к веб-сайтам. Ниже показано, каким образом определяется свойство Cookies. public CookieCollection Cookies { get; set; } В классе CookieCollection реализуются интерфейсы ICollection и IEnumerable, и поэтому его можно использовать аналогично классу любой другой коллекции (подробнее об этом см. в главе 25). У этого класса имеется также индекса тор, позволяющий получать 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 содержит пустое значение. Для того чтобы воспользоваться cookie-наборами, необ ходимо установить это свойство равным экземпляру класса CookieContainer. Во многих приложениях свойство CookieContainer не применяется непосредственно, а вместо него из принятого ответа составляется и затем используется коллекция типа CookieCollection. Свойство CookieContainer просто обеспечивает внутренний механизм сохранения cookie-наборов. В приведенном ниже примере программы отображаются имена и значения cookie- наборов, получаемых из источника по URI, указываемому в командной строке. Следу ет, однако, иметь в виду, что cookie-наборы используются не на всех веб-сайтах, поэто му нужно еще найти такой веб-сайт, который поддерживает cookie-наборы. / Пример проверки cookie-наборов. Для того чтобы проверить, какие именно cookie-наборы используются на веб-сайте, укажите его имя в командной строке. Так, если назвать эту программу CookieDemo, то по команде CookieDemoотобразятся cookie-наборы с веб-сайта по адресу www.msn.com. / using System; using System.Net; class CookieDemo { static void Main(string[] args) { if(args.Length != 1) { Console.WriteLine("Применение: CookieDemo "); return; } // Создать объект запроса типа WebRequest по указанному URI. HttpWebRequest req = (HttpWebRequest) WebRequest.Create(args[0]); // Получить пустой контейнер. req.CookieContainer = new CookieContainer; // Отправить сформированный запрос и получить на него ответ. HttpWebResponse resp = (HttpWebResponse) req.GetResponse; // Отобразить cookie-наборы. Console.WriteLine("Количество cookie-наборов: " + resp.Cookies.Count); Console.WriteLine("{0,-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 Иногда требуется знать, когда именно сетевой ресурс был обновлен в последний раз. Это нетрудно сделать, пользуясь сетевыми средствами класса HttpWebResponse, среди которых определено свойство LastModified, приведенное ниже. public DateTime LastModified { get; } С помощью свойства LastModified получается время обновления содержимого се тевого ресурса в последний раз. В приведенном ниже примере программы отображаются дата и время, когда был в последний раз обновлен ресурс, указываемый по URI в командной строке. / Использовать свойство LastModified. Для того чтобы проверить дату последнего обновления веб-сайта, введите его URI в командной строке. Так, если назвать эту программу LastModifiedDemo, то для проверки даты последней модификации веб-сайта по адресу www.HerbSchildt.com введите команду LastModifiedDemo/ using System; using System.Net; class LastModifiedDemo { static void Main(string[] args) { if(args.Length != 1) { Console.WriteLine("Применение: LastModifiedDemo "); return; } HttpWebRequest req = (HttpWebRequest) WebRequest.Create (args[0]); HttpWebResponse resp = (HttpWebResponse) req.GetResponse; Console.WriteLine("Последняя модификация: " + resp.LastModified); resp.Close; } } Практический пример создания программы MiniCrawler Для того чтобы показать, насколько просто программировать для Интернета сред ствами классов WebRequest и WebReponse, обратимся к разработке скелетного ва рианта поискового робота под названием MiniCrawler. Поисковый робот представляет собой программу последовательного перехода от одной ссылки на сетевой ресурс к другой. Поисковые роботы применяются в поисковых механизмах для каталогизации содержимого. Разумеется, поисковый робот MiniCrawler не обладает такими развиты ми возможностями, как те, что применяются в поисковых механизмах. Эта программа начинается с ввода пользователем конкретного адреса URI, по которому затем читается содержимое и осуществляется поиск в нем ссылки. Если ссылка найдена, то программа запрашивает пользователя, желает ли он перейти по этой ссылке к обнаруженному се тевому ресурсу, найти другую ссылку на имеющейся странице или выйти из програм мы. Несмотря на всю простоту такого алгоритма поиска сетевых ресурсов, он служит интересным и наглядным примером доступа к Интернету средствами С#. Программе MiniCrawler присущ ряд ограничений. Во-первых, в ней обнаруживают ся только абсолютные ссылки, указываемые по гипертекстовой команде href="#"text_emphasis"> MiniCrawler: скелетный вариант поискового робота. Применение: для запуска поискового робота укажите URI в командной строке. Например, для того чтобы начать поиск с адреса www.McGraw-Hill.com, введите следующую команду: MiniCrawler/ using System; using System.Net; using System.IO; class MiniCrawler { // Найти ссылку в строке содержимого. static string FindLink(string htmlstr, ref int startloc) { int i; int start, end; string uri = null; i = htmlstr.IndexOf("href=\"http", startloc, StringComparison.OrdinalIgnoreCase); if(i != -1) { start = htmlstr.IndexOf('"', i) + 1; end = htmlstr.IndexOf('"', start); uri = htmlstr.Substring(start, end-start); startloc = end; } return uri; } static void Main(string[] args) { string link = null; string str; string answer; int curloc; // содержит текущее положение в ответе if(args.Length != 1) { Console.WriteLine("Применение: MiniCrawler "); return; } string uristr = args[0]; // содержит текущий URI HttpWebResponse resp = null; try { do { Console.WriteLine("Переход по ссылке " + uristr); // Создать объект запроса типа WebRequest по указанному URI. HttpWebRequest req = (HttpWebRequest) WebRequest.Create(uristr); uristr = null; // запретить дальнейшее использование этого URI // Отправить сформированный запрос и получить на него ответ. resp = (HttpWebResponse) req.GetResponse; // Получить поток ввода из принятого ответа. Stream istrm = resp.GetResponseStream; // Заключить поток ввода в оболочку класса StreamReader. StreamReader rdr = new StreamReader(istrm); // Прочитать всю страницу. 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.Equals(answer, "П", StringComparison.OrdinalIgnoreCase)) { uristr = string.Copy(link); break; } else if(string.Equals(answer, "B", StringComparison.OrdinallgnoreCase)) { break; } else if(string.Equals(answer, "И", StringComparison.OrdinallgnoreCase)) ( Console.WriteLine("Поиск следующей ссылки."); } } else { Console.WriteLine("Больше ссылок не найдено."); break; } } while(link.Length > 0); // Закрыть ответный поток. if(resp != null) 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("Ошибка ввода-вывода: " + exc.Message); } finally { if(resp != null) resp.Close; } Console.WriteLine("Завершение программы MiniCrawler."); } } Ниже приведен пример сеанса поиска, начиная с адреса www.McGraw-Hill.com. Следует иметь в виду, что конкретный результат поиска зависит от состояния содер жимого на момент поиска. Переход по ссылкеНайдена ссылка:Перейти по ссылке, Искать дальше, Выйти? И Поиск следующей ссылки. Найдена ссылка:Перейти по ссылке, Искать дальше, Выйти? П Переход по ссылкеНайдена ссылка:Перейти по ссылке, Искать дальше, Выйти? П Переход по ссылкеНайдена ссылка:Перейти по ссылке, Искать дальше. Выйти? В Завершение программы MiniCrawler. Рассмотрим подробнее работу программы MiniCrawler. Она начинается с ввода пользователем конкретного URI в командной строке. В методе Main этот URI сохра няется в строковой переменной uristr. Затем по указанному URI формируется запрос, и переменной uristr присваивается пустое значение, указывающее на то, что данный URI уже использован. Далее отправляется запрос и получается ответ. После этого со держимое читается из потока ввода, возвращаемого методом GetResponseStream и заключаемого в оболочку класса StreamReader. Для этой цели вызывается метод ReadToEnd, возвращающий все содержимое в виде строки из потока ввода. Далее программа осуществляет поиск ссылки в полученном содержимом. Для этого вызывается статический метод FindLink, определяемый в программе MiniCrawler. Этот метод вызывается со строкой содержимого и исходным положением, с которо го начинается поиск в полученном содержимом. Эти значения передаются методу FindLink в виде параметров htmlstr и startloc соответственно. Обратите внима ние на то, что параметр startloc относится к типу ref. Сначала в методе FindLink создается копия строки содержимого в нижнем регистре, а затем осуществляется по иск подстроки href="#"http://www.McGraw-Hill.com"; string fname = "data.txt"; try { Console.WriteLine("Загрузка данных по адресу " + uri + " в файл " + fname); user.DownloadFile(uri, fname); } catch (WebException exc) { Console.WriteLine(exc); } Console.WriteLine("Загрузка завершена."); } } Эта программа загружает информацию по адресу www.McGrawHill.com и поме щает ее в файл data.txt. Обратите внимание на строки кода этой программы, в кото рых осуществляется загрузка информации. Изменив символьную строку uri, можно загрузить информацию по любому адресу URI, включая и конкретные файлы, доступ ные по указываемому URL Несмотря на то что классы WebRequest и WebResponse предоставляют больше возможностей для управления и доступа к более обширной информации, для многих приложений оказывается достаточно и средств класса WebClient. Этим классом осо бенно удобно пользоваться в тех случаях, когда требуется только загрузка информации из веб-ресурса. Так, с помощью средств класса WebClient можно получить из Интер нета обновленную документацию на приложение.

ПРИЛОЖЕНИЕ. Краткий справочник по составлению документирующих комментариев

В языке С# предусмотрено три вида комментариев. К двум первым относятся комментарии // и / /, а третий основан на дескрипторах языка XML и назы вается документирующим комментарием. (Иногда его еще называют XML-комментарием.) Однострочный документи рующий комментарий начинается с символов ///, а много строчный начинается с символов / и оканчивается сим волами */. Строки после символов / могут начинаться с одного символа , хотя это и не обязательно. Если все по следующие строки многострочного комментария начина ются с символа , то этот символ игнорируется. Документирующие комментарии вводятся перед объяв лением таких элементов языка С#, как классы, пространства имен, методы, свойства и события. С помощью документи рующих комментариев можно вводить в исходный текст программы сведения о самой программе. При компиля ции программы документирующие комментарии к ней могут быть помещены в отдельный XML-файл. Кроме того, документирующие комментарии можно использовать в средстве IntelliSense интегрированной среды разработки Visual Studio. Дескрипторы XML-комментариев

В С# поддерживаются дескрипторы документации в формате XML, сведенные в табл. 1. Большинство дескрип торов XML-комментариев не требует особых пояснений 1040 Часть II. Библиотека C# и действуют подобно всем остальным дескрипторам XML, знакомым многим про граммистам. Тем не менее дескриптор — сложнее других. Он состоит из двух частей: заголовка и элементов списка. Ниже приведена общая форма дескриптора

:

имя

текст где текст описывает имя. Для описания таблиц текст не используется. Ниже при ведена общая форма элемента списка:

имя_элемента

текст где текст описывает имяэлемента. Для описания маркированных и нумерованных списков, а также таблиц имяэлемента не используется. Допускается применение не скольких элементов списка . Таблица 1. Дескрипторы XML-комментариев Дескриптор Описание <с> код </с> Определяет текст, на который указывает код, как программный код код Определяет несколько строк текста, на кото рый указывает код, как программный код

пояснение Определяет текст, на который указывает по яснение, как описание примера кода

пояснение Описывает исключительную ситуацию, на ко торую указывает имя

Определяет файл, содержащий XML-kom- ментарии для текущего исходного файла. При этом fname обозначает имя файла; path — путь к файлу; tagName — имя дескриптора; tagID — идентификатор дескриптора

<list type = "тип""> заголовок списка элементы списка Определяет список. При этом тип обозначает тип списка, который может быть маркирован ным, нумерованным или таблицей <раrа> текст Определяет абзац текста в другом дескрипторе

пояснение Документирует параметр, на который указы вает имя_параметра. Текст, обозначаемый как пояснение, описывает параметр

Обозначает имя_параметра как имя кон кретного параметра

пояснение Описывает параметр разрешения, связанный с членами класса, на которые указывает иден тификатор. Текст, обозначаемый как пояс нение, описывает параметры разрешения Окончание табл. 1 Компилирование документирующих комментариев Для получения XML-файла, содержащего документирующие комментарии, до статочно указать параметр /doc в командной строке компилятора. Например, для компилирования файла DocTest.cs, содержащего XML-комментарии, в командной строке необходимо ввести следующее. csc DocTest.cs /doc:DocTest.xml Для вывода результата в XML-файл из интегрированной среды разработки Visual Studio необходимо активизировать окно Свойства (Properties) для текущего проекта. Затем следует выбрать свойство Построение (Build), установить флажок XML-файл документации (XML Documentation File) и указать имя выходного XML-файла. Пример составления документации в формате XML В приведенном ниже примере демонстрируется применение нескольких докумен тирующих комментариев: как однострочных, так и многострочных. Любопытно, что многие программисты пользуются последовательным рядом однострочных докумен тирующих комментариев вместо многострочных, даже если комментарий занимает на сколько строк. Такой подход применяется и в ряде комментариев из данного примера. Его преимущество заключается в том, что он позволяет ясно обозначить каждую строку как часть длинного документирующего комментария. Но это все же, скорее, дело стиля, чем общепринятая практика составления документирующих комментариев. Дескриптор Описание

пояснение Текст, обозначаемый как пояснение, пред ставляет собой общие комментарии, которые часто используются для описания класса или структуры

пояснение Текст, обозначаемый как пояснение, описы вает значение, возвращаемое методом

Объявляет ссылку на другой элемент, обозна чаемый как идентификатор

Объявляет ссылку типа “см. также” на иден тификатор

пояснение Текст, обозначаемый как пояснение, пред ставляет собой общие комментарии, которые часто используются для описания метода или другого члена класса

Поделиться:
Популярные книги

Начальник милиции. Книга 5

Дамиров Рафаэль
5. Начальник милиции
Фантастика:
попаданцы
альтернативная история
5.00
рейтинг книги
Начальник милиции. Книга 5

Папина дочка

Рам Янка
4. Самбисты
Любовные романы:
современные любовные романы
5.00
рейтинг книги
Папина дочка

Возвышение Меркурия. Книга 8

Кронос Александр
8. Меркурий
Фантастика:
героическая фантастика
попаданцы
аниме
5.00
рейтинг книги
Возвышение Меркурия. Книга 8

АллатРа

Новых Анастасия
Научно-образовательная:
психология
история
философия
обществознание
физика
6.25
рейтинг книги
АллатРа

Золушка вне правил

Шах Ольга
Любовные романы:
любовно-фантастические романы
6.83
рейтинг книги
Золушка вне правил

Возвышение Меркурия. Книга 5

Кронос Александр
5. Меркурий
Фантастика:
боевая фантастика
попаданцы
аниме
5.00
рейтинг книги
Возвышение Меркурия. Книга 5

На Ларэде

Кронос Александр
3. Лэрн
Фантастика:
фэнтези
героическая фантастика
стимпанк
5.00
рейтинг книги
На Ларэде

Барон Дубов 6

Карелин Сергей Витальевич
6. Его Дубейшество
Фантастика:
юмористическое фэнтези
аниме
сказочная фантастика
фэнтези
5.00
рейтинг книги
Барон Дубов 6

Ротмистр Гордеев

Дашко Дмитрий Николаевич
1. Ротмистр Гордеев
Фантастика:
фэнтези
попаданцы
альтернативная история
5.00
рейтинг книги
Ротмистр Гордеев

Кодекс Крови. Книга VI

Борзых М.
6. РОС: Кодекс Крови
Фантастика:
фэнтези
попаданцы
аниме
5.00
рейтинг книги
Кодекс Крови. Книга VI

Лучше подавать холодным

Аберкромби Джо
4. Земной круг. Первый Закон
Фантастика:
фэнтези
8.45
рейтинг книги
Лучше подавать холодным

Звездная Кровь. Изгой

Елисеев Алексей Станиславович
1. Звездная Кровь. Изгой
Фантастика:
боевая фантастика
попаданцы
рпг
5.00
рейтинг книги
Звездная Кровь. Изгой

Крестоносец

Ланцов Михаил Алексеевич
7. Помещик
Фантастика:
героическая фантастика
попаданцы
альтернативная история
5.00
рейтинг книги
Крестоносец

Сердце Дракона. Том 12

Клеванский Кирилл Сергеевич
12. Сердце дракона
Фантастика:
фэнтези
героическая фантастика
боевая фантастика
7.29
рейтинг книги
Сердце Дракона. Том 12