Как включить сокет на телефоне

Обновлено: 06.07.2024

Этот учебник знакомит с программированием сокетов Java по протоколу TCP/IP с реальным приложением Клиент/сервер.

1. Обзор

Термин сокет программирование относится к написанию программ, которые выполняются на нескольких компьютерах, в которых все устройства подключены друг к другу с помощью сети.

Существует два протокола связи, которые можно использовать для программирования сокетов: Протокол пользовательских дейтаграмм (UDP) и протокол управления передачей (TCP) .

Основное различие между ними заключается в том, что UDP не имеет соединения, что означает отсутствие сеанса между клиентом и сервером, в то время как TCP ориентирован на соединение, что означает, что сначала должно быть установлено исключительное соединение между клиентом и сервером для осуществления связи.

Этот учебник представляет введение в программирование сокетов по сетям TCP/IP и демонстрирует, как писать клиентские/серверные приложения на Java. UDP не является основным протоколом и как таковой может встречаться нечасто.

2. Настройка проекта

Java предоставляет набор классов и интерфейсов, которые заботятся о деталях низкоуровневой связи между клиентом и сервером.

Нам также нужны java.io пакет, который дает нам входные и выходные потоки для записи и чтения во время общения:

3. Простой Пример

Давайте испачкаем руки в самых основных примерах, связанных с клиентом и сервером . Это будет двустороннее коммуникационное приложение, в котором клиент приветствует сервер, а сервер отвечает.

Давайте создадим серверное приложение в классе с именем GreetServer.java со следующим кодом.

Мы включаем метод main и глобальные переменные, чтобы привлечь внимание к тому, как мы будем запускать все серверы в этой статье. В остальных примерах в статьях мы опустим этот тип более повторяющегося кода:

Давайте также создадим клиент под названием GreetClient.java с этим кодом:

Давайте запустим сервер; в вашей IDE вы делаете это, просто запустив его как Java-приложение.

А теперь давайте отправим приветствие на сервер с помощью модульного теста, который подтверждает, что сервер действительно отправляет приветствие в ответ:

Не волнуйтесь, если вы не совсем понимаете, что здесь происходит, так как этот пример призван дать нам представление о том, чего ожидать в дальнейшем в статье.

В следующих разделах мы проанализируем связь сокетов , используя этот простой пример, и углубимся в детали с большим количеством примеров.

4. Как Работают Сокеты

Мы будем использовать приведенный выше пример, чтобы пройти через различные части этого раздела.

По определению, сокет -это одна конечная точка двустороннего канала связи между двумя программами, запущенными на разных компьютерах в сети. Сокет привязан к номеру порта , чтобы транспортный уровень мог идентифицировать приложение, которому предназначены данные для отправки.

4.1. Сервер

Обычно сервер работает на определенном компьютере в сети и имеет сокет, привязанный к определенному номеру порта. В нашем случае мы используем тот же компьютер, что и клиент, и запустили сервер по порту 6666 :

Сервер просто ждет, слушая сокет, чтобы клиент сделал запрос на подключение. Это происходит на следующем шаге:

Когда код сервера встречает метод accept , он блокируется до тех пор, пока клиент не сделает запрос на подключение к нему.

Если все идет хорошо, сервер принимает соединение. После принятия сервер получает новый сокет clientSocket , привязанный к тому же локальному порту, 6666 , а также имеет свою удаленную конечную точку, установленную на адрес и порт клиента.

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

Для каждого нового клиента серверу требуется новый сокет, возвращаемый вызовом accept . ServerSocket используется для продолжения прослушивания запросов на подключение в соответствии с потребностями подключенных клиентов. Мы еще не допустили этого в нашем первом примере.

4.2. Клиент

Клиент должен знать имя хоста или IP-адрес машины, на которой работает сервер, и номер порта, на котором сервер прослушивает.

Чтобы сделать запрос на подключение, клиент пытается встретиться с сервером на компьютере и порту сервера:

Клиент также должен идентифицировать себя с сервером, чтобы привязаться к локальному номеру порта, назначенному системой, который он будет использовать во время этого соединения. Мы не занимаемся этим сами.

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

5. Непрерывная Связь

Таким образом, это полезно только в запросах ping, но представьте, что мы хотели бы внедрить сервер чата, и, безусловно, потребуется непрерывная обратная связь между сервером и клиентом.

Обратите внимание, что мы добавили условие завершения, при котором цикл while завершается, когда мы получаем символ точки.

