客户端不从套接字读取数据?

Posted

技术标签:

【中文标题】客户端不从套接字读取数据?【英文标题】:Client not reading data from socket? 【发布时间】:2016-03-02 21:07:00 【问题描述】:

我花了一段时间阅读其他帖子,但似乎没有人和我有同样的情况。我已经尝试了所有我读过的解决方案,但没有结果,所以我决定提出这个问题。

我一直在开发服务器/客户端应用程序,客户端似乎没有从套接字读取数据,但服务器可以读取客户端发送的数据。客户端在尝试读取该行时冻结。这是我的代码:

客户:

import java.io.IOException;
import java.io.PrintWriter;
import java.net.Socket;
import java.util.Scanner;
import java.util.Timer;
import java.util.logging.Level;
import java.util.logging.Logger;
import javax.swing.JLabel;

public class Main 

    public static Socket s = connect();

    public static void sendMessage(String msg) 
        if (s != null) 
            PrintWriter outWriter = null;
            try 
                outWriter = new PrintWriter(s.getOutputStream(), true);
                outWriter.println(msg);
                outWriter.flush();
             catch (IOException ex) 
                Logger.getLogger(Main.class.getName()).log(Level.SEVERE, null, ex);
                outWriter.close();
             finally 
            
         else 
            System.err.println("Error, socket is null!");
        
    

    public static String readMessage() 
        if (s != null) 
            Scanner in = null;
            try 
                in = new Scanner(s.getInputStream());
             catch (IOException ex) 
                ex.printStackTrace();
            
            String ss = "";
            while (in.hasNext()) 
                ss += in.next();
            
            System.out.println("REPONSE:" + ss);
            return ss;
         else 
            System.err.println("Error, socket is null!");
        
        return "err/readMessage";
    

    public static Socket connect() 
        try 
            Socket sss = new Socket("localhost", 25586);
            sss.setKeepAlive(true);
            return sss;
         catch (IOException ex) 
            ex.printStackTrace();
            System.exit(-1);
        
        return null;
    

    public static void main(String args[]) 
        java.awt.EventQueue.invokeLater(new Runnable() 
            public void run() 

                Thread t = new Thread(new Runnable() 
                    @Override
                    public void run() 
                        sendMessage("HELO");
                        System.out.println("Thread initialized.");

                        while (true) 
                            try 
                                System.out.println("Awaiting message...");
                                Thread.sleep(100);
                                String messages = readMessage();
                                System.out.println("Message recieved! '" + messages + "'");
                                String[] message = messages.split("/");
                                System.out.println(messages);
                                if (message[0].equalsIgnoreCase("DERP"))                                        // err/reason
                                    System.out.println("IT'S FINALLY WORKING!");
                                 else 
                                    System.out.println("Didn't work :( response:" + messages);
                                
                             catch (InterruptedException ex) 
                                ex.printStackTrace();
                            

                        
                    
                );
                t.start();
            
        );
    

服务器:

import java.util.List;
import java.io.BufferedReader;
import java.io.DataOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.OutputStream;
import java.io.PrintWriter;
import java.net.ServerSocket;
import java.net.Socket;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;
import java.util.logging.Level;
import java.util.logging.Logger;

public class Main  /* SERVER */


    public static List<EchoThread> list = new ArrayList<>();

    public static int PORT = 25586;

    public static void main(String[] args) throws IOException 
        new Main(PORT);
    

    public Main(int port) throws IOException 
        this.PORT = port;
        ServerSocket serverSocket = null;
        Socket socket = null;

        try 
            serverSocket = new ServerSocket(PORT);
         catch (IOException e) 
            e.printStackTrace();

        
        while (true) 
            try 
                socket = serverSocket.accept();
             catch (IOException e) 
                System.out.println("I/O error: " + e);
            
            // new threa for a client
            EchoThread s = new EchoThread(socket);
            s.start();
            list.add(s);
        
    

    public static void sendMessageToAll(String ss) 
        for (EchoThread s : list) 
            s.allMessage(ss);
        
    


