Я пытаюсь создать систему, в которой телефон подключается к серверу и отправляет ему данные, данные обрабатываются, сохраняются и возвращаются на телефон. На сервере также есть VPN, к которому подключен телефон.
Моя идея состоит в том, чтобы использовать сокеты, которые должны работать в такой ситуации, но я не могу заставить их работать. Кроме того, как сказано в заголовке, я использую Python на стороне сервера, а приложение находится на Kotlin.
Я начал с тестирования сокетов Python с настройкой эхо-клиент-сервер, и это работает, затем я попытался создать клиент на Kotlin, но он не подключается к серверу. Код сервера такой:
import socket
HOST = "10.8.0.1"
PORT = 65432
with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as s:
s.bind((HOST, PORT))
s.listen()
print("Listening...")
conn, addr = s.accept()
with conn:
print(f"Connected by {addr}")
while True:
data = conn.recv(1024)
print(f"Recived: {data.decode("utf-8")}")
if not data:
break
conn.sendall(data)
Клиент имеет простой интерфейс:
Это код Котлина для этого:
package com.example.testsockets
import android.os.Bundle
import android.view.View
import android.widget.TextView
import androidx.appcompat.app.AppCompatActivity
import com.google.android.material.textfield.TextInputEditText
import java.net.InetSocketAddress
import java.net.Socket
import java.util.concurrent.Executors
class MainActivity : AppCompatActivity() {
private val socket = Socket()
private val executorService = Executors.newSingleThreadExecutor();
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
}
fun send(v: View?){
executorService.execute {
var entrada = findViewById<TextInputEditText>(R.id.entrada)
var salida = findViewById<TextView>(R.id.salida)
val host = "10.8.0.1"
val port = 65432
socket.connect(InetSocketAddress(host, port), 3000)
// This will be used to send to the server
val out = socket.getOutputStream()
out.write(entrada.text.toString().toByteArray(charset("UTF-8")))
}
}
}
И AndroidManifest:
<?xml version = "1.0" encoding = "utf-8"?>
<manifest xmlns:android = "http://schemas.android.com/apk/res/android"
xmlns:tools = "http://schemas.android.com/tools">
<uses-permission android:name = "android.permission.INTERNET"/>
<application
android:allowBackup = "true"
android:dataExtractionRules = "@xml/data_extraction_rules"
android:fullBackupContent = "@xml/backup_rules"
android:icon = "@mipmap/ic_launcher"
android:label = "@string/app_name"
android:roundIcon = "@mipmap/ic_launcher_round"
android:supportsRtl = "true"
android:theme = "@style/Theme.TestSockets"
tools:targetApi = "31">
<activity android:name = ".MainActivity" android:exported = "true">
<intent-filter>
<action android:name = "android.intent.action.MAIN" />
<category android:name = "android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
</application>
</manifest>
Наконец, я получаю сообщение об ошибке:
FATAL EXCEPTION: pool-1-thread-1
Process: com.example.testsockets, PID: 1897
java.lang.Error: java.net.SocketTimeoutException: failed to connect to /10.8.0.1 (port 65432) from /10.8.0.2 (port 45974) after 3000ms
at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1173)
at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:641)
at java.lang.Thread.run(Thread.java:764)
Caused by: java.net.SocketTimeoutException: failed to connect to /10.8.0.1 (port 65432) from /10.8.0.2 (port 45974) after 3000ms
at libcore.io.IoBridge.connectErrno(IoBridge.java:185)
at libcore.io.IoBridge.connect(IoBridge.java:129)
at java.net.PlainSocketImpl.socketConnect(PlainSocketImpl.java:137)
at java.net.AbstractPlainSocketImpl.doConnect(AbstractPlainSocketImpl.java:390)
at java.net.AbstractPlainSocketImpl.connectToAddress(AbstractPlainSocketImpl.java:230)
at java.net.AbstractPlainSocketImpl.connect(AbstractPlainSocketImpl.java:212)
at java.net.SocksSocketImpl.connect(SocksSocketImpl.java:436)
at java.net.Socket.connect(Socket.java:621)
at com.example.testsockets.MainActivity.send$lambda$0(MainActivity.kt:27)
at com.example.testsockets.MainActivity.$r8$lambda$qyWxoPew67mcJ-GNbw1keB5dm7k(Unknown Source:0)
at com.example.testsockets.MainActivity$$ExternalSyntheticLambda0.run(Unknown Source:2)
at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1167)
at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:641)
at java.lang.Thread.run(Thread.java:764)
какая система у тебя на компьютере? В некоторых системах брандмауэр может блокировать все порты, в других он может держать открытым 80 для собственного www-сервера (так что вы можете использовать его без изменений в брандмауэре), но в Linux порт 80 блокируется системой (все порты <1024 блокируются системой). ). Другой вопрос: если у вас на компьютере много сетевых устройств — Wi-Fi, проводная локальная сеть и т. д., потому что каждое устройство может иметь собственный IP-адрес, и если вы используете IP-адрес, назначенный проводной локальной сети, вы не сможете подключиться с помощью wifi — но если сервер использует 0.0.0.0 затем использовать все устройства для приема соединений.
Создание соединения с помощью сокета может быть интересной задачей, но проще и полезнее может быть www server с HTTP protocol. Вы можете использовать его с приложением на Android (и отправлять данные только в формате JSON без HTML), а также проверять его в браузере (и создавать веб-страницу с HTML) или использовать такие программы, как postman, для проверки соединения.
Мне интересно, проблема в VPN. Сокет Python и VPN - Qaru QaruSite
на сервере работает Windows Server 2022, и порт разрешен в брандмауэре. Я протестировал его, запустив клиент Python на другом компьютере, и он подключается и позволяет мне отправлять и получать данные. Этот компьютер подключается из той же сети, что и телефон, и использует ту же VPN, поэтому я не думаю, что это проблема брандмауэра или VPN, поэтому я очень расстроен, это не имеет особого смысла. Спасибо за советы, я их все проверил, вдруг что-то пропустил. И 0.0.0.0 тоже не заработала.
Я могу попробовать решение, предложенное @furas, если не смогу найти другое решение, но сейчас мне просто любопытно узнать, почему оно не работает.
Я нашел способ заставить его работать, но он еще не идеален, поэтому он не попадает в раздел ответов. Я использовал Chaquopy, чтобы создать сокет Python на устройстве Android и отправить с его помощью данные. Проблема в том, что я пока не знаю, как получить данные.






