Форум программистов, компьютерный форум, киберфорум
C# .NET
Войти
Регистрация
Восстановить пароль
 
Рейтинг 4.67/12: Рейтинг темы: голосов - 12, средняя оценка - 4.67
1184 / 540 / 78
Регистрация: 01.07.2009
Сообщений: 3,517
1

Проверка на стороне сервера, что клиент обработал отправленные данные

25.10.2012, 12:07. Показов 2380. Ответов 2
Метки нет (Все метки)

Есть клиент и есть сервер, общаются по именованому каналу (named pipe)

Проблема: нужно сделать так чтобы сервер после записи данных в поток проверял прочитал ли их клиент и если не прочитал то давал ему на это время (допустим 500 мс, думаю этого достаточно) и если за это время клиент так и не считал данные то сервер разрывал подключение дабы поток не простаивал в ожидании.
Проблема в том что просто запись в поток всегда ждёт пока клиент завершит чтение (хоть он там и пол часа висеть будет и не захочет читать с потока данные), а асинхронная запись происходит сразу же и не даёт никакого доп. времени клиенту на чтение.

Из тех методов что нашёл у NamedPipeServerStream есть:
pipeServer.WaitForPipeDrain(); - никакого толку, ждёт завершение всех операций, те по сути считай что асинхронная запись превратилась в синхронную и опять возможен висяк потока на долгое время
WriteTimeout / ReadTimeout; - тоже никакого толку так как выбрасывается исключение что NamedPipeServerStream не поддерживает таких вещей.
Ещё конечно же остался вариант в сервере после записи данных написать:
C#
1
2
                    if(pipeServer.IsConnected)
                        Thread.Sleep(500);
Но это точно же не выход так как сервер будет точно висеть 500 мс, а клиент то может и через 10 мс уже давно всё считал с потока и его нужно было давным-давно закрыть.

Как в таких случаях поступают подскажите пожалуйста, ато сам я решение найти не смог.

Код моего сервера и клиента (асинхронные операции не используются пока что).
Моя обёртка через которую происходит чтение и запись в поток при общении между клиентом и сервером:
C#
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
//класс для работы с потоком
//Позволяет писать пары логин-пароль и считывать результаты их обработки
class StreamRW
{
    private Stream ioStream;//поток куда писать данные
 
    public StreamRW(Stream stream)
    { ioStream = stream; }
 
    //запись пары логин-пароль в поток
    public void WriteData(Tuple<string, string> data)
    {
        byte[] outBuffer;
        using (MemoryStream m = new MemoryStream())
        using (BinaryWriter writer = new BinaryWriter(m))
        {
            writer.Write(data.Item1);
            writer.Write(data.Item2);
            outBuffer = m.ToArray();
        }
        int len = outBuffer.Length;
        ioStream.WriteByte((byte)(len / 256));
        ioStream.WriteByte((byte)(len & 255));
        ioStream.Write(outBuffer, 0, len);
        ioStream.Flush();
    }
 
    //чтение пары логин-пароль из потока
    public Tuple<string, string> ReadData()
    {
        Tuple<string, string> result;
        int len;
        len = ioStream.ReadByte() * 256;
        len += ioStream.ReadByte();
        byte[] inBuffer = new byte[len];
        ioStream.Read(inBuffer, 0, len);
        using (MemoryStream m = new MemoryStream(inBuffer))
        using (BinaryReader reader = new BinaryReader(m))
        {
            try
            {
                result = new Tuple<String, String>(reader.ReadString(), reader.ReadString());
            }
            catch (IOException ex)
            {
                Console.WriteLine("Error has occured :(\n" +
                    "If you'd like to know more, you can google it:" + ex.Message);
                result = new Tuple<String, String>("Error", "Error");
            }
        }
        return result;
    }
 
    //запись кода результата обработки
    public void WriteInt(int answer)
    {
        byte[] outBuffer = new byte[1] { Convert.ToByte(answer) };
        ioStream.Write(outBuffer, 0, 1);
        ioStream.Flush();
    }
 
    //чтение кода результата обработки (если соединение разорвано на стороне сервера то вернёт -1).
    //Исключение выброшено не будет даже если поток закрыт!
    public int ReadInt()
    {
        return Convert.ToInt32(ioStream.ReadByte());
    }
}
Сервер:
C#
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
 private static void ServerThread(object data)
        {
            //создаём именованный канал
            NamedPipeServerStream pipeServer =
                new NamedPipeServerStream("testpipe", PipeDirection.InOut, numThreads, PipeTransmissionMode.Byte, PipeOptions.Asynchronous);
 
            //пара логин-пароль для получения инфо от клиента
            Tuple<string, string> logpass;
 
            //получим ID (просто для вывода на экран, больше не используется)
            int threadId = Thread.CurrentThread.ManagedThreadId;
 
            //сделаем вечный цикл чтобы сервер работал постоянно (ну почти постоянно)
            while (true)
            {
                //подождём подключения
                pipeServer.WaitForConnection();
                //когда клиент подключился выведем уведомление об этом
                Console.WriteLine("Client connected on thread[{0}].", threadId);
                try
                {
                    //настроим обёртку на канал
                    StreamRW srw = new StreamRW(pipeServer);
 
                    //получим связку логин-пароль
                    logpass = srw.ReadData();
 
                    //покажем их на экран
                    Console.WriteLine("Get from client on thread[{0}]: " + logpass.Item1 + " " + logpass.Item2, threadId);
 
                    //отправим ответ клиенту
                    srw.WriteInt(1);
 
                    //выведем инфо что клиент отключился
                    Console.WriteLine("Client on thread[{0}] disconnected.", threadId);
                }
                catch (IOException e)
                {
                    Console.WriteLine("ERROR: {0}", e.Message);
                }
                //отсоединить клиента ибо нефиг тут висеть
                pipeServer.Disconnect();
            }
            pipeServer.Close();
        }