class EchoThread extends Thread 

    protected Socket socket;
    InputStream inp = null;
    BufferedReader brinp = null;

    public EchoThread(Socket clientSocket) 
        this.socket = clientSocket;
        try 
            inp = socket.getInputStream();
            brinp = new BufferedReader(new InputStreamReader(inp));
         catch (IOException e) 
            e.printStackTrace();
        
    

    public void run() 
        String line;
        while (true) 
            try 
                line = brinp.readLine();
                if ((line == null) || line.equalsIgnoreCase("EXIT")) 
                    socket.close();
                    return;
                 else 
                    handle(line);
                
             catch (IOException e) 
                e.printStackTrace();
                return;
            
        
    

    public void handle(String s) 
        String[] keys = s.split("/");
        System.out.println("random: Handling request\"" + s + "\"");
        System.out.println("Response: DERP");
        if (s.equalsIgnoreCase("HELO")) 
            System.out.println("Message recieved:" + s);
        
        respond("DERP");
    

    public void respond(String s) 
        try 
            System.out.println("Response_WILLBE:" + s);
            OutputStream os = socket.getOutputStream();
            PrintWriter pw = new PrintWriter(os, true);
            pw.println(s);
            pw.flush();
            System.out.println("Message sent!");
         catch (Exception ex) 
            Logger.getLogger(EchoThread.class.getName()).log(Level.SEVERE, null, ex);
        
    

    public void allMessage(String s) 
        respond(s);
    

我尝试了flush() 代码和\r\nprintln 修复,但都没有奏效! -- 感谢您的阅读!

【问题讨论】:

无论您是否尝试过换行符和刷新,但它们都不起作用,您都应该使用它们(它们是必要的)。确保使用 PrintWriter(或任何基于文本的输出流)发送的每条消息都以 \n 结尾,并确保在发送每条消息后调用 flush +Nerdizzle 感谢您的回答!我已经尝试过了,但是没有用。我想既然我使用的是 println 而不是 print,它应该可以使用或不使用 \n,但以防万一,我尝试了它,但它仍然失败。我检查了,我正在使用 PrintWriter,我正在刷新(如上面的代码所示) 您的服务器可能会产生无限的数据流。除非服务器关闭连接,否则客户端中的while (in.hasNext()) 永远不会终止。您可以用String ss = in.next();if (in.hasNext()) 替换所有这些。您的客户正在接收数据,但过于贪婪 由于其中包含将其优先于其他问题的请求,因此我遗憾地投反对票。这里的读者大多是志愿者,他们会回答他们感兴趣的问题,而且他们会在闲暇时回答。 (否则你的问题是一个很好的问题)。 【参考方案1】:

我很好奇客户端和服务器的通信方式之间的差异。例如,客户端使用Scanner 来读取输入,而服务器使用BufferedReader(这是我个人的偏好)。只是一个建议:保持一致。

现在——首先,客户端只发送一条消息,然后开始无限循环地读取。看到您确切地知道服务器在您向其发送“HELO”后应该如何响应(它应该用一行“DERP”响应),没有理由在任何类型的循环中从服务器读取。

服务器上也存在同样的问题。就像客户端现在一样,它总是只会向服务器发送一行(“HELO”)。因此,服务器应该只期望一行并且只读取一行。绝对没有理由在循环中读取输入。但是,实际问题只存在于客户端的代码中:

您当前有while(in.hasNext()) 作为客户端从服务器读取输入的时间长度的条件。 The hasNext method differs in logical function depending on the method of communication. 在套接字通信的情况下,hasNext 需要一个流,因此,除非套接字关闭,否则它将始终返回 true。

除非您不知道要读取多少行,否则一般应避免在套接字通信中使用循环。在这种情况下,您应该首先让发送者向接收者发送某种数字,然后接收者应该在 for 循环中读取后续行,读取 'n' 次(其中 n 是接收到的数字)。

这是一个使用PrintWritersBufferedReaders.的非常基本的客户端/服务器通信程序

客户

public static void main(String[] args)
    try
        Socket socket = new Socket("localhost", 12345);
        PrintWriter out = new PrintWriter(socket.getOutputStream());
        BufferedReader in = new BufferedReader(new InputStreamReader(socket.getInputStream()));
        out.write("HELO\n"); //print("HELO\n") and println("HELO") should work too.
        out.flush();
        System.out.println("Server says " + in.readLine());
        in.close();
        out.close();
        socket.close();
    catch(IOException e)e.printStackTrace();

服务器

public static void main(String[] args)
    try
        ServerSocket serverSocket = new ServerSocket(12345);
        while(true)
            new Thread(new ClientConnectionThread(serverSocket.accept())).start();
        
    catch(IOException e)e.printStackTrace();


