简体   繁体   English

我可以使用Java Socket在动态IP上托管聊天服务器吗?

[英]Can I host Chat Server on Dynamic IP using Java Socket?

Investigating the Servlets I've created a simple chat and tested it on local IP - everything works. 调查Servlets我创建了一个简单的聊天并在本地IP上测试它 - 一切正常。 But when I tried to test it through the real network the connection refused - java.net.ConnectException: Connection refused: connect . 但是当我试图通过真实网络测试它时连接被拒绝了 - java.net.ConnectException: Connection refused: connect Is the reason in Dynamic IP which I have, or additional settings are needed? 我有动态IP的原因,还是需要其他设置? Thanks in advance! 提前致谢!

Server: 服务器:

/**
 * 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

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

Client: 客户:

/**
 * 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();
            }
        }
    }

}

If you really have a dynamic ip, you can get yourself a freedns domain (and add a firewall exception), but most probably you're behind NAT. 如果你真的有一个动态IP,你可以给自己一个freedns域(并添加一个防火墙例外),但很可能你是NAT背后的。 To make it work you need multiple things: 为了使它工作,你需要多个东西:

  • Still, get a freedns domain and setup automatic ip address update 仍然,获得一个freedns域并设置自动IP地址更新
  • hardcode the domain in the client 在客户端中对域进行硬编码
  • expose a fixed set of UDP ports by sending UDP packets to nowhere. 通过将UDP数据包发送到任何地方来暴露一组固定的UDP端口。 The UDP port number on public ip usually matches the port number on your host. public ip上的UDP端口号通常与主机上的端口号匹配。 This part is the most important. 这部分是最重要的。 You can check it works using public STUN/TURN servers. 您可以使用公共STUN / TURN服务器检查它是否有效。
  • Hardcode this set of ports into the client. 将这组端口硬编码到客户端。 It should try all ports on the freedns domain, until it finds a working port 它应该尝试freedns域上的所有端口,直到它找到一个工作端口
  • The handshake packets should have a signature unique for your chat so both sides know they're trying to connect to the right software 握手数据包应该具有聊天特有的签名,因此双方都知道他们正在尝试连接到正确的软件

As it appears, most NATs are Port-restricted cone NATs, that is, they drop incoming UDP packets from a peer until you send a packet to that peer. 如图所示,大多数NAT都是端口限制的锥形NAT,也就是说,它们会丢弃来自对等体的传入UDP数据包,直到您向该对等体发送数据包为止。 Besides, NAT UDP mappings you create by sending a packet expire in around 60 seconds, which is much less than for TCP mappings. 此外,通过发送数据包创建的NAT UDP映射在大约60秒内到期,这比TCP映射要少得多。

All this makes pure p2p messaging impossible for parties behind NAT. 所有这些都使得纯粹的p2p消息传递无法用于NAT背后的各方。 To join a p2p network you still need to exchange a few packets via a public server (e-mail or another Instant messaging provider). 要加入p2​​p网络,您仍然需要通过公共服务器(电子邮件或其他即时消息提供程序)交换一些数据包。 There's the library "ice4j" that can produce and parse these packets (SDP) and then create java socket wrappers for direct connections. 有“ice4j”库可以生成并解析这些数据包(SDP),然后为直接连接创建java套接字包装器。

And even if two peers save each other's addresses to connect directly in the future, the addresses will eventually expire due to dynamic ip (usually 24h). 即使两个对等端保存彼此的地址以便将来直接连接,地址最终也会因动态IP(通常为24小时)而到期。

Sounds like either a firewall refusing the connection or a router is not port forwarding, so the request is just being refused. 听起来好像防火墙拒绝连接或者路由器不是端口转发,所以请求被拒绝。 It doesn't sound like anything to do with having a dynamic IP. 这听起来与拥有动态IP无关。

If you are behind a router then there are settings in the router to allow port forwarding, and you might need to add a rule to your firewall. 如果您位于路由器后面,则路由器中存在允许端口转发的设置,您可能需要向防火墙添加规则。 Anyway you can test by just trying to ping the server IP address from elsewhere and if that responds then even try a telnet <server ip> port to see if you can connect. 无论如何,您可以通过尝试从其他地方ping服务器IP地址进行测试,如果响应,那么甚至尝试使用telnet <server ip> port来查看是否可以连接。

Something is getting in the way and refusing the connection! 有些事情正在阻碍并拒绝连接!

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM