- Использование сокетов в Android
- Особенности использования сокетов
- Клиентский android-сокет
- Класс Connection
- Листинг Connection
- Класс активности MainActivity
- Листинг активности
- Методы управления сокетным соединением
- Серверное приложение
- Листинг ConnectionWorker
- Серверный класс
- Листинг CallableDelay
- Листинг CallableServer
- Листинг серверного класса Server
- Простой клиент-сервер на Android (интернет-мессенджер)
- Делаем сервер
- Клиентская часть
- Управляем компьютером с Android устройства
- Начало
- Что мы имеем
- Немного о сокетах
- Сервер. Начинаем хулиганить
- Клиент
Использование сокетов в Android
Создано большое количество приложений как для Android, так и для других ОС, которые взаимодействуют друг с другом с помощью установления соединенией по сети. К таким приложениям относятся, например, мессенджеры социальных сетей WhatsApp, Viber. Как правило, для установления соединения между такими приложениями используются сокеты.
Сокет (socket) — это интерфейс, позволяющий связывать между собой программы различных устройств, находящихся в одной сети. Сокеты бывают двух типов: клиентский (Socket) и серверный (ServerSocket). Главное различие между ними связано с тем, что сервер «открывает» определенный порт на устройстве, «слушает» его и обрабатывает поступающие запросы, а клиент должен подключиться к этому серверу, зная его IP-адрес и порт. В Android сокеты для передачи данных используют по умолчанию протокол TCP/IP, важной особенностью которого является гарантированная доставка пакетов с данными от одного устройства до другого.
Особенности использования сокетов
Что важно знать при использовании сокетов в Android ?
- соединения сокетов отключаются при переходе устройства в спящий режим;
- чтобы не «рвать» соединение при наступлении спящего режима в устройстве можно использовать сервис;
- для использования интернет-сети необходимо Android-приложению предоставить нужные права в манифесте.
Для определения прав в манифесте необходимо в файл AndroidManifest.xml добавить следующую строку :
Теперь android-приложения будет иметь доступ к сети.
Далее в статье рассмотрим пример клиент-серверного сокетного соединения с передачей сообщения. Функции клиента будет выполнять android-приложение. Серверное java-приложение выполним в IDE Eclipse с использованием пакета concurrent. В конце страницы можно скачать оба приложения.
Клиентский android-сокет
Интерфейс andriod-приложения представлен на следующем скриншоте. Форма приложения включает поле ввода текстового сообщения и кнопки установления соединения сервером, передачи сообщения и закрытия соединения.
Клиентское приложение создадим из двух классов : класс взаимодействия с серверным сокетом Connection и класс стандартной активности MainActivity.
Класс Connection
Класс взаимодействия с сервером Connection получает при создании (через конструктор) параметры подключения : host и port. Методы Connection вызываются из активности и выполняют следующие функции :
Метод | Описание |
---|---|
openConnection | Метод открытия сокета/соединения. Если сокет открыт, то он сначала закрывается. |
closeConnection | Метод закрытия сокета |
sendData | Метод отправки сообщения из активности. |
finalize | Метод освобождения ресурсов |
Листинг Connection
Класс активности MainActivity
В активности MainActivity определены параметры сервера : host, port. Помните, что IP-адрес сервера для Вашего android-примера не может быть localhost (127.0.0.1), иначе Вы будете пытаться связаться с сервером внутри Andriod-системы. Кнопки интерфейса связаны с методами обращения к классу Connection. Кнопки отправки сообщения mBtnSend и закрытия соединения mBtnClose с сервером блокируются при старте приложения. После установления соединения с сервером доступ к кнопкам открывается.
Листинг активности
Методы управления сокетным соединением
Ниже представлены методы обработки событий, связанных с нажатием кнопок интерфейса. Обратите внимание, что подключение к серверу выполняется в отдельном потоке, а открытие доступа к кнопкам в основном потоке, для чего вызывается метод runOnUiThread. Для отправки сообщения серверу также создается отдельный поток.
Серверное приложение
Серверное приложение включает 2 класса : Server и ConnectionWorker. Серверный класс Server будет выполнять обработку взаимодействия с клиентом с использованием ConnectionWorker в отдельном потоке. Конструктор ConnectionWorker в качестве параметра получает объект типа Socket для чтения сообщений клиента из потока сокета.
Листинг ConnectionWorker
ConnectionWorker получает входной поток inputStream из клиентского сокета и читает сообщение. Если сообщение отсутствует, т.е. количество прочитанных байт равно -1, то это значит, что соединение разорвано, то клиентский сокет закрывается. При закрытии клиентского соединения входной поток сокета также закрывается.
Серверный класс
Серверный класс Server создадим с использованием многопоточного пакета util.concurrent. На странице описания сетевого пакета java.net и серверного ServerSocket был приведен пример серверного модуля с использованием обычного потока Thread, при работе с которым необходимо решать задачу его остановки : cтарый метод Thread.stop объявлен Deprecated и предан строжайшей анафеме, а безопасная инструкция Thread.interrupt безопасна, к сожалению, потому, что ровным счетом ничего не делает (отправляет сообщение потоку : «Пожалуйста, остановись»). Услышит ли данный призыв поток остается под вопросом – все зависит от разаработчика.
Чтобы иметь возможность остановить сервер «снаружи» в серверный класс Server включим 2 внутренних реализующих интерфейс Callable класса : CallableDelay и CallableServer. Класс CallableDelay будет функционировать определенное время, по истечении которого завершит свою работу и остановит 2-ой серверный поток взаимодействия с клиентами. В данном примере CallableDelay используется только для демонстрации остановки потока, организуемого пакетом util.concurrent.
Листинг CallableDelay
CallableDelay организует цикл с задержками. После завершения последнего цикла cycle поток завершает цикл, останавливает вторую задачу futureTask[1] и закрывает сокет. В консоль выводится соответствующее сообщение.
Листинг CallableServer
Конструктор CallableServer в качестве параметров получает значение открываемого порта для подключения клиентов. При старте (метод call) создается серверный сокет ServerSocket и поток переходит в режим ожидания соединения с клиентом. Остановить поток можно вызовом метода stopTask, либо завершением «задачи» типа FutureTask с данным потоком.
При подключении клиента метод serverSoket.accept возвращает сокет, который используется для создания объекта ConnectionWorker и его запуска в отдельном потоке. А сервер (поток) переходит к ожиданию следующего подключения.
В случае закрытия сокета (завершение внешней задачи FutureTask с данным потоком) будет вызвано исключение Exception, где выполняется проверка закрытия сокета; при положительном ответе основной цикл прерывается и поток завершает свою работу.
Листинг серверного класса Server
Cерверный класс Server создает два потоковых объекта (callable1, callable2), формирует из них две задачи futureTask и запускает задачи на выполнение методом execute исполнителя executor. После этого контролируется завершение выполнение обоих задач методом isTasksDone. При завершении выполнения обеих задач завершается также и цикл работы executor’а.
Два внутренних описанных выше класса (CallableDelay, CallableServer) не включены в листинг.
Источник
Простой клиент-сервер на Android (интернет-мессенджер)
Важно. Все написанное ниже не представляет собой какой либо ценности для профессионалов, но может служит полезным примером для начинающих Android разработчиков! В коде старался все действия комментировать и логировать.
Поехали. Многие мобильные приложения (и не только) используют архитектуру клиент-сервер. Общая схема, думаю, понятна.
Уделим внимание каждому элементу и отметим:
- сервер — представляет собой некую программу, работающую на удаленном компьютере, и реализующую функционал «общения» с приложениями-клиентами (слушает запросы, распознает переданные параметры и значения, корректно отвечает на них);
- клиент — в нашем случае, программа на мобильном устройстве, которая умеет формировать понятный серверу запрос и читать полученный ответ;
- интерфейс взаимодействия — некий формат и способ передачи/получения запросов/ответов обеими сторонами.
Неважно, как реализован любой из этих элементов, все они в любом случае присутствуют. Давайте реализуем примитивный сервер и Android клиент, работающий с ним. Как пример, будем использовать любой популярный мобильный интернет-мессенджер (Viber, ICQ), а приложение условно назовем «интернет-чат».
Схема взаимодействия следующая:
Клиент, установленный на устройстве А, посылает сообщение для клиента, установленного на устройстве Б. И наоборот. Сервер играет роль связующего звена между устройством А и Б… С, Д… и т.д. Также он играет роль «накопителя» сообщений, для их восстановления, на случай удаления на одном из клиентских устройств.
Для хранения сообщений используем SQL БД как на сервере, так и на устройствах-клиентах (в принципе, вся работа клиентов интернет-мессенджеров и сводится к постоянной синхронизации локальной и удаленной БД с сообщениями). Дополнительно, наш интернет-чат будет уметь стартовать вместе с запуском устройства и работать в фоне. Взаимодействие будет происходить путем HTTP запросов и JSON ответов.
Более логично, если синхронизация происходит через порт/сокет, это с одной стороны упрощает задачу (не нужно циклично слать HTTP запросы на проверку новых сообщений, достаточно проверять состояние прослушиваемого сокета), но с другой стороны, это усложняет создание серверной части приложения.
Делаем сервер
Для реализации «сервера», нам нужно зарегистрироваться на любом хостинге, который дает возможность работы с SQL и PHP.
Создаем пустую SQL БД, в ней создаем таблицу.
- author — автор сообщения;
- client — получатель сообщения;
- data — время и дата получения сообщения на сервере;
- text — сообщение.
В двух следующих файлах необходимо изменить переменные, содержащие данные для доступа к БД, на свои, полученные Вами при регистрации Вашего«сервера».
Структура запросов к api:
- обязательный атрибут action — может быть равен select (сервер ответит списком записей из своей БД), insert (сервер добавить новую запись в свою БД), delete (сервер очистит свою БД)
- если action=insert, нам нужно будет передать дополнительные параметры: author (кто написал сообщение), client (кому адресовано сообщение), text (сообщение)
- action=select может содержать дополнительный параметр data, в этом случае ответ сервера содержит не все сообщения из БД, а только те, у которых время создания позднее переданного
Примеры:
- chat.php?action=delete – удалит все записи на сервере
- chat.php?action=insert&author=Jon&client=Smith&text=Hello — добавит на сервере новую запись: автор Jon, получатель Smith, содержание Hello
- chat.php?action=select&data=151351333 — вернет все записи, полученные после переданного времени в long формате
Клиентская часть
Теперь структура Android приложения:
В фоне работает FoneService.java, который, в отдельном потоке, каждые 15 секунд делает запрос на сервер. Если ответ сервера содержит новые сообщения, FoneService.java записывает их в локальную БД и отправляет сообщение ChatActivity.java о необходимости обновить ListView, с сообщениями. ChatActivity.java (если она в этот момент открыта) получает сообщение и обновляет содержимое ListView из локальной БД.
Отправка нового сообщения из ChatActivity.java происходит сразу на сервер, минуя FoneService.java. При этом наше сообщение НЕ записывается в локальную БД! Там оно появится только после получения его назад в виде ответа сервера. Такую реализацию я использовал в связи с важным нюансом работы любого интернет-чата — обязательной группировкой сообщений по времени. Если не использовать группировку по времени, будет нарушена последовательность сообщений. Учитывая, что клиентские приложения просто физически не могут быть синхронизированы с точностью до миллисекунд, а возможно будут работать даже в разных часовых поясах, логичнее всего будет использовать время сервера. Так мы и делаем.
Создавая новое сообщение, мы передаем запросом на сервер: имя автора сообщения, имя получателя сообщения, текст сообщения. Получая эту запись назад, в виде ответа сервера, мы получаем то, что отправляли + четвертый параметр: время получения сообщения сервером.
Источник
Управляем компьютером с Android устройства
Начало
А началось все с того, что вызывает меня генеральный к себе, и говорит: «Вот видишь телефон? Хочу чтобы там была кнопка, я на нее нажимаю, и у меня в ноутбуке кино включается. Нажимаю другую – музыка играет.» И еще чего-то много наговорил, уж не помню. «Задача понятна? Выполняй!» Вот уж не знаю, с чего такая потребность у него возникла. То ли звезды не под тем углом встали, то ли сон какой приснился. Короче, не поймешь этих богатых… Ну да ладно.
Поначалу полез рыться в Гугл в поисках подходящей программы, а потом подумал – а какого черта? Напишу сам. Тем более, что задача не показалась сложной, да и “зов кода” уже давал о себе знать (этакая профессиональная it-ломка). Вот и решил соединить Windows и Android собственными силами.
То, что он просил, я сделал за пару дней. Но здесь я не хочу городить много кода, проверок и обработок исключений и т.п. Статья скорее предназначена для самых маленьких, как основа, опираясь на которую, можно построить что-то более масштабное. Ни в коем случае не претендую на оригинальность, явно кто-то что-то подобное писал, я просто предлагаю свой вариант. В общем, всем, кому интересно, посвящается.
Что мы имеем
Значит так. С одной стороны, у нас телефон с Android на борту, с другой — Windows с установленными программами, притом некоторые из этих программ нам надо запускать, подав команду с телефона.
Телефон и компьютер свяжем через локальную сеть, тут без вариантов (ну не смски же посылать). Таким образом, будем писать две программы. Первая — это сервер, работающий на компьютере, задача этой программы — открыть и слушать порт. Если на этот порт падает что-то полезное, то выполнить заданное нами действие. Вторая программа — это клиент, запущенный на телефоне, ее задача обработать действия пользователя, подключиться к серверу и передать информацию.
Немного о сокетах
Тема программирование сокетов до того уже заезженная, что и особо говорить нечего. Но все же в двух словах, для тех, кто не любит ходить по ссылкам.
Сокет — это программный интерфейс, который позволяет устанавливать связь между двумя процессами, используя протокол tcp/ip. Сокет ассоциирован с двумя аспектами: ip-адресом и портом. Где ip-адрес — это адрес хоста (компьютера) в сети, с ним работает протокол IP. Port — это идентификатор приложения, к которому адресовано соединение, тут работает протокол TCP. Порт может быть как TCP, так и UDP, в этой статье я буду использовать только TCP. Поскольку ip-адрес является уникальным как в сети интернет, так и в локальной сети, то он однозначно определяет адрес отправителя и адрес принимающего. Порт же является уникальным в пределах операционной системы, он определяет приложение, с которым мы хотим взаимодействовать. Порты могут быть стандартными, например, 80 закреплен за HTTP, или 3389 — RDP. Вы можете использовать любой незанятый порт, но стандартные лучше не трогать. Очень хорошо и с примерами о сокетах написано здесь.
Сервер. Начинаем хулиганить
Запускать Aimp, Windows Media Player и т.п. даже с телефона — это не интересно, да и на базе этой статьи вы сможете все это легко реализовать, немного переделав код. Давайте лучше побезобразничаем. Будим крутить-вертеть экран монитора как нам вздумается или выводит неожиданные сообщения (этакий однонаправленный ацкий мессенджер), и самое ужасное — выключим компьютер! Правда, за это могут и на вилы надеть. Ну да ладно, пускай сначала поймают.
Итак, приступим. В Visual Studio создаем новое Windows Form приложением с именем, скажем, FunnyJoke. Открываем файл Program.cs и удаляем весь код в теле функции Main. Этот код инициализирует главную форму приложения, нашему серверу никакие окна не нужны, он должен сидеть тихо мирно и ждать команд.
В классе Program определим следующие переменные:
Я взял порт 10000, именно его и будет слушать наш сервер, вместо ip адреса задал 0.0.0.0 это говорит о том, что будут обрабатываться все доступные сетевые интерфейсы. Это не совсем правильно, но для начала сойдет. Далее я определил три константы, которые задают коды команд, приходящие от клиента. В начале проекта не забываем подключить:
Теперь, вместо удаленного кода в функции Main вставляем следующий:
Пример хорошо комментирован. Но все же поясню. Сначала создаем локальную конечную точку и ассоциируем ее с нашим ip адресом и портом. Затем, определяем основной сокет, связываем его с конечной точкой, и переводим в режим прослушивания. После этого входим в бесконечный цикл, и начиная со строки:
наш сервер переходит в состояние ожидания соединения. При удачном соединении создастся новый экземпляр Socket, посредствам которого мы и будем общаться с нашим клиентом. После того как соединение установлено начинаем читать данные:
Команды клиента закодированы однобайтовым кодом (описаны в начале программы), сервер расшифровав код команды начинает ее выполнять, после этого снова переходит в режим ожидания. Исключением является codeMsg, т.к. после нее ожидается набор байт, содержащий строку сообщения. Поэтому, получив эту команду сервер снова читает данные с сокета:
Строка, приходящая от клиента, имеет кодировку символов UTF-8, поэтому прежде чем показать ее несчастному пользователю, необходимо привести ее к стандартному виду.
Что бы упростить программу, и не создавать лишние диалоги я использовал стандартный класс MessageBox, но у таков подхода есть один недостаток. MessageBox создает модальное окно, которое блокирует поток всего приложения. Другими словами, пока открыто окно с сообщением наш сервер ничего не делает. Минус конечно, но за простоту надо платить.
Процедуру, изменения ориентации экрана, расписывать не буду, ее код я выполнил так как рекомендует Microsoft вот тут. Как повернуть экран средствами .NET я не нашел. Это легко осуществимо для мобильных платформ, а вот для обычного PC оказалась неразрешимая проблема. Но, на помощь пришел старый добрый WINAPI и все разрулил.
Выключаем компьютер штатными средствами Windows, путем вызова команды shutdown с соответствующими флагами.
С сервером, пожалуй, все. Исходный код проекта я прикреплю в конце статьи.
Клиент
Клиент будем писать в Android Studio, поскольку мне эта IDE больше нравится чем Eclipse. Любителям последнего думаю не составит больших трудностей переделать проект. Для отладки я использовал VirtualBox с установленной виртуальной машиной Android, ибо родной эмулятор жутко тормозной, и жизни не хватить что бы с его помощью что-то отладить. Ну и периодически проверял на «живом» телефоне. Итак, создаем проект с именем FunnyJoke, задаем минимальную версию API, которую способен утянуть ваш телефон (у меня 16) и выбираем Empty Activity. Все остальное по умолчанию. Делаем разметку представления. С дизайном я шибко не извращался, кому надо пускай рисует красивые кнопки, размещает их по фен Шую и т.п. Я сделал просто: два поля типа EditText, первое для ввода ip адреса контролируемого компьютера, второе для текста сообщения, и кнопка, которая заставит поворачиваться рабочий стол. А вот кнопку завершения работы я сделал большую и угрожающее красную. Это чтоб случайно не нажать.
Тут стоит обратить внимание на поле edIPaddress, в нем стоит фильтрация на ввод только цифр и. (точка), так-как поле предназначено для ввода ip адреса. Надо сказать, что это единственная проверка на правильность введенных данных, все остальное остается на совести пользователя. Еще хочу cказать о кнопке btnPowerOff ее состояние отслеживает селектор, и в зависимости от того нажата она или нет меняет изображение (иначе, не понятно произошло ли нажатие, кнопка будет выглядеть как статичная картинка). Вот код селектора button_img.xml:
Соответственно в ресурсах должны быть две картинки одна для нажатого состояния, другая для обычного. Получится вот такой экран:
На этом с разметкой закончим. Переходим к файлу MainActivity.java. В первую очередь, так же, как и в сервере, определяем коды команд и некоторые переменные:
Далее переходим к обработчику нажатия кнопок. Обратите внимание, что обработчик один, и какая копка была нажата определяем по идентификатору. В первую очередь получаем строку с поля edIPaddress, если поле не заполнено, то выводим сообщение о необходимости ввода ip адреса, и больше ничего не делаем.
В Android не рекомендуется создавать долгоиграющие процессы в основном потоке, это связанно с тем, что возможно “подвисание” программы, и пользователь или система может просто закрыть приложение, не дождавшись ответа. К таким долгоиграющим процессам относится и работа с сетью. В этом случае необходимо создать дополнительный поток, в котором и выполнять “долгий” код. В java есть стандартный класс Thread, который позволяет управлять потоками но, его мы использовать не будем, т.к. в Android существует специально предназначенный для этого класс AsyncTask. Подробно можно почитать здесь или здесь.
Создаем класс, который будет заниматься отправкой сообщения, его родителем делаем AsyncTask, и переопределяем метод doInBackground в теле которого и будет находится основной код:
Сначала создаем экземпляр класса InetAddress, который будет содержать в себе ip сервера. Потом создаем сокет, связываем его с удаленным адресом и портом, и запрашиваем стандартный поток ввода/вывода (вернее только вывода, потому что наш клиент ничего не получает). И наконец, в зависимости от значения переменной codeCommand, посылаем сообщение серверу.
Теперь вернемся к нашему обработчику нажатия кнопок, создадим экземпляр класса SenderThread, затем в зависимости от того какая кнопка была нажата инициализируем переменную codeCommand, по ней наш поток будет определять что мы от него хотим. И наконец, активируем, вызвав метод execute().
Немного поправим манифест приложения, дадим разрешение на использование сети и wi-fi, без этого ничего работать не будет:
Все! Можно собирать и проверять. Вот результат:
Источник