private class ClientConnectionThread implements Runnable
    private Socket socket;
    public ClientConnectionThread(Socket socket)
        this.socket = socket;
    
    @Override
    public void run()
        try
            BufferedReader in = new BufferedReader(new InputStreamReader(socket.getInputStream()));
            PrintWriter out = new PrintWriter(socket.getOutputStream());
            System.out.println("Client says " + in.readLine());
            out.write("HELO Back!\n"); //again, print("HELO Back!\n") and
                //println("HELO Back!") should also work
            out.flush();
            out.close();
            in.close();
            socket.close();
        catch(IOException e)e.printStackTrace();
    

请注意,在实际通信中不需要循环。

【讨论】:

非常感谢!它现在完全完全可用!你刚刚为我节省了很多时间! :) HELO 和 DERP 只是简化程序的示例。 感谢我的示例,这是一个很好的起点。我已经复制粘贴了您的代码,但它不起作用:1) 客户端中的新 BuffereReader(new InputeStreamReader(..)) 和 2) 对于我的 JDK 9.0 版本,它仅在我添加 out.flush 后才有效。客户端中的 println。看完这个问答我做的2***.com/questions/40004783/printwriter-wont-write @Nikita 谢谢,很久以前写的,我的 Java 生锈了。添加了刷新调用(尽管服务器调用是不必要的,因为它在关闭时刷新),并修复了客户端 BufferedReader 构造函数【参考方案2】:

正如@zapl 所说,您的客户端在从服务器读取数据时非常贪婪。让我们看看你用来从服务器读取数据的过程:

String ss = "";
while (in.hasNext()) 
    ss += in.next();

System.out.println("REPONSE:" + ss);

此代码将不断从服务器拉取数据,直到流关闭。在从文件读取数据或解析字符串时,您可能会使用这种类型的代码块,这非常适合这些情况,因为您希望将流中的所有数据都放入您的字符串中。

您要做的是从新行\n 指定的服务器读取第一个响应。而不是做while(in.hasNext()),你应该只检查是否in.hasNext(),如果是,则将其作为响应。

String ss = "";
if (in.hasNext()) 
    ss += in.next();

System.out.println("REPONSE:" + ss);

或者更优雅

if (in.hasNext()) 
    String response = in.next();
    System.out.println("REPONSE:" + response);
    return response;

....

【讨论】:

假设在输入流的实例化中出现 IOException(在这种情况下是扫描仪,但 BufferedReader 更容易处理),其处理方式与意外关闭流的处理方式相同(意思是在. hasNext() 返回 false),我相信 hasNext 方法只会使处理错误更加困难和重复(因为相同的代码将出现在扫描仪实例化 try 块的 catch 子句以及 if(in.hasNext()) 的 else 语句中)。仅使用BufferedReader 并以相同的方式捕获所有 IOExceptions 不是更有意义吗? @Nerdizzle 我同意在使用 Socket 时不建议使用 Scanner。然而,鉴于 OP 的问题,为什么他的代码不能正常工作,这就是原因。如果这是 StackExchange 的代码审查部分,我会倾向于写更多关于我对他/她的代码的个人意见。【参考方案3】:

首先,对于套接字,我总是喜欢通过 localhost 运行服务器并 telnet 进入它。当我用你的那样做时,我来回没有问题。因此,现在只需修复您的客户即可。

对于客户端,(暂时)摆脱对 Scanner 类的使用。这个类还有一整套可能导致问题的用法。相反,请保持简单。

更改您的客户端以使用 BufferedReader。这是您的新 readMessage() 方法:

    public  String readMessage() 
        if (s != null) 
            BufferedReader in = null;
            try 
                System.out.println("Creating reader");
                in = new BufferedReader(new InputStreamReader(s.getInputStream()));
             catch (IOException ex) 
            
            String ss = "";
            try 
                ss = in.readLine();
             catch (IOException e) 
            
            System.out.println("REPONSE:" + ss);
            return ss;

您的客户端现在可以正常工作(对我而言),这意味着您在使用扫描仪时遇到了问题。

我会让你调试它,因为这不是你的问题。玩得开心! ;)

【讨论】:

以上是关于客户端不从套接字读取数据?的主要内容,如果未能解决你的问题,请参考以下文章

Postgres JDBC 客户端卡在从套接字读取

不要从输入队列套接字中读取数据

在 TCP 套接字程序中,客户端发送一些数据,但服务器需要多次读取。为啥?

QT 套接字不读取所有数据

从套接字读取数据时出现问题

如何使用套接字通道读取和写入数据并接受连接