Мы запустим Echo Server , используя метод main так же, как мы это сделали для GreetServer . На этот раз мы запускаем его на другом порту, таком как 4444 чтобы избежать путаницы.

Клиент Echo похож на Отличный клиент , поэтому мы можем дублировать код. Мы разделяем их для ясности.

В другом тестовом классе мы создадим тест, чтобы показать, что несколько запросов к EchoServer будут обслуживаться без закрытия сокета сервером. Это верно до тех пор, пока мы отправляем запросы от одного и того же клиента.

Работа с несколькими клиентами-это другой случай, который мы рассмотрим в следующем разделе.

Давайте создадим метод setup для инициирования соединения с сервером:

Мы также создадим метод tearDown для освобождения всех наших ресурсов, это лучшая практика для каждого случая, когда мы используем сетевые ресурсы:

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

6. Сервер С Несколькими Клиентами

Несмотря на то, что предыдущий пример был улучшением по сравнению с первым, он все еще не является отличным решением. Сервер должен иметь возможность обслуживать множество клиентов и множество запросов одновременно.

Работа с несколькими клиентами-это то, что мы рассмотрим в этом разделе.

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

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

Мы сделаем это, чтобы создать новый сокет для каждого нового клиента и обслуживать запросы этого клиента в другом потоке. Количество клиентов, обслуживаемых одновременно, будет равно количеству запущенных потоков.

Основной поток будет выполнять цикл while, когда он прослушивает новые соединения.

Хватит разговоров, давайте создадим еще один сервер под названием EchoMultiServer.java. Внутри него мы создадим класс потока обработчика для управления коммуникациями каждого клиента в его сокете:

Обратите внимание, что теперь мы вызываем accept внутри цикла while . Каждый раз, когда выполняется цикл while , он блокирует вызов accept до тех пор, пока не подключится новый клиент , а затем для этого клиента создается поток обработчика EchoClientHandler .

Давайте запустим наш сервер, используя его основной метод на порту 5555 .

Для ясности мы все равно поместим тесты в новый набор:

Мы могли бы создать столько тестовых случаев, сколько нам заблагорассудится, каждый из которых порождает нового клиента, и сервер будет обслуживать их все.

7. Заключение

В этом уроке мы сосредоточились на введении в программирование сокетов через TCP/IP и написали простое клиент-серверное приложение на Java.

Собственно, предлагаю вам на растерзание рассмотрение свое решение.

1. Фоновый режим

Сделать это можно, например, так:

public class NotificationService extends Service

public class LocalBinder extends Binder NotificationService getService() return NotificationService.this;
>
>

@Override
public void onCreate() super.onCreate();

@Override
public IBinder onBind(Intent intent) return mBinder;
>

@Override
public void onDestroy()

// Здесь выполняем инициализацию нужных нам значений
// и открываем наше сокет-соединение
private void startService()

> catch (InterruptedException e) e.printStackTrace();
>
>

// данный метод открыает соединение
public void openConnection() throws InterruptedException
try

// WatchData - это класс, с помощью которого мы передадим параметры в
// создаваемый поток
WatchData data = new WatchData();
data.email = acc_email;
data.ctx = this;

// создаем новый поток для сокет-соединения
new WatchSocket().execute(data);

> catch (Exception e) // TODO Auto-generated catch block
e.printStackTrace();
>
>
>

  1. Получает каким-либо образом e-mail пользователя, который нужно будет передать на сервер (в примере я его просто присвоил);
  2. Передает этот e-mail в поток, который создаст сокет-соединение.
2. Создание потока

Зачем мы вообще создаем поток? Затем, что сервисы в андроиде не создают собственного, а выполняются в потоке приложения, что может (и будет!) тормозить наше приложение. Подробнее про сервисы — тут.
Итак, поток создавать нужно. Я для этой задачи решил использовать хрень под названием AsyncTask.
Для создания нужного мне AsyncTask'а я сделал два класса, о которых писал выше: WatchData и WatchSocket

WatchSocket:
class WatchSocket extends AsyncTask<WatchData , Integer, Integer>
Context mCtx;
Socket mySock;

protected void onProgressUpdate(Integer. progress)

protected void onPostExecute(Integer result)
// Это выполнится после завершения работы потока
>

protected void onCancelled(Object result)
// Это выполнится после завершения работы потока
>


protected Integer doInBackground(WatchData. param)
InetAddress serverAddr;

mCtx = param[0].ctx;
String email = param[0].email;

try while(true)
serverAddr = InetAddress.getByName("192.168.0.10");
mySock = new Socket(serverAddr, 4505);

// открываем сокет-соединение
SocketData data = new SocketData();
data.ctx = mCtx;
data.sock = mySock;

String message = email;
// Посылаем email на сервер
try PrintWriter out = new PrintWriter( new BufferedWriter( new OutputStreamWriter(mySock.getOutputStream())),true);

Здесь может возникнуть вопрос: «Зачем делать еще один поток? Почему не слушать сервер в этом же потоке?»

Отвечаю:
Во время моих экспериментов с AsyncTask'ами выяснилось, что при закрытии соединения методом «сервер взял и вырубился», слушающий его AsyncTask тупо берет и заканчивает работу. Пересоединить сокет с сервером прямо в том же таске у меня, как я ни бился — не вышло. Не берусь заявлять, что этого сделать невозможно — просто предлагаю свое рабочее решение.
Итак, чтобы слушать сервер, создается дополнительный поток GetPacket. Ему, в качестве параметров, в классе SocketData передаются открытый нами сокет и контекст приложения.

3. Слушаем сервер

Собственно, код второго потока:

class SocketData
Socket sock;
Context ctx;
>

class GetPacket extends AsyncTask<SocketData, Integer, Integer>
Context mCtx;
char[] mData;
Socket mySock;

>
catch(Exception e)
Toast.makeText(mCtx, "Socket error: " + e.getMessage(), Toast.LENGTH_LONG).show();
>
>

protected void onPostExecute(Integer result)
// Это выполнится после завершения работы потока
>

protected void onCancelled(Object result)
// Это выполнится после завершения работы потока
>

protected Integer doInBackground(SocketData. param)
mySock = param[0].sock;
mCtx = param[0].ctx;
mData = new char[4096];


Существует очень много приложений (на Android и на любых других ОС), которые взаимодействуют друг с другом с помощью соединения по сети. Например, к таким приложениям можно отнести любой месседжер: WhatsApp, Viber и т.д. Как правило, соединение между приложениями достигается путём использования сокетов.

В связи с этим одним из основных применений сокетов служит использование их в качестве средства коммуникации.

В Android сокеты по умолчанию используют для передачи данных протокол TCP/IP вместо UDP. Важной особенностью этого протокола является гарантированная доставка пакетов с данными от одной конечной точки до другой, что делает этот протокол более надёжным. Протокол UDP не гарантирует доставку пакетов, поэтому этот протокол следует использовать, когда надёжность менее важна, чем скорость передачи.

Сервер

В роли сервера здесь будет выступать судья, поскольку он должен принимать ответы от всех команд и контролировать процесс игры. Для начала создадим класс SocketServer, наследующий от Thread.

Задача сервера заключается в том, чтобы слушать заданный порт и принимать входящие подключения. Однако поскольку у нас должно быть 4 клиента, их нужно как-то различать. Для этих целей создадим класс UserManager, целью которого будет связывание сокета, полученного в результате метода accept() с пользователем, который установил соединение.

При подключении нового пользователя передаём в интерфейс данные о нём с помощью метода userConnected(), аналогично при дисконнекте вызываем userDisconnected().

Метод close() закрывает соединение с клиентом.

Теперь пробросим интерфейс в класс SocketServer.

В SocketServer создадим экземпляр класса UserManager и список, содержащий объекты этого класса. При создании нового сокета он передаётся в конструктор UserManager, после чего запускается поток.

Чтобы остановить сервер, напишем метод close() со следующим кодом.

Для начала здесь нужно закрыть все соединения с клиентами, после этого остановить прослушивание и остановить сокет методом close().

Теперь нужно создать интерфейс, который будет передавать данные в основной поток. Для этого создадим интерфейс со следующим кодом.

Теперь в главном потоке нужно создать экземпляр класса SocketServer и пробросить интерфейс.

Метод updatePlayer() обновляет список подключенных игроков при подключении\отключении кого-либо из игроков.

Примечание: если из потока нужно обновить элементы интерфейса, то следует вызывать runOnUiThread(), который позволяет выполнять код в UI-потоке.

Клиент

Клиент это игрок, который отвечает на вопрос, заданный судьёй. После сигнала он должен нажать на кнопку, чтобы дать ответ на вопрос.

Для реализации клиентского сокета создадим класс SocketClient.

Метод isConnected() проверяет, подключился ли клиент к серверу.

Метод isRunning() проверяет, запущен ли клиент.

Теперь создадим на активности экземпляр класса SocketClient и пробросим интерфейс.

