Справочник функций

Ваш аккаунт

Войти через: 
Забыли пароль?
Регистрация
Информацию о новых материалах можно получать и без регистрации:

Почтовая рассылка

Подписчиков: -1
Последний выпуск: 19.06.2015

C# TcpListener - сброс соединения (создание веб-сервера)

244
30 сентября 2010 года
UAS
2.0K / / 19.07.2006
Вообщем, создаю простенький HTTP веб-сервер. Проблема в том, что при подключении к нему через браузер (Mozilla) в 70-80% случаях происходит вывод сообщения:
Цитата:
Соединение было сброшено. Во время загрузки страницы соединение с сервером было сброшено.


В других браузерах ситуация не лучше. Также пробовал через одну прогу-генератор HTTP-запросов - выдает мне в 100% случаев Connection refused.
Фаервол не влияет, включал/выключал - эффекта ноль. Какой-то закономерности в успешной отдаче данных не нашел.

Код запуска сервера (слушателя):

 
Код:
public void Start() {
    _is_shutdown = false;
    _listener = new TcpListener(IPAddress.Any, 100);
    _listener.Start();

    _ThreadListener = new Thread(this.Listen);
    _ThreadListener.Start();
}


Код метода Listen (он просто выдает стандартный HTTP/1.1 200 OK):
Код:
protected void Listen()
{
    while (!_is_shutdown)
    {
        var client = _listener.AcceptTcpClient();

        // указываем код состояния
        byte[] responseStatus = Encoding.ASCII.GetBytes("HTTP/1.1 200 OK\r\n");
        client.GetStream().Write(responseStatus, 0, responseStatus.Length);
        responseStatus = null;

        // конец
        client.GetStream().Write(Encoding.ASCII.GetBytes("\r\n"),0,2);
        client.Close();
    }
}


Пробовал вместо TcpClient использовать Socket - эффект тот же самый.
К серверу обращаюсь по адресу, естественно, 127.0.0.1:100

Target Framework: .Net Framework 3.5
MS VS 2008
9.0K
30 сентября 2010 года
t-34
129 / / 30.11.2007
Попробуйте так:
Код:
protected void Listen()
{
    while (!_is_shutdown)
    {
        if (_listener.Pending())
        {
        var client = _listener.AcceptTcpClient();

        // указываем код состояния
        byte[] responseStatus = Encoding.ASCII.GetBytes("HTTP/1.1 200 OK\r\n");
        client.GetStream().Write(responseStatus, 0, responseStatus.Length);
        responseStatus = null;

        // конец
        client.GetStream().Write(Encoding.ASCII.GetBytes("\r\n"),0,2);
        client.Close();
        }
    }
}
244
30 сентября 2010 года
UAS
2.0K / / 19.07.2006
К сожалению, не помогает. Ни _listener.Pending(), ни client.Connected.

Из интереса переписал данный пример на Java. Результат тот же самый, кажись проблема где-то даже не в коде, а вообще в подходе.
5
01 октября 2010 года
hardcase
4.5K / / 09.08.2005
Цитата: UAS
К сожалению, не помогает. Ни _listener.Pending(), ни client.Connected.

Из интереса переписал данный пример на Java. Результат тот же самый, кажись проблема где-то даже не в коде, а вообще в подходе.


Ну, наверное стоит прочитать, что клиент туда написал? ;)

5
01 октября 2010 года
hardcase
4.5K / / 09.08.2005
Цитата: UAS

Код метода Listen (он просто выдает стандартный HTTP/1.1 200 OK):
Код:
protected void Listen()
{
    while (!_is_shutdown)
    {
        var client = _listener.AcceptTcpClient();

        // указываем код состояния
        byte[] responseStatus = Encoding.ASCII.GetBytes("HTTP/1.1 200 OK\r\n");
        client.GetStream().Write(responseStatus, 0, responseStatus.Length);
        responseStatus = null;  [COLOR="Red"]// зачем?[/COLOR]

        // конец
        client.GetStream().Write(Encoding.ASCII.GetBytes("\r\n"),0,2);
        client.Close();
    }
}


Пробовал вместо TcpClient использовать Socket - эффект тот же самый.
К серверу обращаюсь по адресу, естественно, 127.0.0.1:100

Target Framework: .Net Framework 3.5
MS VS 2008


Ну и стоит использовать C# и IDIsposable контракт (позволяет не говорить Close в данном случае):

