Форум программистов, компьютерный форум, киберфорум
LostCoast
Войти
Регистрация
Восстановить пароль
Блоги Сообщество Поиск  

Обучающая статья по использованию асинхронных сокетов. Оптимизация.

Запись от LostCoast размещена 17.12.2012 в 09:53
Показов 10493 Комментарии 0
Метки c#, c-sharp

Содержание:
  1. Базовые функции
    1. Введение
    2. Передача данных
    3. Регистрация
    4. Авторизация
    5. Оптимизация
  2. Возможности для клиентов
    1. Изменение архитектуры
    2. Передача сообщений между клиентами
    3. Статус клиентов
    4. Список пользователей
    5. Передача файлов
Оптимизация.

Часть 1. Изменения на стороне клиента.

Это часть будет посвящена небольшим доработкам в плане интерфейса, если это слово можно применить к консольным приложениям, и некоторых моментов на стороне сервера.
Перейдем к проекту клиентской части. Немного переделаем наше меню. С начала, добавим метод ShowHelp над методом Main:

C#
1
2
3
4
5
6
7
8
9
10
private static void ShowHelp()
        {
            Console.WriteLine("--------Commands--------");
            Console.WriteLine("    [1] Registration");
            Console.WriteLine("    [2] Authorize");
            Console.WriteLine("    [3] Send message");
            Console.WriteLine("    [4] Send file");
            Console.WriteLine("    [0] Exit");
            Console.WriteLine("------------------------");
        }
Он будет вызываться, когда пользователь введет команду –help в нашем приложении.
Также изменим процесс общения клиента с сервером. Добавим возможность при вводе команды для общения, предоставлять пользователю отправку сообщений, сколько он захочет, до тех пор, пока не напишет команду –exit. Добавим метод для общения с сервером:

C#
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
private static void StartChatWithServer(ref Client client)
        {
            while (true)
            {
                Console.Write("Write message: ");
                string cmd = Console.ReadLine();
                if (cmd == "-exit")
                {
                    break;
                }
                else
                    client.Send(cmd);
                System.Threading.Thread.Sleep(100);
            }
        }
Расположите его над методом ShowHelp. Осталось переделать функцию Main. Ее код будет выглядеть следующим образом:

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
static void Main(string[] args)
        {
            Client client = new Client(GetIp(), 5555);
            client.Connect();
         string REGIST = "_CMD_REGISTR";
            string AUTH = "_CMD_AUTH";
            Console.WriteLine("Connection completed. For more information use command \"-help\".");
            
            while (true)
            {
                Console.Write("\n>>> ");
                string cmd = Console.ReadLine();
                try
                {
                    switch (Convert.ToInt32(cmd))
                    {
                        case 1:
                            client.Send(REGIST);
                            string send_regist = Console.ReadLine();
                            client.Send(ToSHA1(send_regist));
                            send_regist = Console.ReadLine();
                            client.Send(ToSHA1(send_regist));
                            break;
                        case 2:
                            client.Send(AUTH);
                            string send_auth = Console.ReadLine();
                            client.Send(ToSHA1(send_auth));
                            send_auth = Console.ReadLine();
                            client.Send(ToSHA1(send_auth));
                            break;
                        case 3:
                            Console.WriteLine("Use command \"-exit\" to end chat");
                            StartChatWithServer(ref client);
                            break;
                        case 4:
                            Console.WriteLine("Sending file");
                            client.SendFile("test.txt");
                            break;
                        case 0:
                            client.Disconnect();
                            return;
                        default:
                            Console.WriteLine("Unkhown command");
                            break;
                    }
                }
                catch (Exception) { }
 
                if (cmd == "-help")
                    ShowHelp();
                System.Threading.Thread.Sleep(100);
            }
        }
Теперь наш клиент напоминает «настоящую» программу. Думаю здесь не должно быть много вопросов, так как я в плане механики клиента ничего нового не добавляю, просто делаю более удобным его использование.

Часть 2. Изменения на стороне сервера.

