3

调查 Servlet 我创建了一个简单的聊天并在本地 IP 上对其进行了测试——一切正常。但是当我尝试通过真实网络对其进行测试时,连接被拒绝 - java.net.ConnectException: Connection refused: connect。是我拥有的动态 IP 中的原因,还是需要其他设置?提前致谢!

服务器:

/**
 * Created by rnd on 7/4/2017.
 */

import java.io.*;
import java.net.*;
import java.nio.charset.Charset;
import java.nio.charset.StandardCharsets;
import java.util.*;

public class VerySimpleChatServer {

    ArrayList clientOutputStreams;

    public static void main (String[] args) {
        new VerySimpleChatServer().go();
    }

    public void go() {
        clientOutputStreams = new ArrayList();
        try {
            ServerSocket serverSock = new ServerSocket(5000);

            while(true) {
                Socket clientSocket = serverSock.accept();

                Charset charset = StandardCharsets.UTF_8;
                OutputStreamWriter osw = new OutputStreamWriter( clientSocket.getOutputStream(), charset );
                PrintWriter writer = new PrintWriter( new BufferedWriter( osw ) );

//                PrintWriter writer = new PrintWriter(clientSocket.getOutputStream());

                writer.println("Welcome to the chat 7 kids.... Семеро Козлят");
                writer.flush();

                clientOutputStreams.add(writer);
                Thread t = new Thread(new ClientHandler(clientSocket));
                t.start() ;
                System.out.println("got a connection");
            }
        } catch(Exception ex) {
            ex.printStackTrace();
        }
    } // Закрываем go


public class ClientHandler implements Runnable {

    BufferedReader reader;
    Socket sock;

    public ClientHandler(Socket clientSocket) {

        try {
            sock = clientSocket;
            InputStreamReader isReader = new InputStreamReader(sock.getInputStream(), StandardCharsets.UTF_8);
            reader = new BufferedReader(isReader);
        } catch(Exception ex) {ex.printStackTrace();}

    } // Закрываем конструктор

    public void run() {
        String message;

        try {
            while ((message = reader.readLine()) != null) {
                System.out.println("read " + message);
                tellEveryone(message);
            } // Закрываем while
        } catch(Exception ex) {ex.printStackTrace();}
    } // Закрываем run
} // Закрываем вложенный класс


    public void tellEveryone(String message) {
        Iterator it = clientOutputStreams.iterator();
        while(it.hasNext()) {
            try {
                PrintWriter writer = (PrintWriter) it.next();
                writer.println(message);
                writer.flush();
            } catch(Exception ex) {
                ex.printStackTrace();
            }
        } // Конец цикла while
    } // Закрываем tellEveryone

} // Закрываем класс

客户:

/**
 * Created by rnd on 7/4/2017.
 */

import java.io.*;
import java.net.*;
import java.nio.charset.Charset;
import java.nio.charset.StandardCharsets;
import java.util.*;
import javax.swing.*;
import java.awt.*;
import java.awt.event.*;


public class SimpleChatClient {

    JTextArea incoming;
    JTextField outgoing;
    BufferedReader reader;
    PrintWriter writer;
    Socket sock;

    public static void main(String[] args) {
        SimpleChatClient client = new SimpleChatClient();
        client.go();}

    public void go(){
        JFrame frame = new JFrame("Ludicrously Simple Chat Client");
        JPanel mainPanel = new JPanel();
        incoming = new JTextArea(15,50);
        incoming.setLineWrap(true);
        incoming. setWrapStyleWord (true) ;
        incoming.setEditable(false);
        JScrollPane qScroller = new JScrollPane(incoming);
        qScroller. setVerticalScrollBarPolicy (ScrollPaneConstants.VERTICAL_SCROLLBAR_ALWAYS) ;
        qScroller. setHorizontalScrollBarPolicy (ScrollPaneConstants.HORIZONTAL_SCROLLBAR_ALWAYS) ;
        outgoing = new JTextField(20);
        JButton sendButton = new JButton("Send") ;

        sendButton.addActionListener(new SendButtonListener());
        mainPanel.add(qScroller);
        mainPanel.add(outgoing);
        mainPanel.add(sendButton);

        setUpNetworking();

        Thread readerThread = new Thread(new IncomingReader());
        readerThread.start();

        frame.getContentPane().add(BorderLayout.CENTER, mainPanel);
        frame.setSize(800,500);
        frame.setVisible(true);

    }

