Java 简单的 Echo 服务器无法运行。

3

我经常浏览SO(即Stack Overflow),并发现了许多与我尝试做的相同的例子。但是我根本找不到我的代码问题所在。也许我只需要一个新鲜的眼睛来看我的代码。 因此,为了避免被标记为重复线程,这里就是我的问题。我有一段简单的Java代码。它打开一个端口,连接到一个套接字,获取输入流和输出流,并向输出流中放入一些文本,然后尝试读取输入流中的文本。当执行readLine方法时,它不会返回到代码中。它只是继续运行,永远不会回到主方法。

import java.net.*;
import java.io.*;
import java.io.ObjectInputStream.GetField;

public class echoserver {

public static void main(String[] args) {
    // TODO Auto-generated method stub
    String hostName = "127.0.0.1";
    //      InetAddress.getLocalHost()
    int portNumber = 5000;
    ServerSocket ss = null;
    Socket echoSocket = null;


    try {
        ss = new ServerSocket(portNumber);
        echoSocket = new Socket(hostName, portNumber);
//          echoSocket = ss.accept();
        System.out.println("open");
        System.out.println(echoSocket.isBound());

        PrintWriter writer = new PrintWriter(echoSocket.getOutputStream());
        for (int i = 0; i < 100; i++) {
            writer.print("test String");    
        }

        writer.flush();
//          writer.close();

        System.out.println("inputstream read");
       DataInputStream is = new DataInputStream(echoSocket.getInputStream());
       String fromStream = is.readLine();
       System.out.println(fromStream);

       System.out.println("bufferreader read");

        BufferedReader reader = new BufferedReader(new InputStreamReader(echoSocket.getInputStream()));
        String fromReader = reader.readLine();

        System.out.println(fromReader);

    } catch (UnknownHostException  ex1) {
        // TODO Auto-generated catch block
        System.out.println("EX1");
        ex1.printStackTrace();
    }

    catch (IOException ex2) {
        // TODO: handle exception
        System.out.println("EX2");
        ex2.printStackTrace();
    }

    finally {
        try {
            echoSocket.close();
            ss.close();
        } catch (IOException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
    }



}

}

编辑:下面是更新的代码... 这段代码唯一的问题在于Server.Run中的while循环永远不会结束。我寻找了其他属性(我记得有类似isTextAvailable的东西),但找不到。这段代码的想法是将其转换为聊天客户端。不用说,这是一场斗争!

编辑2:我找到了问题所在。我从编写端没有关闭套接字,因此监听器一直在监听!感谢大家的帮助!

clientsocket.close();

添加了一行代码,就可以正常工作了!

import java.net.*;
import java.io.*;
import java.io.ObjectInputStream.GetField;
import java.util.*;

public class echoserver {



static echoserver echo;

public static class Client implements Runnable {

    Socket clientsocket;
    String hostName = "127.0.0.1";
    int portNumber = 5000;
    static int onesleep = 0;

    public void run(){ 


        System.out.println("Client Run  " + new Date());

        try {
            clientsocket = new Socket(hostName,portNumber);
            PrintWriter writer = new PrintWriter(clientsocket.getOutputStream());

            for (int i = 0; i < 10; i++) {
                writer.println("test String   " + i );  
            }

            writer.flush();
                            clientsocket.close();
        } catch (UnknownHostException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        } catch (IOException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }



    }
}

public class Server implements Runnable {

    public void run(){

        System.out.println("Server Run" + new Date());
        int portNumber = 5000;
        ServerSocket ss = null;
        Socket serversocket = null;
        InputStreamReader streamReader; 

        try {
            ss = new ServerSocket(portNumber);
            serversocket = ss.accept();

            System.out.println("bufferreader read  " + new Date());

            streamReader = new InputStreamReader(serversocket.getInputStream());
            BufferedReader reader = new BufferedReader(streamReader);
            String fromReader;

            System.out.println(reader.ready());
            System.out.println(reader.readLine());



            while ((fromReader = reader.readLine()) != null) {
                System.out.println(fromReader);

            }

            System.out.println("After While in Server Run");


        } catch (IOException ex_server) {
            // TODO Auto-generated catch block
            System.out.println("Server Run Error    " + new Date());
            ex_server.printStackTrace();

        }
          finally {
              try {
                serversocket.close();
                  ss.close();
            } catch (IOException e) {
                // TODO Auto-generated catch block
                e.printStackTrace();
            }

          }
        System.out.println("open" + new Date());
        System.out.println(serversocket.isBound());


    }
}


public void go(){

    Server server = new Server();
    Thread serverThread = new Thread(server);
    serverThread.start();

    Client client = new Client();
    Thread clientThread = new Thread(client);
    clientThread.start();

}

public static void main(String[] args) {
    // TODO Auto-generated method stub

    echo = new echoserver();
    echo.go();





}

}

ss 用于什么? - adv12
ss 用于打开端口。如果我不使用 ss,则 echoSocket 的创建会失败,提示端口未打开。 - NewGuyInJava
这是你的服务器代码还是客户端代码?echoSocket 服务器应该可以通过 telnet 进行测试。 - Elliott Frisch
它应该同时进行。一个线程发送,另一个线程监听。使用更新的代码,监听者永远不会结束! - NewGuyInJava
2个回答

4

我之前已经准备好了一篇文章,但是根据你在另一个答案中的最后一条评论,似乎你已经想通了。无论如何,我还是会发布这篇文章,以防它有任何帮助。

总的来说,你当前的类有效地代表了同一线程/进程中的客户端和服务器端部分。正如你所看到的,你能够将数据写入出站(或客户端)套接字,但是服务器端组件从未有机会监听传入连接。

因此,当你尝试从传入(或服务器端)套接字的输入流中读取数据时,因为没有收到任何内容,所以什么也不存在。 readline() 方法最终会阻塞直到有数据可用,这就是为什么你的程序似乎在那个点上停滞不前。此外,像 haifzhan 所说的那样,使用 new Socket(...) 创建新套接字并不会建立连接,你只是拥有一个空流的套接字。

ServerSocket#accept 方法是你需要使用的方法,以便监听连接。该方法将为您创建套接字,从中您可以尝试从其流中读取数据。像 haifzhan 所说的那样,该方法会阻塞直到建立连接,这就是为什么它不能在单线程环境中正常工作的根本原因。

要在同一应用程序中执行此操作,您只需要将组件分开并在不同的线程中运行即可。尝试使用以下代码:

public class EchoClient {

    public static void main(String[] args) throws InterruptedException {
        new Thread(new EchoServer()).start(); // start up the server thread

        String hostName = "localhost";
        int portNumber = 5000;

        try {
            Socket outboundSocket = new Socket(hostName, portNumber);

            System.out.println("Echo client is about to send data to the server...");

            PrintWriter writer = new PrintWriter(outboundSocket.getOutputStream());
            for (int i = 0; i < 100; i++) {
                writer.print("test String");
            }

            System.out.println("Data has been sent");

            writer.flush();
            outboundSocket.close();
        } catch (UnknownHostException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}

而服务器组件则作为单独的线程运行:

public class EchoServer implements Runnable {

    public void run(){
        try {
            ServerSocket ss = new ServerSocket(5000);
            System.out.println("Waiting for connection...");
            Socket inboundSocket = ss.accept();

            System.out.println("inputstream read");
            DataInputStream is = new DataInputStream(inboundSocket.getInputStream());

            BufferedReader reader = new BufferedReader(new InputStreamReader(is));

            String fromStream = reader.readLine();
            System.out.println(fromStream);

            System.out.println("bufferreader read");
            ss.close();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}

谢谢Paul!我已经弄清楚了,它与你的解决方案有点接近。本来想点赞的,但是声望不够!我正在发布我的代码,但有一个小问题。任何帮助/建议都将不胜感激 :) - NewGuyInJava
@NewGuyInJava 别担心,我认为现在点赞是15分。无论如何,我会稍后更深入地查看更新的代码;不过,我认为问题在于连接仍然处于活动状态,因为客户端没有关闭套接字,所以流永远不会达到“流结束”的条件,这将返回 null(这是循环的控制条件)。在您的客户端代码中,尝试像我一样添加 .close() 到套接字上,看看是否有所不同。 - Paul Richter
1
我发完代码后就发现了这个问题!我已经更新了代码!而且我也获得了投票的权限 :) 给你点赞 +1 :) - NewGuyInJava

2
您没有连接到任何客户端套接字...
来自编写套接字的服务器端

accept方法会等待直到有一个客户端在此服务器的主机和端口上启动并请求连接。当有一个连接被请求并成功建立时,accept方法将返回一个新的Socket对象,该对象绑定到同一本地端口,并将其远程地址和远程端口设置为客户端的地址和端口。服务器可以通过这个新的Socket与客户端进行通信,并在原始ServerSocket上继续监听客户端连接请求。

ss = new ServerSocket(portNumber);
echoSocket = new Socket(hostName, portNumber)
// echoSocket = ss.accept();

你不应该使用新的Socket(host, port)来创建echoSocket,ss.accept()是正确建立服务器客户端连接的方式。
之所以出现问题,是因为你上面的代码(echoSocekt = ss.accept();)不正确,所以下面的代码无法使用。
DataInputStream is = new DataInputStream(echoSocket.getInputStream());

如果你调用is.available(),它会返回0,这意味着无法读取任何字节。
阅读我提供的链接,查看EchoServer.java和EchoClient.java,然后你就可以建立自己的连接了。

我也尝试过了。ss.accept(); 没有返回。我可以在端口扫描工具中看到该端口被 jawaw.exe 占用,但仍然没有任何反应。 - NewGuyInJava
accept() 是一个阻塞函数,它会一直等待直到连接建立成功,你需要在客户端代码中发送连接请求给服务器端代码。请阅读 EchoServer.java 和 EchoClient.java,它们非常清晰易懂。 - Haifeng Zhang
谢谢!我会仔细阅读。听起来我需要两个线程。 - NewGuyInJava

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接