Опубликован: 12.02.2013 | Доступ: свободный | Студентов: 791 / 47 | Длительность: 17:51:00
Специальности: Программист
Лекция 7:

Средства Windows Phone для работы с сетью

17.3. Создание подключения по протоколу TCP

Проблема использования протокола UDP заключается в том, что система, которая передаёт информацию по этому протоколу, не знает, принял ли эту информацию получатель. Если необходимо создать надёжное подключение, нужно использовать протокол TCP.

Протокол TCP позволяет создавать сеансы подключения между двумя устройствами. Сообщения создаются и передаются точно так же, как и при использовании протокола UDP, но каждое сообщение является частью сеанса. Сеанс будет продолжаться, пока одна из сторон не решит его прекратить. Ответ, который передаётся после приёма сообщения, позволяет программам определить, успешно ли было передано сообщение. В конце сеанса одна из сторон закрывает подключение.

Рассмотрим процесс создания метода для запроса веб-страницы:

private string RequestWebPage(string url, string page, out string pageContent)

Этот метод принимает в качестве параметров адрес веб-сервера, имя запрашиваемой страницы, и в третий параметр заносится содержимое веб-страницы. Метод возвращает пустую строку, если запрос был выполнен успешно, или описание ошибки, если запрос завершился неудачей.

В Интернете используется протокол передачи гипертекста HTTP. Этот протокол определяет язык, используемый браузером для запроса страниц содержимого веб-сервера. Например, чтобы получить с сервера веб-страницу, браузер отправляет команду GET и адрес веб-страницы на сервере.

Поскольку важно, чтобы все части веб-страницы были доставлены клиенту, протокол HTTP использует TCP-подключение. Для получения веб-страницы программа должна выполнить следующие действия:

  1. Создать TCP-подключение к веб-серверу, используя порт 80, который зарезервирован для HTTP-запросов.
  2. Отправить веб-серверу запрос GET, в котором указать нужную страницу.
  3. Получить содержимое запрошенной веб-страницы. Если страница большая, может потребоваться несколько операций чтения.
  4. Закрыть подключение.

Создание TCP-подключения

Подключение по протоколу TCP создаётся аналогично рассмотренному ранее примеру.

private string ConnectTCP(string host, int port)
{
    string response = "Истекло время ожидания";

    DnsEndPoint hostEntry = new DnsEndPoint(host, port);

    hostSocket = new Socket(AddressFamily.InterNetwork, SocketType.Stream,
        ProtocolType.Tcp);

    SocketAsyncEventArgs socketEventArgs = new SocketAsyncEventArgs();
    socketEventArgs.RemoteEndPoint = hostEntry;

    socketEventArgs.Completed += new EventHandler<SocketAsyncEventArgs>
        (delegate(object s, SocketAsyncEventArgs e)
    {
        if (e.SocketError == SocketError.Success)
        {
            response = "";
        }
        else
        {
            // получить результат запроса
            response = e.SocketError.ToString();
            // очистить параметры подключения
            hostSocket = null;
        }
        transferDoneFlag.Set();
    });

    transferDoneFlag.Reset();

    hostSocket.ConnectAsync(socketEventArgs);

    transferDoneFlag.WaitOne(MESSAGE_TIMEOUT_MSECS);
    return response;
}

В отличие от предыдущего примера, в этом методе вызывается метод ConnectAsync класса Socket. Если этот метод выполнится успешно, то он создаст настроенное TCP-подключение к удаленной системе, с помощью которого можно будет отправлять и получать сообщения, пока подключение не будет закрыто. Однако, подключение создаётся для передачи только одной веб-страницы. Если пользователь перейдёт на другую страницу, необходимо будет создать новое TCP-подключение к другой странице.

Отправка сообщения через TCP-подключение

Далее нужно сформировать текст сообщения и отправить его через созданное подключение:

private string SendMessageTCP(string message)
{
    if (hostSocket == null)
        return "Подключение не установлено";

    string response = "Тайм-аут подключения";
  
    SocketAsyncEventArgs socketEventArg = new SocketAsyncEventArgs();

    // использование параметров конечной точки для настройки передачи сообщения
    socketEventArg.RemoteEndPoint = hostSocket.RemoteEndPoint;

    byte[] messageBytes = Encoding.UTF8.GetBytes(message);
    socketEventArg.SetBuffer(messageBytes, 0, messageBytes.Length);

    socketEventArg.Completed += new EventHandler<SocketAsyncEventArgs>
        (delegate(object s, SocketAsyncEventArgs e)
    {
        if (e.SocketError == SocketError.Success)
        {
            response = "";
        }
        else
        {
            response = e.SocketError.ToString();
            CloseTCP();
        }

        transferDoneFlag.Set();
    });

    transferDoneFlag.Reset();

    hostSocket.SendAsync(socketEventArg);

    transferDoneFlag.WaitOne(MESSAGE_TIMEOUT_MSECS);

    return response;
}