После того, как будут заданы название команды и IP-адрес сервера, запустится метод connectToServer(), создающий поток, в котором инициализируется экземпляр SocketClient. Внутри него реализован интерфейс с методами onConnected() и messageReceived().

В методе onConnected() мы получаем событие, что клиент установил соединение, и вызываем метод sendPing(), который будет каждые 2 секунды посылать на сервер пинги. Это необходимо для более надежного соединения, поскольку отследить на стороне клиента, что сервер прекратил работу, может быть весьма затруднительно. В случае, если соединение теряется, начинает вызываться метод connectToServer() до тех пор, пока соединение не восстановится.

Когда вызывается метод активности onPause(), клиент останавливается с помощью следующего кода.

При возврате в активность восстановить соединение можно вручную, нажать на кнопку переподключения, которая вызовет метод connectToServer().

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


Клиент-серверная архитектура — наиболее распространенная структура приложений в Интернете. В этой архитектуре клиенты (т.е. персональные компьютеры, устройства Интернета вещей и т. д.) сначала запрашивают ресурсы с сервера. Затем сервер отправляет обратно соответствующие ответы на запросы клиентов. Чтобы это произошло, должен быть какой-то механизм, реализованный как на стороне клиента, так и на стороне сервера, который поддерживает эту сетевую транзакцию. Этот механизм называется коммуникацией посредством сокетов.

Почти каждое приложение, которое пол а гается на сетевые операции, такие как извлечение данных с удаленных серверов и загрузка файлов на сервер, широко использует сокеты “под капотом”. Несколько примеров таких приложений — браузеры, чат-приложения и одноранговые сетевые приложения.

В этой статье мы более подробно рассмотрим сокеты и простую клиент-серверную реализацию с использованием сокетов в Java.

Примечание: существует два типа сокетов: TCP и UDP. Поскольку большинство сетевых приложений используют TCP, здесь я буду говорить только о TCP-сокетах и их реализации.

Сокет — это программная (логическая) конечная точка, устанавливающая двунаправленную коммуникацию между сервером и одной или несколькими клиентскими программами. Сокет — это нечто “программное”. Другими словами, сокет не существует на физическом уровне. Прикладное программное обеспечение определяет сокет так, чтобы он использовал порты на основном компьютере для его реализации. Это позволяет программистам комфортно работать с низкоуровневыми деталями сетевых коммуникаций, такими как порты, маршрутизация и т. д., внутри прикладного кода.

TCP-сокет устанавливает связь между клиентом и сервером в несколько этапов.


  • Socket() — на сервере создается конечная точка для коммуникации.
  • Bind() — сокету присваивается уникальный номер и для него резервируется уникальная комбинации IP-адреса и порта.
  • Listen() — после создания сокета сервер ожидает подключения клиента.
  • Accept() — сервер получает запрос на подключение от клиентского сокета.
  • Connect() — клиент и сервер соединены друг с другом.
  • Send()/Recieve() — обмен данными между клиентом и сервером.
  • Close() — после обмена данными сервер и клиент разрывают соединение.

На каждой из перечисленных выше стадий коммуникации сокетов “под капотом" происходит много всего сложного. Однако этих знаний вполне достаточно для понимания и демонстрации того, как работает коммуникация посредством TCP-сокетов.

К настоящему времени мы уже достаточно знаем о TCP-сокетах. Давайте теперь посмотрим на них в действии.

Давайте посмотрим, как мы можем реализовать коммуникацию сокетов в Java. Мы сейчас напишем две Java-программы. Одной будет программа, запущенная на сервере, а другой — клиентская программа, которая будет взаимодействовать с сервером.

Реализация серверного сокета

В приведенной выше программе сервер открывает сокет с порта 50001 на серверной машине и ожидает клиента на server.accept() . После подключения клиента создается экземпляр выходного потока. Это может быть использовано для отправки данных с сервера на подключенный клиент. Именно это и делает serverOutput.writeBytes() . После отправки данных соединение с клиентом завершается.

Теперь давайте создадим клиент для взаимодействия с серверным сокетом, созданным выше.

Реализация клиентского сокета

Запуск программ

Сначала запустите серверную Java-программу, а затем клиентскую Java-программу (потому что сервер уже должен работать для подключения клиента). Вы увидите Received data: Java Revisited в терминале, где работает клиентская программа. Вот что здесь произошло: серверная программа отправила данные клиенту по запросу, а клиентская программа вывела их на терминал.

В этой статье мы обсудили, что такое сокеты и Java-реализация связи TCP-сокетов.

Читайте также: