(webSocket) Не могу подключиться к серверу с телефона. C#
Есть консольное приложение для сервера. Сервер запущен на VPS. Есть два устройства на одном wifi. Первое - это компьютер. С него спокойно могу зайти. Через unity билд для андроида внутри юнити. Второе это мой андроид. С него не могу подключиться к серверу.
await webSocket.ConnectAsync(new Uri("ws://45.143.93.104:8080"), CancellationToken.None);
Я пришёл к выводу, что мне нужен сертификат SSL/TLS, чтобы андроид пропустил соединение. Но я понятия не имею как его сделать. К тому же на ip. У меня же не домен...
Может кто-то дать совет? Обязателен ли этот сертификат? Других путей нет? Обязательно через wss потом делать?
Ответы (1 шт):
Android 9+ (Pie, API 28) блокирует все незашифрованные (ws://
, http://
) соединения по умолчанию. Это можно обойти, добавив разрешение в AndroidManifest.xml
:
<application android:usesCleartextTraffic="true" />
Но это временное решение, так как Google активно продвигает wss://
и в будущем возможны более жесткие ограничения (как с http://
). Лучше сразу использовать wss://
, чтобы избежать проблем в будущем.
Если все-таки нужен wss://
, то можно прокинуть WebSocket через Nginx с Let's Encrypt - тогда получите настоящий wss://
без проблем. Nginx принимает wss://
, а сервер продолжает работать на ws://
внутри VPS.
Let's Encrypt не выдает сертификаты для IP-адресов, поэтому необходимо зарегистрировать домен и настроить его на свой сервер. Если хочется обойтись без этого, можно использовать самоподписанный сертификат. Android не доверяет самоподписанным сертификатам, поэтому придется вручную добавить его в каждое устройство. Говорят, можно настроить Android приложение чтобы оно доверяло самоподписанному сертификату, тогда не придется его добавлять на каждое устройство вручную. Но я не нашел инструкции для Unity, только вот эту: Trust my own self-signed certificate in local network using Android Native, Android Studio and retrofit.
Установка Nginx
sudo apt update
sudo apt install nginx -y
Проверяем, что он работает:
systemctl status nginx
Если не запущен, запускаем:
sudo systemctl enable nginx
sudo systemctl start nginx
Предположим, что ваше C#-приложение работает на порту 8080
и слушает ws://123.123.123.123:8080
. Проверим, что сервер работает локально:
curl -i ws://localhost:8080
(примечание) В этом примере curl
используется как быстрая проверка доступности порта, а не полноценный WebSocket-клиент. curl
не поддерживает WebSocket по-настоящему, так что вы получите ошибку вроде:
HTTP/1.1 400 Bad Request
Это нормально, если сервер вообще отвечает - значит, порт открыт.
Если WebSocket-сервер отвечает на ws://123.123.123.123:8080
, но не доступен на ws://localhost:8080
, значит сервер слушает только внешний интерфейс. Используйте 0.0.0.0
, чтобы сервер слушал все подключения.
Теперь настроим бесплатный сертификат от Let's Encrypt (замените example.com
на ваш домен):
sudo apt install certbot python3-certbot-nginx -y
sudo certbot certonly --nginx -d example.com
Если у вас только IP (без домена), то Let's Encrypt не выдаст сертификат. В этом случае вам придется использовать самоподписанный сертификат (я объясню в конце).
Сертификаты будут сохранены в:
/etc/letsencrypt/live/example.com/fullchain.pem
/etc/letsencrypt/live/example.com/privkey.pem
Проверяем, что сертификат установлен:
sudo certbot renew --dry-run
Настройка Nginx как реверс-прокси для WebSocket
Используем механизм переключения протокола, описанный здесь: WebSocket proxying.
Создадим конфиг Nginx, который будет перенаправлять wss://
на ws://
:
sudo nano /etc/nginx/sites-available/websocket
Добавляем конфигурацию (не забудьте заменить example.com
на свой домен):
server {
listen 443 ssl;
server_name example.com;
ssl_certificate /etc/letsencrypt/live/example.com/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/example.com/privkey.pem;
location / {
proxy_pass http://localhost:8080/;
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "Upgrade";
proxy_set_header Host $host;
}
}
server {
listen 80;
server_name example.com;
return 301 https://$host$request_uri;
}
Активируем конфигурацию:
sudo ln -s /etc/nginx/sites-available/websocket /etc/nginx/sites-enabled/
Проверяем синтаксис:
sudo nginx -t
Перезапускаем Nginx:
sudo systemctl restart nginx
Теперь вы можете подключаться к wss://example.com
вместо ws://123.123.123.123:8080
.
Проверка через wscat
(клиент WebSocket):
npm install -g wscat
wscat -c wss://example.com
Если все работает нормально, вы должны увидеть соединение.
Что делать, если нет домена?
Можно сгенерировать самоподписанный сертификат:
sudo openssl req -x509 -nodes -days 365 -newkey rsa:2048 \
-keyout /etc/ssl/private/nginx-selfsigned.key \
-out /etc/ssl/certs/nginx-selfsigned.crt
Затем заменить конфиг Nginx:
ssl_certificate /etc/ssl/certs/nginx-selfsigned.crt;
ssl_certificate_key /etc/ssl/private/nginx-selfsigned.key;
Минусы: такой сертификат необходимо вручную добавить в приложение.
Можно ли подключить сертификат в C#-сервер без Nginx?
Можно настроить свой WebSocket-сервер так, чтобы он работал с сертификатом Let's Encrypt напрямую.
Устанавливаем Certbot и запускаем команду:
certbot certonly --standalone -d example.com
Сертификаты сохраняются в /etc/letsencrypt/live/example.com/fullchain.pem
и privkey.pem
. Преобразуем файлы pem
в PFX:
openssl pkcs12 -export -out certificate.pfx -inkey privkey.pem -in fullchain.pem
Теперь можно загрузить сертификат SslStream:
var certificate = new X509Certificate2("certificate.pfx", "your_password");
var listener = new TcpListener(IPAddress.Any, 443);
listener.Start();
while (true)
{
var client = await listener.AcceptTcpClientAsync();
var sslStream = new SslStream(client.GetStream());
await sslStream.AuthenticateAsServerAsync(certificate);
}
Если же вы используете ASP.NET Core с Kestrel, обратитесь к разделу SSL/TLS Protocols в документации.
Let's Encrypt выдает сертификаты сроком на 90 дней, так что нужно автоматизировать обновление. По расписанию выполнять команду:
certbot renew --quiet
Можно добавить это в cron (crontab -e
):
0 0 * * 1 certbot renew --quiet && systemctl restart myapp
Плюсы:
- Нет сторонних прокси
Минусы:
- Нужно обновлять сертификаты вручную (или автоматизировать)
- Сервер должен быть доступен снаружи (Certbot требует порт
80
или443
).