【问题标题】:Java automatically connect to server if it opened如果打开,Java 会自动连接到服务器
【发布时间】:2014-08-12 15:35:34
【问题描述】:

我正在尝试创建一个程序来确定服务器是连接、关闭还是断开连接。 我需要让客户端在打开时自动连接到服务器。

问题:

1. How can I continuously ping the server, to determine if the server is up?

2. Why is it when I click the button the server can only receive once.

3. Determine when the Server is disconnected

这是我的代码:

服务器

   public Server()
    {
        super("Server");
        server.setLayout(new BorderLayout());
        main.setLayout(new BorderLayout());
        top.setLayout(new BorderLayout());
        bot.setLayout(new BorderLayout());
        console.setEditable(false);
        console.setFont(new Font("Courier New",Font.PLAIN,14));
        console.setBackground(Color.BLACK);
        console.setForeground(Color.WHITE);
        bot.add(console);
        top.add(btnReply,BorderLayout.EAST);
        top.add(queryline,BorderLayout.CENTER);
        main.add(top,BorderLayout.NORTH);
        main.add(bot,BorderLayout.CENTER);
        add(main);
    }

    private void runServer() throws IOException
    {
        int port = 25000;
        ServerSocket serverSocket = new ServerSocket(port);
        console.setText("Server is Up and listening to the port: "+port+"\n");
        System.out.println("Server is Up and listening to the port: "+port+"\n");

        while(true)
        {
            Thread thread = new Thread(new Handler(socket));
            thread.start();
        }
    }

    public static void main(String args[]) 
    {
        Server f=new Server();
        f.setVisible(true);
        f.setExtendedState(JFrame.MAXIMIZED_BOTH);
        f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);

        try
        {
            f.runServer();
        }catch(IOException ex)
        {
            //JOptionPane.showMessageDialog(null,ex.getMessage());
        }
    }

    @Override
    public void actionPerformed(ActionEvent e) 
    {

    }
}

class Handler implements Runnable 
{
    private Socket socket;
    public Handler(Socket socket){
        this.socket = socket;
    }

    public void run() 
    {
        // You may need to add a repeat and exit clause here...
        Server f=new Server();
        try
        {
            InputStreamReader ir = new InputStreamReader(socket.getInputStream());
            BufferedReader br = new BufferedReader(ir);
            String message = br.readLine();
            System.out.println(message);
            String[] received=message.split("~");
            f.console.append(received[1]+": requesting for "+received[0]+"\n");
            System.out.println(received[1]+": requesting for "+received[0]+"\n");
        }catch(IOException ex)
        {
            ex.printStackTrace();
        }
    }
}

客户

public class Branch extends JFrame
{
    private static Socket socket;
    JPanel main=new JPanel();
    JPanel top=new JPanel();
    JPanel bot=new JPanel();
    JButton btnItem=new JButton("item");
    JButton btnGlstock=new JButton("glstock");
    JTextArea console=new JTextArea();
    JScrollPane scrollv=new JScrollPane(console);

    ActionListener item=new ActionListener()
    {
        @Override
        public void actionPerformed(ActionEvent e)
        {
            String ip="";
            String send="";
            try
            {
                ip=InetAddress.getLocalHost().getHostAddress();
                send="item~"+ip;
                request(send);
            }catch(Exception ex)
            {
                JOptionPane.showMessageDialog(null,ex.getMessage());
            }
        }
    };

    ActionListener glstock=new ActionListener()
    {
        @Override
        public void actionPerformed(ActionEvent e)
        {
            String ip="";
            String send="";
            try
            {
                ip=InetAddress.getLocalHost().getHostAddress();
                send="glstock~"+ip;
                request(send);
            }catch(Exception ex)
            {
                JOptionPane.showMessageDialog(null,ex.getMessage());
            }
        }
    };

    private void request(String send)
    {
        try
        {
            OutputStream os = socket.getOutputStream();
            OutputStreamWriter osw = new OutputStreamWriter(os);
            BufferedWriter bw = new BufferedWriter(osw);
            bw.write(send+"\n");
            bw.flush();
        }catch(Exception ex)
        {
            ex.printStackTrace();
        }
    }