Клиент:
C#
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
            {
                NamedPipeClientStream pipeClient =
                    new NamedPipeClientStream(".", "testpipe",
                        PipeDirection.InOut, PipeOptions.None,
                        TokenImpersonationLevel.Impersonation);
 
                Console.WriteLine("Connecting to server...\n");
                pipeClient.Connect();
 
                //настроить обёртку на канал
                StreamRW srw = new StreamRW(pipeClient);
 
                //записать связку логин-пароль в поток
                srw.WriteData(new Tuple<string,string>("login","password"));
 
                //"зависнуть" на 5секунд чтобы продемонстрировать что сервер всё это время тоже висит и ничего не делает
                //в ожидании пока клиент не считает ответ  об обработке данных (число int)
                Thread.Sleep(5000);
 
                //вывести ответ от сервера
                Console.Write(srw.ReadInt());
 
                pipeClient.Close();
                // Give the client process some time to display results before exiting.
                Thread.Sleep(4000);
            }
0
Programming
Эксперт
94731 / 64177 / 26122
Регистрация: 12.04.2006
Сообщений: 116,782
25.10.2012, 12:07
Ответы с готовыми решениями:

Boost::Asio клиент - сервер: При вводе с телнета на стороне сервера появляется куча крякозябр
Раньше использовал Code::Blocks и winsock. Сейчас решил попробовать VS2013 и библиотеку boost::asio...

Так поступают данные в программу обработчик находящююся на стороне сервера?
В программу-обработчик на стороне сервера поступают name=&quot;name&quot; value=&quot;value&quot; name = value value...

Нужно узнать, сколько времени пользователь провел на данной странице и затем записать данные в БД на стороне сервера.
Нужно узнать, сколько времени пользователь провел на данной странице и затем записать данные в БД...

В массив выводить данные-id клиента, данные серверу и данные, отправленные сервером
Здравствуйте. Имеется клиент-серверное приложение, вопрос по стороне сервера. Имеется...

2
Эксперт Java
4075 / 3809 / 745
Регистрация: 18.05.2010
Сообщений: 9,331
Записей в блоге: 11
25.10.2012, 20:44 2
Если нет ожидания с таймаутом, то его всегда можно сэмулировать через потоки.
После отправки, создаете поток, в котором вызываете WaitForPipeDrain.
После чего ждете окончания потока с таймаутом.
C#
1
2
3
4
5
6
7
8
9
Thread waitThread = new Thread(() => WaitForPipeDrain());
waitThread.Start();
bool isTerminated = waitThread.Join(500);
if (isTerminated) {
   //все удачно отправилось
} else {
  //клиент отвалился, можно прибить поток
  waitThread.Abort();
}
1
1184 / 540 / 78
Регистрация: 01.07.2009
Сообщений: 3,517
26.10.2012, 00:49  [ТС] 3
turbanoff, спасибо, сейчас попробую.

Добавлено через 2 часа 11 минут
В общем этот подход до конца не решает мои проблемы. Я как делаю: запускаю 4 потока сервера и они там себе ждут клинетов, в каждом есть while(true) так что после обслуживания очередного клиента как только он отключается поток сервера ждёт сл. клиента и так всё время.
Если я в main создам 4 потока и буду потом пользоваться описанным методом то возникает куча проблем. Во первых придёться теперь же завершать поток каждый раз так как иначе эта моя ловушка на зависший поток прибъёт его, она же не в курсе что он уже другого клиента обслуживает ну и в связи с этим появляются проблемы...
Смоделирую проблему: есть 4 потока, к каждому подключился какой-то клиент. 1 поток работает 400 мс, 2, 3 и 4 спраляются за 10мс. Так как в main у меня будет
bool isTerminated = waitThread.Join(500);
то перезапуск первого потока произойдёт через 500 мс (мейн ждёт завершения этой операции так что мейн "висит"), но так как перезапуск остальных потоков идёт после перезапуска первого то потоки 2, 3, 4 тоже висят 500 мс

Добавлено через 27 минут
В общем-то мне нужно что-то что будет пристреливать потоки которые повысли в ожидании, но если поток продолжает обработку то и трогать его не надо ... Другое дело как определять что он продолжает работу...
0
IT_Exp
Эксперт
87844 / 49110 / 22898
Регистрация: 17.06.2006
Сообщений: 92,604
26.10.2012, 00:49

Заказываю контрольные, курсовые, дипломные работы и диссертации здесь.

С сервера отправить данные на клиент
разобрался я более менее с сокетами но вот не как не получается с сервера отправить строчку на...

Передать данные с сервера на клиент
Всем доброго времени суток. При выборе товара в таблице нужно получить его артикул и фирму из...

Клиент на Android не получает данные с сервера
Сервер на компе, написан на .Net, клиент на Android, ставится на устройство. Находясь в одной сети...

Как правильно передавать данные с сервера на клиент (игра)
Добрый день. Есть структурированный список игроков(т.е. те кто онлайн) В цикле каждому...


Искать еще темы с ответами

Или воспользуйтесь поиском по форуму:
3
Ответ Создать тему
Опции темы

КиберФорум - форум программистов, компьютерный форум, программирование
Powered by vBulletin® Version 3.8.9
Copyright ©2000 - 2021, vBulletin Solutions, Inc.