Форум программистов, компьютерный форум, киберфорум
Наши страницы

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

Войти
Регистрация
Восстановить пароль
Рейтинг: 5.00. Голосов: 3.

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

Запись от LostCoast размещена 17.12.2012 в 09:53
Метки 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 никогда не закончится, так как в нем используется бесконечный цикл.
Нам этом все. Наше приложение готово. Подведем итоги. В ходе написания простейшего клиент-серверного приложения, мы изучили основные возможности асинхронных сокетов. Научились передавать, принимать сообщения, отслеживать состояние подключения. А также разработали простой интерфейс для общения с сервером.
Но, как ни крути, наше приложение далеко от совершенства. Мало кому будет интересно общаться с севером, да еще при этом получая свое сообщение в ответ. В следующем уроке мы научимся обмениваться сообщениями между клиентами, используя сервер, как промежуточное звено. Научимся давать клиенту список онлайн пользователей, добавим такие вещи как список друзей и статусы пользователей (онлайн, оффлайн, занят и т.п.).
Размещено в Без категории
Просмотров 2693 Комментарии 0
Всего комментариев 0

Комментарии

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