    public Branch()
    {
        super("Branch");
        scrollv.setAutoscrolls(true);
        main.setLayout(new BorderLayout());
        top.setLayout(new FlowLayout());
        top.add(btnItem);
        top.add(btnGlstock);
        btnItem.addActionListener(item);
        btnGlstock.addActionListener(glstock);
        bot.setLayout(new BorderLayout());
        console.setEditable(false);
        console.setForeground(Color.white);
        console.setBackground(Color.black);
        bot.add(scrollv,BorderLayout.CENTER);
        main.add(top,BorderLayout.NORTH);
        main.add(bot,BorderLayout.CENTER);
        add(main);
    }

   private void connect2Server() throws IOException
   {
    Timer timer=new Timer(5000,checkPing);

    try
    {
        socket = new Socket(IP,port);
        console.append("You are now Connected to the Server\r\n");
        //timer.start();
        socket.setSoTimeout(5000);
        InputStreamReader isr=new InputStreamReader(socket.getInputStream());
        isr.read();
    }
    catch(SocketTimeoutException ex)
    {
        ex.printStackTrace();
    }
    catch(IOException ex)
    {
        console.append("Server is offline\r\n");
        ex.printStackTrace();
    }
    connect2Server();
  }

    public static void main(String args[]) 
    {
        Branch frame=new Branch();
        frame.setVisible(true);
        frame.setExtendedState(JFrame.MAXIMIZED_BOTH);
        frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        frame.connect2Server();
        frame.connectDatabase();
    }
}

