Como conectar um servidor a um cliente fora da rede local?

Oi! Faz pouco tempo que comecei a tentar aprender programação com sockets no Java e estou tendo algumas dificuldades. Eu procurei um pouco sobre o assunto e tentei montar o programa, que funciona perfeitamente com o localhost e com conexões na rede local. Mas quando tento fazer a conexão com um computador fora da rede, a conexão não ocorre. Tentei utilizar alguns recursos de UPnP, mas ainda não consegui fazer funcionar. Provavelmente, estou usando de forma errada

Esse é o código do servidor:

package com.gados.net;

import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.io.IOException;
import java.net.InetAddress;
import java.net.ServerSocket;
import java.net.Socket;
import java.net.SocketTimeoutException;
import java.util.ArrayList;

import com.dosse.upnp.*;

public class Server implements Runnable {

	
	private ServerSocket ss;
	private DataOutputStream out;
	private DataInputStream in;
	private InetAddress ip;
	private int port;
	public int maxClients = 2, minClients = 2;
	private ArrayList<Socket> clients = new ArrayList<>();
	private ServerListener listener;
	public Thread thread;
	
	
	public boolean start = false;
	private boolean open = true;
	
	public Server(int port, ServerListener listener) {
		thread = new Thread(this);
		this.listener = listener;
		this.port = port;
		
		
		try {
			ss = new ServerSocket(port);
			this.ip = ss.getInetAddress();
			ss.setSoTimeout(100000);
		} catch (IOException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
		
	}
	
	public void listenPort() {

			
		
		
		try {
			
			if (UPnP.isUPnPAvailable()) { //is UPnP available?
                if (UPnP.isMappedTCP(port)) { //is the port already mapped?
                    System.out.println("UPnP port forwarding not enabled: port is already mapped");
                } else if (UPnP.openPortTCP(port)) { //try to map port
                    System.out.println("UPnP port forwarding enabled");
                } else {
                    System.out.println("UPnP port forwarding failed");
                }
            } else {
                System.out.println("UPnP is not available");
            }
			 
			
			System.out.println("listening to port: " + port);
			Socket server = ss.accept();
			System.out.println("Conncted to " + server.getRemoteSocketAddress());
			
			in = new DataInputStream(server.getInputStream());
			
			System.out.println("message from " + server.getRemoteSocketAddress() + ": " + in.readUTF());
			
			out = new DataOutputStream(server.getOutputStream());
			
			clients.add(server);
			
			System.out.println("connection stablished with " + server.getLocalSocketAddress());
//			out.writeUTF("ping");
			
			listener.clientConnected(in, out);
			
		
			
		} catch(SocketTimeoutException e) {
			System.out.println("timed out!");
		} catch (IOException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		} 
		
		
		
	}
	
	
	private void kickClient(Socket client) throws IOException {
		for(int i = 0; i < clients.size(); i++) {
			if(!client.equals(clients.get(i))) continue;
			
			clients.get(i).close();
			clients.remove(i);
			break;
		}
	}
	
	private void closeServer() throws IOException {
		for(Socket s : clients) {
			kickClient(s);
		}
		ss.close();
	}
	
	
	
	@Override
	public void run() {
		// TODO Auto-generated method stub
		while(open) {
			
			while(!start || clients.size() < minClients || clients.size() <= maxClients && !start) {
				
				listenPort();
			}
			
			listener.receivedInput(in);
			listener.writeOutput(out);
		}
		
		try {
			closeServer();
		} catch (IOException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
		
	}

}

Esse é o código do cliente:

package com.gados.net;

import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.io.IOException;
import java.net.InetAddress;
import java.net.Socket;

public class Client implements Runnable{

	private Socket socket;
	private DataOutputStream out;
	private DataInputStream in;
	private String ip;
	private int port;
	private boolean open = true;
	private ClientListener listener;
	
	public Thread thread;
	
	public Client(String ip, int port, ClientListener listener) {
		thread = new Thread(this);
		this.ip = ip;
		this.port = port;
		this.listener = listener;
	
	}
	
	private void connect() {
		try {
			System.out.println("Trying to connect to " + ip + " on port " + port);
			socket = new Socket(ip, port);
			
			
			System.out.println("connected to " + ip + " on port " + port);
			out = new DataOutputStream(socket.getOutputStream());
			
			
			in = new DataInputStream(socket.getInputStream());
			
			System.out.println("server return: " + in.readUTF());
			
			listener.connected(in, out);
			
		} catch (IOException e) {
			// TODO Auto-generated catch block
			listener.unknownHost();
			e.printStackTrace();
		}
	}

	@Override
	public void run() {
		// TODO Auto-generated method stub
		while(open) {

			
			if(socket == null) connect();
			
			
			
			listener.receivedInput(in);
			listener.writeInput(out);
			
		}
		
		try {
			socket.close();
		} catch (IOException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
	}

}

Eu estou iniciando o servidor e o cliente assim:

if(JOptionPane.showConfirmDialog(mainWindow, "server?") == 0) {
			int port = Integer.parseInt(JOptionPane.showInputDialog("port:"));
			Server sv = new Server(port, new ServerIn());
			sv.thread.start();
			sv.start = true;
		} else {
			
			String ip = JOptionPane.showInputDialog("IP:");
			int port = Integer.parseInt(JOptionPane.showInputDialog("port:"));
			
			Client cl = new Client(ip, port, new ClientIn());
			cl.thread.start();
		}

Os parâmetros “ServerListener” e “ClientListener” são interfaces com métodos que ainda não estão fazendo nada.

(Se faltou alguma informação importante, me avise. Eu sei bem pouco mesmo sobre o assunto)

Algém pode me ajudar? Obrigado!

Tenha em mente que para que a conexão seja possível, o servidor precisa estar acessível, e isso vai além do seu código.

Pense num servidor web que vc suba na sua máquina e eu vá tentar acessá-lo. Vc teria que me fornecer um endereço que eu consiga acessar (por exemplo, o endereço teria que responder à um ping executado por mim).

Para que algum serviço rodando na sua sua rede seja disponível fora dela, vc teria que usar serviços como DDNS ou NOIP junto com configurações no seu roteador (NAT) para fazer o redirecionamento.

Acho que entendi, mas é mais complexo do que eu esperava. Teria como você dar um exemplo sobre como fazer ou me recomendar algum artigo sobre o assunto que eu possa ler?

Obrigado pela ajuda!

Tem roteadores que fornecem funções para isso já de fábrica, já vi alguns com opção de usar o DDNS por exemplo.

Como cada roteador tem uma forma diferente de configurar, tu pode dá uma pesquisada por “Configurar NAT roteador” de acordo com o modelo que vc usa. Pesquise tb por NO-IP e DDNS.

A ideia é permitir que os acessos vindos da internet em uma determinada porta seja roteada para a máquina onde o serviço está rodando. Imagine:

{{internet}} → acessa o IP que cai no seu roteador → {{seu-roteador}} → redireciona para um IP da rede em uma determinada porta → {{maquina-da-rede}}

1 curtida

Valeu, cara! Vou dar uma procurada aqui.