По сути, сервер работает нормально. НО есть пару моментов, которые следует учесть, а именно что должен делать сервер при отключении от него клиента. Тут два варианта развития событий: первый, клиент вышел корректно, через Disconnect; второе, при отправке или приеме сообщения на стороне сервера произошла ошибка.
Перейдем к классу ServerHandler. Начнем со второго варианта, так как он попроще.
Чтобы устранить этот момент, надо всего лишь добавить блоки try catch в методы приема и отправки сообщений. Перепишем этим методы с учетом блоков try catch:

C#
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
private void Send(string msg)
        {
            try
            {
                Socket socket = this.client_socket;
                byte[] buffer = Encoding.UTF8.GetBytes(msg);
                socket.BeginSend(buffer, 0, buffer.Length, SocketFlags.None, new AsyncCallback(SendCallBack), socket);
            }
            catch (SocketException) { Disconnect(); }
        }
private void Receive()
        {
            try
            {
                StateObject state = new StateObject();
                state.workSocket = this.client_socket;
                state.workSocket.BeginReceive(state.buffer, 0, StateObject.bufferSize, SocketFlags.None, new AsyncCallback(ReceiveCallBack), state);
            }
            catch (SocketException) { Disconnect(); }
        }
Уточним еще один момент. При возникновении какой-либо ошибки, следует отключать клиента, так как нам не нужен висящий в списке «ошибочный клиент». С таким клиентом сервер не будет работать, поэтому лучше, чтобы клиент пере зашёл.
Метод Disconnect реализуется очень просто. Код:

C#
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
public void Disconnect()
        {
            Console.WriteLine("Client disconnected");
            try
            {
                this.client_socket.Shutdown(SocketShutdown.Both);
            }
            catch (Exception ex)
            {
                Console.WriteLine(ex.ToString());
            }
            finally
            {
                this.client_socket.Close();
            }
        }
Чтобы отключить клиента, надо вызвать метод Shutdown, указав в аргументе отключения для отправки и приема сообщений. Далее в блоке finally разрешить существующее соединение между клиентом и сервером, вызвав метод Close.
Осталась ситуация, когда клиент сам вызывает отключение. Это очень важный момент, так как ошибок мы при этом не получаем, а свойство сокета IsConnected не покажет вам реальное состояние (прим. так написано в MSDN). Но на самом деле это не такая уж трудная задача. Существует много путей для проверки подключения, начиная от различных пингов клиента и заканчивая проверкой данных в существующем соединении.
Мы же будем проверять состояние сокета, и смотреть на количество передаваемых байтов в сети. Начнем с написания метода, который возвращает нам состояние подключения. Код:

C#
1
2
3
4
5
6
7
8
private bool IsConnected(Socket socket)
        {
            try
            {
                return !(socket.Poll(1, SelectMode.SelectRead) && socket.Available == 0);
            }
            catch (SocketException) { return false; }
        }
Немного поясню, что происходит. Первый метод, которым мы проверяем, является Poll. Он принимает два параметра: первый – интервал в миллисекундах, второй – состояние сокета, либо чтение, либо запись. Так как наш сервер является пассивным, что значит, что он всегда слушает сокет методом Receive, более логично проверять, что состояние сокета доступно на чтение данных. И втрое наше условие это Available – оно показывает, сколько байт доступно для чтения. Как только наш сокет не может прочитать данные, либо данные вообще отсутствуют – мы делаем вывод, что соединение разорвано.
Теперь нам надо как то вызывать эту проверка. Чтобы сильно не нагружать систему, добавим вызов бесконечного цикла, с интервалом в 5 секунд, запущенный в новом потоке. Нам метод будет просто вызывать метод IsConnected, и в случае false, будет отключать клиента. Код:

C#
1
2
3
4
5
6
7
8
9
10
11
12
private void CheckConnection()
        {
            while (true)
            {
                System.Threading.Thread.Sleep(5000);
                if (!IsConnected(this.client_socket))
                {
                    Disconnect();
                    return;
                }
            }
        }