【问题讨论】:

    标签: java sockets network-programming


    【解决方案1】:

    如何持续 ping 服务器,以确定服务器是否已启动?

    使用某种计时器来安排定期回调并在那里进行测试。 ping 服务器(在这种情况下)可能就像尝试与它建立连接一样简单......

    你也可以使用InetAddress#isReachable...

    为什么我点击按钮时服务器只能接收一次

    因为服务器不再处理您的连接。看看你的服务器代码...

    while(true)
    {
        //^Reading the message from the client
        // Wait for a NEW client connection
        socket = serverSocket.accept();
        // Process request...
        InputStream is = socket.getInputStream();
        InputStreamReader isr = new InputStreamReader(is);
        BufferedReader br = new BufferedReader(isr);
        String message = br.readLine();
        System.out.println(message);
        String[] received=message.split("~");
        console.append(received[1]+": requesting for "+received[0]+"\n");
        // ... No longer monitoring for new content...
    }
    

    serverSocket.accept(); 正在等待新的客户端连接,而不是监视现有连接。

    您应该做的是产生某种Thread,传递对客户端Socket 连接的引用并在那里处理它,在它自己的循环中......提供超时或可能的退出条件,所以可以关闭套接字和线程...

    判断服务器何时断开

    基本上,如果您尝试向服务器(或客户端)发送内容,它会抛出某种Exception,这很好地表明连接由于某种原因断开了。

    您也可以使用某种计时器定期向服务器发送“健康”检查消息,但是,您需要确保您正在同步您的通信,因此两个(客户端)线程不会尝试同时发送消息。

    旁注

    您当前的代码在发送消息时会阻塞事件调度线程。当您在本地进行测试时,这可能不是什么大事,但是,根据连接速度和服务器及时响应的能力,可能会导致您的 UI 出现“冻结”一两秒(或更长)

    查看Concurrency in Swing了解更多详情

    更新示例

    为了让您的服务器能够处理多个客户端连接,您需要为每个新连接生成一个新线程,例如...

    private void runServer() throws IOException
    {
        int port = 25000;
        ServerSocket serverSocket = new ServerSocket(port);
        console.setText("Server is Up and listening to the port: "+port+"\n");
        System.out.println("Server is Up and listening to the port: "+port+"\n");
    
        while(true)
        {
            Socket socket = serverSocket.accept();
            Thread thread = new Thread(new Handler(socket));
            thread.start();
        }
    }
    

    然后在您的“客户端套接字处理程序”中,您需要设置一个循环,该循环将继续从客户端套接字读取,直到出现问题或客户端请求终止通信,例如...

    class Handler implements Runnable 
    {
        private Socket socket;
        public Handler(Socket socket){
            this.socket = socket;
        }
    
        public void run() 
        {
            // You may need to add a repeat and exit clause here...
            try
            {
                boolean keepRunning = true;
                while (keepRunning) {
                    InputStreamReader ir = new InputStreamReader(socket.getInputStream());
                    BufferedReader br = new BufferedReader(ir);
                    String message = br.readLine();
                    System.out.println(message);
                    String[] received=message.split("~");
                    System.out.println(received[1]+": requesting for "+received[0]+"\n");
                    // Check for a valid "exit" state and change keepRunning
                    // if the client want's to disconnect...
                }
            }catch(IOException ex)
            {
                ex.printStackTrace();
            } finally {
                try {
                    if (socket != null) {
                        socket.close();
                    }
                } catch (Exception exp) {
                }
            }
        }
    }
    

    另外,请确保您正确关闭资源;)

    【讨论】:

    • master @MadProgrammer 对不起,我不知道如何线程我该怎么办?因为我以前的程序我发送文件时只connect2Server,现在我必须每秒connect2Server来确定我是否丢失或连接到服务器。
    • 那么是时候开始关注Concurrency了,对于example
    • 不是真的,因为如果您需要(在程序中),您可以更改running 的状态...我就是这样的老派...
    • 不,根据您的代码Handler 不是服务器的内部类,因此它无法访问相同的变量。您需要将JTextArea 的引用传递给Handler,就像您处理套接字一样。您最好将一个可运行的示例放在一起并发布另一个问题
    • @askManiac 就个人而言,我会隔离谁可以读取/写入底层套接字,因为这可能会很快搞砸。如果客户端在下载之前的请求时无法请求新项目,那么它没有任何区别,请将其保持在同一个线程并在 I/O 周围获得一些内置的安全性。另一方面,如果您希望允许客户端发出多个请求(并且可能有多个文件),那么您可能需要考虑另一种设计......
    【解决方案2】:

    如何持续 ping 服务器,以确定服务器是否已启动?

    继续尝试连接它,如果失败则睡眠和循环。

    为什么我点击按钮时服务器只能接收一次。

    因为您的服务器每次连接只读取一次。 Handler.run()方法需要循环直到readLine()返回null或者发生异常。

    确定服务器何时断开

    read() 将返回 -1,readLine() 将返回 null,任何其他 readXXX() 将抛出 EOFException. 但是您的客户端根本不会读取。您可能应该调整您的应用程序协议,以便服务器回复每次发送。否则,您将不得不等到发送时出现IOException,由于 TCP 缓冲,这可能需要多次迭代才能发生。

    其他说明:

    1. 不要在每次发送时创建新的流。在套接字的整个生命周期中使用相同的流、读取器、写入器。
    2. 不要在事件线程中执行网络代码。使用单独的线程。

    【讨论】:

    • 谢谢@EJP 我关注了MadProgrammer,我在Timer 中使用了address.isReachable(5000); 语句,我终于使处理程序线程工作了!但我不能附加 JTextArea。
    • 使用 isReachable() 是没有意义的。它不会告诉您连接是否真的会成功。只有真正尝试连接才能做到这一点。你无法预测未来。
    • 哦... EJP 我该怎么做read() 的事情((:我真的不知道对不起,但是如果你发布一个示例代码可以吗:D
    • 使用Socket.setSoTimeout() 设置读取超时,并捕获超时发生时产生的SocketTimeoutException。将其设置为高,例如预期响应时间加倍,因此只有在确实存在问题时才会发生。
    • 你的意思是InputStreamReader isr=new InputStreamReader(socket.getInputStream) isr.read();
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2015-06-27
    • 2016-07-24
    • 2019-09-09
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2015-04-19
    相关资源
    最近更新 更多