    private void setUpNetworking() {
        try {
            sock = new Socket("178.165.87.221", 5000);
            InputStreamReader streamReader = new InputStreamReader(sock.getInputStream(), StandardCharsets.UTF_8 );
            reader = new BufferedReader(streamReader);


            Charset charset = StandardCharsets.UTF_8;
            OutputStreamWriter osw = new OutputStreamWriter( sock.getOutputStream(), charset );
            writer = new PrintWriter( new BufferedWriter( osw ) );

//            writer = new PrintWriter(sock.getOutputStream());
            System.out.println("networking established");
        } catch (IOException ex) {
                ex.printStackTrace();}
    }

    public class SendButtonListener implements ActionListener {
        public void actionPerformed (ActionEvent ev) {
            try {
                writer.println(outgoing.getText());
                writer.flush();

            } catch(Exception ex) {
                ex.printStackTrace();
            }
            outgoing. setText ("") ;
                    outgoing.requestFocus () ;}
    }

    public class IncomingReader implements Runnable{
        @Override
        public void run() {
            String message;

            try{
                while((message=reader.readLine())!=null ){
                    System.out.println("read " + message);
                    incoming.append(message + "\n");
                }

            } catch (Exception ex) {
                ex.printStackTrace();
            }
        }
    }

}
4

2 回答 2

2

如果你真的有一个动态 ip,你可以给自己一个 freedns 域(并添加一个防火墙例外),但很可能你在 NAT 后面。要使其正常工作,您需要做很多事情:

  • 尽管如此,获得一个freedns域并设置自动IP地址更新
  • 在客户端硬编码域
  • 通过向任何地方发送 UDP 数据包来暴露一组固定的 UDP 端口。公共 ip 上的 UDP 端口号通常与您主机上的端口号匹配。这部分是最重要的。您可以使用公共 STUN/TURN 服务器检查它是否有效。
  • 将这组端口硬编码到客户端中。它应该尝试 freedns 域上的所有端口,直到找到一个工作端口
  • 握手包应该有一个独特的签名,以便双方都知道他们正在尝试连接到正确的软件

看起来,大多数 NAT 都是端口受限的锥形 NAT,也就是说,它们会丢弃来自对等方的传入 UDP 数据包,直到您向该对等方发送数据包。此外,您通过发送数据包创建的 NAT UDP 映射在大约 60 秒后过期,这比 TCP 映射要短得多。

所有这一切都使得纯粹的 p2p 消息对于 NAT 后面的各方来说是不可能的。要加入 p2p 网络,您仍然需要通过公共服务器(电子邮件或其他即时消息服务提供商)交换一些数据包。有一个库“ice4j”可以生成和解析这些数据包(SDP),然后为直接连接创建 java 套接字包装器。

而且即使两个对等方保存了对方的地址以后直接连接,由于动态ip(一般是24h),地址最终还是会过期。

于 2017-07-24T20:15:43.937 回答
1

听起来防火墙拒绝连接或路由器没有端口转发,所以请求只是被拒绝。这听起来与拥有动态 IP 没有任何关系。

如果您在路由器后面,则路由器中有允许端口转发的设置,您可能需要向防火墙添加规则。无论如何,您可以通过尝试ping从其他地方尝试服务器 IP 地址来进行测试,如果响应,那么甚至可以尝试telnet <server ip> port查看您是否可以连接。

有什么东西妨碍了连接并拒绝连接!

于 2017-07-25T03:52:48.413 回答