Код:
protected void Listen()
{
    while (!_is_shutdown)
    {
        using(var client = _listener.AcceptTcpClient())
        using(var stream = client.GetStream())
        {
            // указываем код состояния
            byte[] responseStatus = Encoding.ASCII.GetBytes("HTTP/1.1 200 OK\r\n");
            stream.Write(responseStatus, 0, responseStatus.Length);

            // конец
            stream.Write(Encoding.ASCII.GetBytes("\r\n"),0,2);
        }
    }
}
244
01 октября 2010 года
UAS
2.0K / / 19.07.2006
Твою же... Я над этим уже хз сколько часов сижу.
Первый подобный опыт работы с сетевыми приложениями даёт знать.
Спс harcase (блин, с меня точно бутылка, уже не в первый раз помогаешь).

В принципе, я бы мог и догадаться, что надо было считать сначала, а потом писать ответ %) Просто здесь приведён урезанный (для простоты) отрывок кода, а так-то код значительно больше, а там я считывал весь запрос, но не до конца.

Это получается, чтобы работало так, как я раньше сделал - надо было BeginRead/Write юзать? Ну это уже вопрос из интереса.

Upd.: Хмм, про IDisposable почитаю, такого не знал (да как и многого под .net)
5
01 октября 2010 года
hardcase
4.5K / / 09.08.2005
Цитата: UAS

Это получается, чтобы работало так, как я раньше сделал - надо было BeginRead/Write юзать? Ну это уже вопрос из интереса.


BeginXXX начинает асинхронную операцию - т.е. операцию происходящую в другом потоке управления (нитке).
А чтобы работало нужно некоторое количество данных прочитать из стрима. В твоем случае можно навесить StreamReader:

Код:
using(var client = _listener.AcceptTcpClient())
        using(var stream = client.GetStream())
        using(var reader = new StreamReader(stream, Encoding.ASCI))
        using(var writer = new StreamWriter(stream, Encoding.ASCI))  // аналогично StreamWriter,
                                                            // после этого можно писать строки ответа без ручкой перекодировки
        {
            // указываем код состояния
            writer.Write("HTTP/1.1 200 OK\r\n");
            // конец
            writer.Write("\r\n");

            writer.Flush();
        }

и несколько раз сделать ReadLine() - прочитать запрос построчно (это не безопасно, но в простом случае допустимо).
244
01 октября 2010 года
UAS
2.0K / / 19.07.2006
Можно тогда ещё один вопрос?
В цикле (прослушивания) я получаю TcpClient client, затем создаю экземпляр класса-обработчика запроса, передавая ему client и ссылку на сервер. Затем в конструкторе того класса-обработчика я создаю новый поток, проблема в том, что в потоке client становится равным null.

Прослушка:
 
Код:
while (!_is_shutdown)
{
    // this - ссылка на текущий объект-сервер
    var response = new HTTPRequestWorker(_listener.AcceptTcpClient(), this);
}


Обработка запроса:
Код:
// конструктор
public HTTPRequestWorker(TcpClient client, HTTPServer server)
{
    // private-переменные для работы внутри класса HTTPRequestWorker
    _client = client;
    _server = server;

    //ProccessRequest(); - метод, занимающийся обработкой и отсылкой рез-ов, закрытия соединений
    Thread t = new Thread(ProccessRequest);
    t.Name = "Request Proccessing";
    t.Start();
}


Метод ProccessRequest:
 
Код:
public void ProccessRequest()
{
    var ns = _client.GetStream(); // System.NullReferenceException
    ...
}


Вообщем, как видно, в последнем коде вылетает System.NullReferenceException
Вообще
244
01 октября 2010 года
UAS
2.0K / / 19.07.2006
Хммм, магическим образом все почему-то заработало. Вопрос выше снимается
5
02 октября 2010 года
hardcase
4.5K / / 09.08.2005
Цитата: UAS
Хммм, магическим образом все почему-то заработало. Вопрос выше снимается



Кстати, вручную изготавливать Thread не очень хорошая практика (это долгая операция) - лучше использовать потоки из пула. Это можно делать явно - через класс ThreadPool, либо посредством асинхронных операций.
вот пример простого многопоточного TCP-сервера из реальной утиллитки, писался мною несколько лет назад, на шедевр не претендует, но идею многопоточности демонстрирует:

Код:
using System;
using System.Collections.Generic;
using System.Text;
using Lanpolis.Netlife.Arp;
using System.Net;
using System.Net.Sockets;
using System.IO;
using System.Threading;
using Lanpolis.Netlife.Common.Utils;

namespace Lanpolis.Netlife {
   
    public sealed class NetlifeNetworkProvider : NetlifeSourceProvider {

