Csabatom Ответов: 2

Если клиент завершает работу, сервер продолжает выполнять метод clientlistener. Как решить эту проблему?


Сервер запускается, и клиент подключается, он многопоточен, и главное-транслировать сообщение, чтобы оно попало на сервер. Однако, когда а клиент отключается(принудительно закрывается), он все еще пытается запустить метод ClientListener(подчеркнуто)

СЕРВЕРНЫЙ КОД:
using System;
using System.Collections.Generic;
using System.Net;
using System.Net.Sockets;
using System.IO;
using System.Text;
using System.Threading;

namespace Server
{
    class Program
    {
        private static TcpListener tcpListener;
        private static List<TcpClient> tcpClientsList = new List<TcpClient>();
        private static Boolean wroteshutdownonce = false;

        static void Main(string[] args)
        {
            Console.Write("Please input, what the port should be: ");
            int port = Convert.ToInt32(Console.ReadLine());

            tcpListener = new TcpListener(IPAddress.Any, port);
            tcpListener.Start();

            Console.Write(time());
            Console.ForegroundColor = ConsoleColor.White;
            Console.WriteLine("Server started");

            while (true)
            {
                TcpClient tcpClient = tcpListener.AcceptTcpClient();
                tcpClientsList.Add(tcpClient);

                Thread thread = new Thread(ClientListener);
                thread.Start(tcpClient);
                wroteshutdownonce = false;
            }
        }

        public static void ClientListener(object obj)
        {
            TcpClient tcpClient = (TcpClient)obj;
            StreamReader reader = new StreamReader(tcpClient.GetStream());

            Console.Write(time());
            Console.ForegroundColor = ConsoleColor.White;
            Console.WriteLine("Client connected");
            while (true)
            {
                //try
                //{
                    string message = reader.ReadLine();
                    BroadCast(message, tcpClient);
                    Console.Write(time());
                    Console.ForegroundColor = ConsoleColor.White;
                    Console.WriteLine(message);
                //} catch
                //{
                    /*if (wroteshutdownonce == false)
                    {
                        Console.ForegroundColor = ConsoleColor.DarkRed;
                        Console.WriteLine("One Client has been shut down!!");
                        Console.ForegroundColor = ConsoleColor.White;
                        tcpClient.Close();
                        wroteshutdownonce = true;
                    }*/
                //}
            }
        }

        public static void BroadCast(string msg, TcpClient excludeClient)
        {
            foreach (TcpClient client in tcpClientsList)
            {
                if (client != excludeClient)
                {
                    StreamWriter sWriter = new StreamWriter(client.GetStream());
                    sWriter.WriteLine(msg);
                    sWriter.Flush();
                }
            }
        }
        static string time()
        {
            String datetime = Convert.ToString(DateTime.Now);
            string[] splittedTime = datetime.Split(' ');
            datetime = "[" + splittedTime[0] + splittedTime[1] + splittedTime[2] + " " + splittedTime[3] + "] ";
            Console.ForegroundColor = ConsoleColor.Green;
            return datetime;
        }
    }
}


КЛИЕНТСКИЙ КОД:
using System;
using System.Collections.Generic;
using System.Net;
using System.Net.Sockets;
using System.IO;
using System.Text;
using System.Threading;

namespace BroadcastClient
{
    class Program
    {
        static void Main(string[] args)
        {
            Console.Write("Type in your username: ");
            String username = Console.ReadLine();
            try
            {
                TcpClient tcpClient = new TcpClient("127.0.0.1", 5001);
                Console.WriteLine("Connected to server.");
                Console.WriteLine("");

                Thread thread = new Thread(Read);
                thread.Start(tcpClient);

                StreamWriter sWriter = new StreamWriter(tcpClient.GetStream());

                while (true)
                {
                    if (tcpClient.Connected)
                    {
                        string input = Console.ReadLine();
                        sWriter.WriteLine(username+";" + input);
                        sWriter.Flush();
                    }
                }

            }
            catch (Exception e)
            {
                Console.Write(e.Message);
            }

            Console.ReadKey();
        }

        static void Read(object obj)
        {
            TcpClient tcpClient = (TcpClient)obj;
            StreamReader sReader = new StreamReader(tcpClient.GetStream());

            while (true)
            {
                try
                {
                    string message = sReader.ReadLine();
                    Console.WriteLine(message);
                }
                catch (Exception e)
                {
                    Console.WriteLine(e.Message);
                    break;
                }
            }
        }
    }
}


Что я уже пробовал:

