TCP-соединение не устанавливается после перфорации
Поэтому я пытаюсь реализовать дырокол TCP между двумя мобильными устройствами (мобильный Android и ноутбук с мобильным широкополосным модемом USB). Я использую другой ноутбук, подключенный к Wi-Fi, чтобы действовать в качестве сервера.
Сначала я получаю ноутбук для подключения к серверу, который отображает его общедоступный IP-адрес и номер порта NAT, который видит сервер.
try{
System.out.println("connect to server");
Socket connectToServer = new Socket();
System.out.println("49");
connectToServer.bind(new InetSocketAddress(myIPAddress.getHostAddress(), myPort));
System.out.println("51");
connectToServer.connect(new InetSocketAddress(serverIPAddress.getHostAddress(), serverPort));
System.out.println("connected to server: " + connectToServer);
connectToServer.close();
}
catch (Exception e){
System.out.println("Exception 1 :" + e.toString());
//connectToServer.close();
}
Мобильный телефон делает то же самое, поэтому я вижу, что это общедоступный IP и номер порта NAT. Ноутбук и мобильный телефон закрывают соединения с сервером.
Затем ноутбук подключается к мобильному телефону, используя тот же локальный IP-адрес и номер порта (который он использовал для подключения к серверу) и используя общедоступный IP-адрес мобильного телефона и номер порта NAT.
try{
Socket punchSocket = new Socket();
System.out.println("33");
punchSocket.bind(new InetSocketAddress(myIPAddress.getHostAddress(), myPort));
System.out.println("35");
punchSocket.connect(new InetSocketAddress(mobileAddress.getHostAddress(), mobilePort));
punchSocket.close();
}
catch(Exception e){
System.out.println("exception 2 caught " + e.toString());
}
И поскольку мобильный телефон отстает от сети, время соединения истекает, и я получаю исключение.
try{
Socket mobileSocket = new Socket();
System.out.println("105");
mobileSocket.bind(new InetSocketAddress(myIPAddress.getHostAddress(), myPort));
System.out.println("124");
mobileSocket.connect(new InetSocketAddress(mobileAddress.getHostAddress(), mobilePort));
System.out.println("connection made: " + mobileSocket);
mobileSocket.close();
}
catch(Exception e){
System.out.println("exception 2 caught " + e.toString());
}
Наконец, я снова пытаюсь подключиться к мобильному телефону. Пока ноутбук пытается подключиться к мобильному телефону, я также инициирую подключение с мобильного телефона. Используя тот же IP-адрес и порт (мобильный телефон, используемый для подключения к серверу), я подключаюсь к общедоступному IP-адресу и порту ноутбука.
но и мобильный телефон, и ноутбук пытаются подключиться только по истечении времени ожидания.
Вместо того, чтобы просто пытаться подключиться с ноутбука на мобильный во второй раз, я также попытался заставить ноутбук прослушивать соединение:
ServerSocket welcomeSocket = new ServerSocket(myPort, 50, myIPAddress);
welcomeSocket.setSoTimeout(5);
System.out.println("Start listening for mobile to connect");
while(true){
try{
Socket newsock = welcomeSocket.accept();
System.out.println("connection made: " + newsock);
}
catch(Exception e) {
System.out.println("exception 4 caught " + e.toString());
}
}
Но это тоже не сработало. Понятия не имею почему. Есть ли у меня способ проверить, позволит ли NAT моего мобильного провайдера даже пробивать дыры?
Вот как я подключаюсь с мобильного телефона:
try {
Log.d("stop", "line 64");
InetAddress serverIPAddress = InetAddress.getByName(serverIP);
InetAddress myIPAddress = InetAddress.getByName(myIP); //InetAddress.getLocalHost();
Log.d("stop", "line");
Log.d("localHost: ", myIPAddress.getHostAddress());
Socket clientSocket = new Socket();
Log.d("stop", "line 70");
clientSocket.bind(new InetSocketAddress(myIPAddress.getHostAddress(), myPort));
Log.d("stop", "line 72");
clientSocket.connect(new InetSocketAddress(serverIPAddress.getHostAddress(), serverPort));
Log.d("stop", "line 74");
BufferedReader inFromClient = new BufferedReader(new InputStreamReader(clientSocket.getInputStream()));
receivedMessage = inFromClient.readLine();
Log.d("stop", "line 78");
clientSocket.close();
//Toast.makeText(this, "socket connected", Toast.LENGTH_SHORT).show();
return receivedMessage; //receivedMessage;
} catch (IOException e) {
//Toast.makeText(this, "socket caughyt", Toast.LENGTH_SHORT).show();
e.printStackTrace();
error1 = "no Server";
Log.d("error", error1 + e.getMessage());
}
Я провел большую часть сегодняшнего дня, пытаясь понять это. Когда serversock et используется на ноутбуке, ошибка на мобильном телефоне - тайм-аут. Это случайно сработало однажды прошлой ночью (1 час ночи), и я лег спать. Сегодня снова не работает. Не знаю, почему нет. Любая помощь будет оценена.
РЕДАКТИРОВАТЬ:
Хорошо, теперь, после того как ноутбук пытается подключиться к мобильному устройству и выходит из строя (а затем постоянно пытается подключиться к мобильному устройству, а не к сценарию сокета сервера), мобильное устройство пытается подключиться к ноутбуку и подключается. Если я удаляю строки: BufferedReader inFromClient = new BufferedReader(new InputStreamReader(clientSocket.getInputStream())); receiveMessage = inFromClient.readLine(); тогда мобильный говорит, что соединился. если я оставляю линии, мобильный телефон получает ошибку, что узел сбрасывает соединение.
Однако в любом случае ноутбук, кажется, не реагирует вообще. Если я использую альтернативу сокета сервера вместо попытки подключения с ноутбука, мобильный телефон не сможет подключиться при истечении времени ожидания соединения.
1 ответ
Похоже, у вас есть хорошее решение для поиска правильных общедоступных адресов двух устройств. Однако даже с этой информацией многие (или, может быть, даже большинство) мобильных сетей по-прежнему не разрешают входящие TCP-соединения с мобильным устройством.
Некоторые из них будут иметь разные ограничения для разных типов устройств, поэтому может показаться, что они иногда работают с мобильным широкополосным модемом, но не с телефоном. Эти различия могут даже быть простыми, потому что модем и телефонные соединения устанавливаются по-разному в сетях операторов (например, используйте разные APN с разными правилами).
Скорее всего, вы в курсе, но вы все равно можете обмениваться данными сквозным образом, используя сервер в качестве "промежуточной" точки в середине - это, очевидно, увеличивает сложность серверного уровня в вашем решении, но имеет преимущество в надежности работы большинства операторов. Подход выглядит примерно так:
- Ноутбук подключается к серверу
- Мобильное соединение с сервером
- Ноутбук запрашивает у сервера список подключенных устройств и "обнаруживает" Mobile
- Ноутбук отправляет сообщение на мобильный. Фактически он отправляет сообщения на сервер с указанием, что они должны быть отправлены на мобильный телефон.
- Сервер уведомляет мобильный телефон о том, что для него доступно сообщение (например, с помощью какого-либо уведомления о сообщениях, такого как Google Cloud Messaging, или просто с помощью устройств, которые регулярно опрашивают, чтобы узнать, есть ли у них сообщения).
- Мобильный телефон получает сообщения с сервера.