        public NetlifeNetworkProvider(ArpCacheEngine cache_engine)
            : base(cache_engine) {
            // заполняем список допустимых адресов клиентов
            _allowed_clients.AddRange(Lanpolis.Netlife.Common.NetlifeConfig.Config.AllowedClientIps);

           
            // создаем TcpListener-ы из конфига
            Console.Write("[{0}] Server initialized with following endpoints: ", DateTime.Now);
            List<IPEndPoint> ep_list = new List<IPEndPoint>();
            foreach (string ep_str in Lanpolis.Netlife.Common.NetlifeConfig.Config.SourceEndpointValues) {
                IPEndPoint ep;
                if (Helpers.TryParseEndpoint(ep_str, out ep)) {
                    if (!ep_list.Contains(ep)) {
                        ep_list.Add(ep);
                        TcpListener listener = new TcpListener(ep);
                        _listeners.Add(listener);
                        Console.Write("{0} ", ep.ToString());
                    }
                }
            }
            Console.WriteLine();        
        }

        private bool _listening = false;
        public override bool Listening {
            get {
                return _listening;
            }
        }

        public const int BACKLOG = 30; // очередь TCP-соединений

        private List<TcpListener> _listeners = new List<TcpListener>();

        private List<string> _allowed_clients = new List<string>();

        public override void Start() {
            if (!_listening) {
                // запускаем каждый TcpListener
                Console.WriteLine("[{0}] Starting server...", DateTime.Now);
                foreach (TcpListener listener in _listeners) {
                    listener.Start(BACKLOG);
                    BeginAcceptSocket(listener);
                }
                _listening = true;
                Console.WriteLine("[{0}] Server started.", DateTime.Now);
            }
        }

        public override void Stop() {
            if (_listening) {
                // останавливаем TcpListener-ы
                Console.WriteLine("[{0}] Stopping server...", DateTime.Now);
                foreach (TcpListener listener in _listeners) {
                    listener.Stop();
                }
                _listening = false;
                Console.WriteLine("[{0}] Server stopped.", DateTime.Now);
            }
        }

        private void BeginAcceptSocket(TcpListener listener) {
            // запускаем асинхронную операцию ожидания клиента
            // фактическое ожидание происходит в отдельном потоке
            listener.BeginAcceptSocket(new AsyncCallback(ClientReceivedCallback), listener);
        }

        private bool EndAcceptSocket(IAsyncResult async_result, TcpListener listener, out Socket socket) {
            try {
                // завершаем операцию ожидания клиента -
                //   т.е. фактически блокируем выполнение текущего потока до момента подключения
                //   клиента
                socket = listener.EndAcceptSocket(async_result);
                return true;
            } catch (ObjectDisposedException) {
                socket = null;
                return false;
            }
        }

        private long client_counter = 0;

        private void ClientReceivedCallback(IAsyncResult async_result) {
            TcpListener listener = (TcpListener)async_result.AsyncState;
            Socket socket;
            if (EndAcceptSocket(async_result, listener, out socket)) {
                // одного клиента получили
                // запускаем ожидание следующего
                if (_listening)
                    BeginAcceptSocket(listener);

                long client_number = Interlocked.Increment(ref client_counter);
                string client_id = socket.RemoteEndPoint.ToString();
                DateTime start_time = DateTime.Now;
                Console.WriteLine("[{0}] Source client #{1} connected from {2}.", start_time, client_number, client_id);

                string client_address = ((IPEndPoint)socket.RemoteEndPoint).Address.ToString();
                if (!_allowed_clients.Contains(client_address)) {
                    Console.WriteLine("[{0}] Source client #{1} ({2}) rejected by address.", start_time, client_number, client_id);
                    return;
                }

                try {
                    // изготавливаем NetworkStream
                    //   и запускаем общение с клиентом
                    using (NetworkStream stream = new NetworkStream(socket, true))
                    //using (BufferedStream buffer = new BufferedStream(stream, 1024))
                    using (StreamReader reader = new StreamReader(stream)) {
                        string header = reader.ReadLine();
                        ProcessClient(client_id, header, reader);
                    }
                } catch (ThreadAbortException) {
                    Console.WriteLine("[{0}] Thread termination detected.", DateTime.Now);
                } catch (Exception error) {
                    Console.WriteLine("[{0}] {1}", DateTime.Now, error.Message);
                } finally {
                    DateTime end_time = DateTime.Now;
                    TimeSpan time_spent = end_time - start_time;
                    Console.WriteLine("[{0}] Time spent for client #{1} ({2}): {3} sec.", end_time, client_number, client_id, time_spent.TotalSeconds);
                    Console.WriteLine("[{0}] Source client #{1} ({2}) disconnected.", end_time, client_number, client_id);
                }
            } else {
                Console.WriteLine("[{0}] Socket has closed, stopping thread.", DateTime.Now);
            }
        }
    }
}
Реклама на сайте | Обмен ссылками | Ссылки | Экспорт (RSS) | Контакты
Добавить статью | Добавить исходник | Добавить хостинг-провайдера | Добавить сайт в каталог