Он продолжает сбрасывать следующую ошибку:
Системы.ИО.Исключение IOException
HRESULT = 0x80131620
Сообщение = не удается прочитать транспортное соединение: существующее соединение было принудительно закрыто удаленным хостом.
исходная система
Трассировка стека:
в System.Net.Sockets.Сетевой поток.Read (Byte [] buffer, Int32 offset, Int32 size)
в System.IO.StreamReader.ReadBuffer ()
в системе.ИО.Что streamreader.С readline ()
на сервере.Программа.ClientListener (Object obj) в C: \ Users \ username \ Desktop \ C # Broadcasting \ BroadcastServer \ Program.cs: строка 52
в системе.Нарезание резьбы.ThreadHelper.ThreadStart_Context (состояние объекта)
в системе.Нарезание резьбы.Параллельном режиме.RunInternal (параллельном режиме параллельном режиме, ContextCallback обратного вызова, состояние объекта, логическое preserveSyncCtx)
в системе.Нарезание резьбы.Параллельном режиме.Выполнения (в параллельном режиме параллельном режиме, ContextCallback обратного вызова, состояние объекта, логическое preserveSyncCtx)
в системе.Нарезание резьбы.Параллельном режиме.Выполнения (в параллельном режиме параллельном режиме, ContextCallback обратного вызова, состояние объекта)
в системе.Нарезание резьбы.ThreadHelper.ThreadStart (Object obj)

Внутреннее Исключение 1:
SocketException: существующее соединение было принудительно закрыто удаленным хостом



ПЫТАТЬСЯ:
- перерыв; метод
- создать переменную wroteshutdownonce, поэтому он не будет бегать на объект TcpClient.Close(); повторите команду.
- объект TcpClient.Закрывать();


Спасибо Вам за помощь!

2 Ответов

Рейтинг:
1

RickZeeland

См. пример здесь, the клиент.Закрывать(); должно быть вне цикла While:
Класс TcpListener (System.Net.Sockets) | Microsoft Docs[^]

С помощью такого цикла While:

// Loop to receive all the data sent by the client.
while((i = stream.Read(bytes, 0, bytes.Length))!=0)
{
  // Translate data bytes to a ASCII string.
  data = System.Text.Encoding.ASCII.GetString(bytes, 0, i);
  Console.WriteLine("Received: {0}", data);
)

// Shutdown and end connection
client.Close();


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


Csabatom

Попробовал, как вы сказали, это выглядит так:

public static void ClientListener(object obj)
{
Объект TcpClient объект TcpClient = (объект TcpClient)параметр obj;
StreamReader reader = новый StreamReader(tcpClient.Метод getstream());

Приставка.Время записи());
Приставка.ForegroundColor = ConsoleColor.Белый;
Приставка.WriteLine("клиент подключен");
в то время как (правда)
{
строковое сообщение = reader.ReadLine();
Широковещательная передача(сообщение, tcpClient);
Приставка.Время записи());
Приставка.ForegroundColor = ConsoleColor.Белый;
Приставка.WriteLine(сообщение);
}
объект TcpClient.Закрывать();
}

Печать одного и того же сообщения об ошибке :(

RickZeeland

Дело в том, что вы не можете продолжать чтение из потока, когда все данные считываются, см. обновленное решение для примера цикла While.

Csabatom

Пробовал, но не получилось. Я думаю, что я самый большой нуб xD

RickZeeland

Это не самая простая вещь для начала, возможно, вы можете сначала попробовать пример и постепенно расширить его своим собственным кодом. Удачи и не сдавайтесь слишком легко !

Csabatom

В любом случае спасибо за помощь :)

Рейтинг:
1

Doug- VisualBasic VB.NET

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

Однако я не уверен, что отключение клиента вызывает ошибку. В вашем цикле while вы продолжаете предполагать, что есть новое соединение, а затем отправляете его. Что делать, если нет никакой новой связи? Я бы также проверил наличие активного соединения перед чтением из него, как вы это делаете в своем разделе вещания.

Я бы попытался сделать ваши соединения активными на событиях, а не на цикле.

Проверьте это для получения дополнительной информации.
https://www.codeproject.com/Articles/1415/Introduction-to-TCP-client-server-in-C

Также
https://social.msdn.microsoft.com/Forums/en-US/802a98c0-9d56-4416-9c3f-954616ed4754/how-to-get-socket-events-in-c">Как получить события сокета в C#

Другой вариант-включить способ отслеживания идентификатора каждого клиента. Использование создания GUID здесь поможет. Вы создаете его, когда клиент подключается, и он будет отслеживать каждого клиента и его статус. Вы можете создать "класс подключения клиента" с ip-адресом и статусом всех подключенных клиентов, а затем при отключении события установить статус "offline" или другой флаг и проверить его перед вызовом методов прослушивателя (или широковещательной передачи). Или при отключении вы можете удалить GUID, а если он не существует, то не вызывайте методы.

Надеюсь, это укажет вам правильное направление.