Этот код почти такой же, как и для отправки сообщения по UDP-подключению. Перед передачей сообщения проверяется, установлено ли подключение.

Если в процессе передачи произошла ошибка, подключение закрывается методом CloseTCP:

void CloseTCP()
{
    if (hostSocket != null)
    {
        hostSocket.Close();
        hostSocket = null;
    }
}

Получение сообщения через TCP-подключение

Для получения содержимого веб-страницы используется код, который аналогичен получению сообщения через UDP-подключение:

private string ReceiveMessageTCP(out string message)
{
    message = "";

    if (hostSocket == null)
    {
        return "Подключение не установлено";
    }

    string response = "Тайм-аут подключения";
    string result = "";

    SocketAsyncEventArgs socketEventArg = new SocketAsyncEventArgs();
    socketEventArg.RemoteEndPoint = hostSocket.RemoteEndPoint;

    // создания буфера для получения сообщения
    byte[] responseBytes = new Byte[MAX_BUFFER_SIZE];
    socketEventArg.SetBuffer(responseBytes, 0, MAX_BUFFER_SIZE);

    socketEventArg.Completed += new EventHandler<SocketAsyncEventArgs>
        (delegate(object s, SocketAsyncEventArgs e)
    {
        if (e.SocketError == SocketError.Success)
        {
            response = "";
            if (e.BytesTransferred > 0)
            {
                result = Encoding.UTF8.GetString(e.Buffer, e.Offset,
                    e.BytesTransferred);
                result = result.Trim('\0');
            }
        }
        else
        {
            response = e.SocketError.ToString();
        }
        transferDoneFlag.Set();
    });

    transferDoneFlag.Reset();

    hostSocket.ReceiveAsync(socketEventArg);

    transferDoneFlag.WaitOne(MESSAGE_TIMEOUT_MSECS);

    message = result;

    return response;
}

Эта версия метода проверяет, передал ли сервер какую-либо информацию, и если да, то декодирует полученную информацию и возвращает её в виде строки.

Создание запроса

Теперь можно использовать созданные методы для построения запроса веб-страницы. Метод отправит запрос и получит содержимое веб-страницы. Об окончании передачи свидетельствует получение пустого результата от метода ReceiveMessageTCP.

private string RequestWebPage(string url, string page, out string pageContent)
{
    pageContent = "";

    // создание подключения
    string response = ConnectTCP(url, 80);

    if (response != "")
    {
        return response;
    }

    // отправка запроса страницы с помощью HTTP-команды GET
    response = SendMessageTCP("GET " + page + " HTTP/1.1\r\nHost: " + url +
        "\r\nConnection: Close\r\n\r\n");

    if (response != "")
    {
        CloseTCP();
        return response;
    }

    string wholePage = "";
    string fetchText;

    // повторение получения пакетов от сервера
    // до тех пор, пока метод не вернёт пустую строку
    do
    {  
        response = ReceiveMessageTCP(out fetchText);

        if (response != "")
        {
            CloseTCP();
            return response;
        }

        wholePage += fetchText;
    }
    while (fetchText != "");

    pageContent = wholePage;

    CloseTCP();

    return response;
}

Этот метод возвращает полный текст запрошенной веб-страницы. Если какой-либо метод вернёт сообщение об ошибке, метод RequestWebPage закроет TCP-подключение и завершит работу. Код метода CloseTCP для закрытия подключения выглядит так:

void CloseTCP()
{
    if (hostSocket != null)
    {
        hostSocket.Close();
        hostSocket = null;
    }
}

Если в приложении вызывать созданный метод при нажатии на кнопку, то событие нажатия на кнопку не завершится до тех пор, пока не будет получено всё содержимое страницы или сообщение об ошибке. При этом, пользователь не сможет в это время выполнять другие действия. Если необходимо разрешить пользователю параллельно выполнять другую работу, рекомендуется выполнять загрузку страницы в отдельном потоке.