Мне удалось заставить это работать, вот код:
Python-сервер:
import socket
import threading
import logging
import struct
HOST = "10.8.0.1"
PORT = 65432
# Formato del logging
logging.basicConfig(level=logging.DEBUG, format='%(asctime)s %(levelname)s %(message)s')
# Creación del servidor de sockets
server = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
server.bind((HOST, PORT))
server.listen(5)
# Lista de clientes conectados
clientes = []
def client(cliente_socket):
try:
while True:
#Receive the size of the data that is going to be sent
tam_datos_bytes = cliente_socket.recv(1024)
if not tam_datos_bytes:
break
tam_datos = int(tam_datos_bytes.decode("utf-8"))
#Receive the data
data = b''
while len(data) < tam_datos:
packet = cliente_socket.recv(tam_datos - len(data))
if not packet:
break
data += packet
if len(data)<100:
if not data or data.decode("utf-8") == "EOC":
break
#Send the response
response = "Received"
tam_response = len(response)
cliente_socket.sendall(struct.pack('!I', tam_response)) # Envía el tamaño del mensaje como un entero de 4 bytes
cliente_socket.sendall(response)
except Exception as e:
logging.error(f"Error receiving data: {e}")
finally:
# Close socket and remove it
cliente_socket.close()
if cliente_socket in clientes:
clientes.remove(cliente_socket)
# Listen for conecctions
while True:
# Accept Conection
cliente_socket, address = server.accept()
logging.info(f"Conectado el cliente {address}")
# Add client to list
clientes.append(cliente_socket)
# Create a thread to manage the client
hilo_cliente = threading.Thread(target=client, args=(cliente_socket,))
hilo_cliente.start()
Котлин-клиент:
class Sockets {
private var text: TextView
constructor(texto: TextView,message: String){
text = texto
sendMessage(message)
}
fun sendMessage(message: String) = CoroutineScope(Dispatchers.IO).launch {
try {
// Create the connection
val socketChannel = SocketChannel.open()
socketChannel.connect(InetSocketAddress("10.8.0.1", 65432))
// Send the message length
val tamMensaje = message.length
val tamMensajeBuffer = ByteBuffer.wrap(tamMensaje.toString().toByteArray()) // 4 bytes para el tamaño (int)
socketChannel.write(tamMensajeBuffer)
// send the message
val mensajeBuffer = ByteBuffer.wrap(message.toByteArray())
while (mensajeBuffer.hasRemaining()) {
socketChannel.write(mensajeBuffer)
}
// Wait for a response and write it on a TextView
val respuesta = recibirMensaje(socketChannel)
withContext(Dispatchers.Main) {
if (respuesta != null) {
text.text = respuesta
}
}
// Close socket
socketChannel.close()
} catch (e: Exception) {
withContext(Dispatchers.Main) {
e.printStackTrace()
}
}
}
private fun recibirMensaje(socketChannel: SocketChannel): String? {
return try {
// Recive the message from the server
val tamMensajeBuffer = ByteBuffer.allocate(4)
while (tamMensajeBuffer.hasRemaining()) {
if (socketChannel.read(tamMensajeBuffer) <= 0) {
return null
}
}
tamMensajeBuffer.flip()
val tamMensaje = tamMensajeBuffer.int
// Recive the data
val dataBuffer = ByteBuffer.allocate(tamMensaje)
while (dataBuffer.hasRemaining()) {
if (socketChannel.read(dataBuffer) <= 0) {
return null
}
}
dataBuffer.flip()
var respuesta = String(dataBuffer.array(),0,dataBuffer.array().size)
respuesta
} catch (e: Exception) {
Log.d("conexion",e.printStackTrace().toString())
null
}
}
}
При этом у нас есть сервер Python, который позволяет использовать несколько соединений и сообщения разных размеров, а также клиент Kotlin для подключения к нему, отправки данных и их получения.
Вам следует проверить конфигурацию брандмауэра на сервере. Вы можете попробовать использовать telnet-клиент на устройстве Android или просто веб-браузер, посетив
http://10.8.0.1:65432/, чтобы посмотреть, отреагирует ли сервер. Вы также можете попробовать установить (временно)0.0.0.0какHOSTдля сервера.