Все, что осталось, это создать новый поток. Это делается легко, буквально в две строчки. Создадим переменную типа Thread в классе ServerHandler и назовем ее thread_for_check:

C#
1
private Thread thread_for_check;
Далее добавим две строчки в конструкторе класса:

C#
1
2
this.thread_for_check = new System.Threading.Thread(CheckConnection);
this.thread_for_check.Start();
Все готово. Теперь при инициализации нового экземпляра будет создаваться новый поток, который будет следить за состояние подключения. Осталось только завершать этот поток, вместе с уничтожением соединения. Перейдем к методу Disconnect и добавим следующую строчку, над вызовом метода Shutdown:

C#
1
this.thread_for_check.Abort();
Метод Abort немедленно завершает поток. Это нужно из-за того, что метод CheckConnection никогда не закончится, так как в нем используется бесконечный цикл.
Нам этом все. Наше приложение готово. Подведем итоги. В ходе написания простейшего клиент-серверного приложения, мы изучили основные возможности асинхронных сокетов. Научились передавать, принимать сообщения, отслеживать состояние подключения. А также разработали простой интерфейс для общения с сервером.
Но, как ни крути, наше приложение далеко от совершенства. Мало кому будет интересно общаться с севером, да еще при этом получая свое сообщение в ответ. В следующем уроке мы научимся обмениваться сообщениями между клиентами, используя сервер, как промежуточное звено. Научимся давать клиенту список онлайн пользователей, добавим такие вещи как список друзей и статусы пользователей (онлайн, оффлайн, занят и т.п.).
Метки c#, c-sharp
Размещено в Без категории
Надоела реклама? Зарегистрируйтесь и она исчезнет полностью.
Всего комментариев 0
Комментарии
 
Новые блоги и статьи
Часы электронные
Uhbif79 12.08.2026
Выкладываю программу часов. Программа позволяет: 1. Использовать системное время и дату, 2. Есть возможность вводить время и дату вручную. 3. Реализованы 2 будильника: начало и конец рабочего дня. . . .
Часы с будильником на основе класса QLCDNumber
Uhbif79 12.08.2026
Всем добрый день, выкладываю программу часов с будильником на основе класса QLCDNumber. Здесь я пробовал самостоятельно создавал классы, впервые столкнулся с видимостью переменной одного класса из. . .
Установка MinGW GCC 16.2 и CMake
8Observer8 10.08.2026
VK Видео: https:/ / vkvideo. ru/ video-240781534_456239017 YouTube: eY5-5PyI9NM Текстовая версия
Неделя из жизни имитационной модели склада: мои кривые руки растут, откуда надо
anaschu 10.08.2026
Неделя из жизни имитационной модели склада: как я почти написал неправильную логику и что с этим делать Работаю сейчас над учебно-рабочим проектом: строю в AnyLogic имитационную модель процессов. . .
Калькулятор для расчета родства
russiannick 07.08.2026
1. Задача: Создать калькулятор для расчета родства. Родственных связей существует 8 ступеней, такие как: p - отец P - мать q - муж Q - жена b - брат B - сестра s - сын S - дочь
Мир по моей воле
kumehtar 07.08.2026
Когда-то кажется, что всё просто. Ты весь такой светлый. Причиняешь добро. Борешься за справедливость в этом тёмном мире. Потом начинаешь замечать одну неприятную вещь. Почти каждый хороший. . .
Кредитный калькулятор
Maks 05.08.2026
Решение задачи по прикладной информатике средствами 1С. Задача: Напишите приложение-калькулятор, которое помогает рассчитывать параметры кредита для аннуитетного и дифференцированного видов. . .
У нас сейчас поговорку "Опять 25" нужно переделать на "Опять +35".
kumehtar 04.08.2026
С ностальгией вспоминаю времена моего детства, когда у нас и правда +25 - была максимальная температура летом. Раньше +25 °C реально казались вершиной жары, когда можно было весь день пропадать на. . .
КиберФорум - форум программистов, компьютерный форум, программирование
Powered by vBulletin
Copyright ©2000 - 2026, CyberForum.ru