C# TcpListener - сброс соединения (создание веб-сервера)
В других браузерах ситуация не лучше. Также пробовал через одну прогу-генератор HTTP-запросов - выдает мне в 100% случаев Connection refused.
Фаервол не влияет, включал/выключал - эффекта ноль. Какой-то закономерности в успешной отдаче данных не нашел.
Код запуска сервера (слушателя):
_is_shutdown = false;
_listener = new TcpListener(IPAddress.Any, 100);
_listener.Start();
_ThreadListener = new Thread(this.Listen);
_ThreadListener.Start();
}
Код метода Listen (он просто выдает стандартный HTTP/1.1 200 OK):
{
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
{
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();
}
}
}
Из интереса переписал данный пример на Java. Результат тот же самый, кажись проблема где-то даже не в коде, а вообще в подходе.
Из интереса переписал данный пример на Java. Результат тот же самый, кажись проблема где-то даже не в коде, а вообще в подходе.
Ну, наверное стоит прочитать, что клиент туда написал? ;)
Код метода Listen (он просто выдает стандартный HTTP/1.1 200 OK):
{
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 в данном случае):
{
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);
}
}
}
Первый подобный опыт работы с сетевыми приложениями даёт знать.
Спс harcase (блин, с меня точно бутылка, уже не в первый раз помогаешь).
В принципе, я бы мог и догадаться, что надо было считать сначала, а потом писать ответ %) Просто здесь приведён урезанный (для простоты) отрывок кода, а так-то код значительно больше, а там я считывал весь запрос, но не до конца.
Это получается, чтобы работало так, как я раньше сделал - надо было BeginRead/Write юзать? Ну это уже вопрос из интереса.
Upd.: Хмм, про IDisposable почитаю, такого не знал (да как и многого под .net)
Это получается, чтобы работало так, как я раньше сделал - надо было BeginRead/Write юзать? Ну это уже вопрос из интереса.
BeginXXX начинает асинхронную операцию - т.е. операцию происходящую в другом потоке управления (нитке).
А чтобы работало нужно некоторое количество данных прочитать из стрима. В твоем случае можно навесить StreamReader:
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() - прочитать запрос построчно (это не безопасно, но в простом случае допустимо).
В цикле (прослушивания) я получаю TcpClient client, затем создаю экземпляр класса-обработчика запроса, передавая ему client и ссылку на сервер. Затем в конструкторе того класса-обработчика я создаю новый поток, проблема в том, что в потоке client становится равным null.
Прослушка:
{
// 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:
{
var ns = _client.GetStream(); // System.NullReferenceException
...
}
Вообщем, как видно, в последнем коде вылетает System.NullReferenceException
Вообще
Кстати, вручную изготавливать Thread не очень хорошая практика (это долгая операция) - лучше использовать потоки из пула. Это можно делать явно - через класс ThreadPool, либо посредством асинхронных операций.
вот пример простого многопоточного TCP-сервера из реальной утиллитки, писался мною несколько лет назад, на шедевр не претендует, но идею многопоточности демонстрирует:
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);
}
}
}
}