当前位置: 首页 > 知识库问答 >
问题:

从socket inputStream访问字节数组值

穆阳嘉
2023-03-14

我试图从发送到套接字的输入流中检索字节值。我用了很多方法,但它总是给我打印字节数组的地址,而不是它的内容。下面是我的客户端和服务器代码。当一个包从客户端发送到服务器时,服务器实例化一个新的线程来处理连接。所以slaveSocket是我想要使用的套接字。

public class TCPClient {

public static void main(String[] args) throws IOException{
    Socket socket;
    String address;
    int port;
    String userInput;
    String serverResponse;

    PrintWriter out;
    BufferedReader in;

    //read characters from user
    BufferedReader stdIn;

    if (args.length != 2) {
        System.err.println("Usage: java EchoClient <address> <port>");
        System.exit(1);
    }

    byte[] mode = "octet".getBytes(Charset.forName("UTF-8"));
    address = args[0];
    port = Integer.parseInt(args[1]);

    try{
        //connect socket to server
        socket = new Socket(address, port);

        //Construct PrintWriter to write objects to the socket
        out = new PrintWriter(socket.getOutputStream(), true);

        //Construct BufferedReader to read input from the socket
        in = new BufferedReader(new InputStreamReader(socket.getInputStream()));

        //Another reader to read characters typed by the user
        stdIn = new BufferedReader(new InputStreamReader(System.in));

        //scanner for menu option
        Scanner scanner = new Scanner(System.in);
        int menuOption;

        System.out.println("Press 1 to read from file or 2 to write to file");
        menuOption = scanner.nextInt();

        if (menuOption == 1){
            String filename = "";
            String text = "";

            System.out.println("Enter file name");
            filename = scanner.next();
            byte[] packet = new byte[512];

            //Constructing the RRQ Packet
            //Ading the OPCODE
            packet[0] = 1;
            //adding the filename
            filename.getBytes(Charset.forName("UTF-8"));
            byte[] filenameB = filename.getBytes(Charset.forName("UTF-8"));
            System.arraycopy(filenameB,0,packet,1, filenameB.length);
            //adding a 0
            packet[filenameB.length +1] = 0;
            //adding the mode
            System.arraycopy(mode,0,packet,1+filenameB.length+1,mode.length);
            //adding the last 0
            packet[1+filenameB.length+1+mode.length+1] = 0;



            out.println(packet);

        }else if(menuOption == 2){

        }

        socket.close();
    }catch(UnknownHostException e){
        System.err.println("Dont know about host" + address);
        System.exit(1);
    }catch(IOException e){
        System.err.println("Couldnt get I/O for the connection to " + address);
        System.exit(1);
    }
}

}

public class TCPServer {

public static void main(String[] args) throws IOException{

    //port of the server
    int port = 10000;

    //Socket objects
    ServerSocket masterSocket;
    Socket slaveSocket;

    //instantiate the server socket
    masterSocket = new ServerSocket(port);
    System.out.println("Server Started");

    boolean flag1 = true;

    while(true){

        slaveSocket = masterSocket.accept();
        System.out.println("Accepted TCP connection from: " +
                slaveSocket.getInetAddress() + ", " + slaveSocket.getPort() + "...");
        System.out.println("Initialising new Thread...");

        new TCPServerThread(slaveSocket).start();
    }
}

}

public class TCPServerThread extends Thread{

private Socket slaveSocket = null;

public TCPServerThread(Socket socket){
    super("TCPServerThread");
    this.slaveSocket = socket;
}

public void run(){

    byte[] ClientPacket = new byte[512];

    PrintWriter socketOutput;
    InputStream socketInput;
    try{
        //send packet to client
        socketOutput = new PrintWriter((slaveSocket.getOutputStream()), true);
        //read packet from client
        socketInput = new DataInputStream(slaveSocket.getInputStream());

        ClientPacket = socketInput.readAllBytes();
        System.out.println(new String(ClientPacket, StandardCharsets.UTF_8));

    }catch (IOException e){
        System.err.println(e);
    }

}

}

共有1个答案

计阳泽
2023-03-14

你无可救药地夸大了这一点。

WriterReader进行字符输入和输出<代码>输入流和输出流执行字节输入和输出。

你把基于字节的东西(最后,网络端口是基于字节的,而不是基于字符的)以危险的方式变成基于字符的东西,然后试图读写基于字符的东西。

解决办法很简单。别再这样了。你有基于字节的东西,绝对没有必要涉及到读写器。

一堆引起问题的行:

out.println(packet);

PrintStreams是调试辅助工具。你不能用它们来做这些。例如,这一行将打印换行符(在基于字节的流系统中绝对不是你想要的东西!),并将打印“对象”——它通过调用来实现。toString()方法,而数组的toString方法基本上是无用的。这就解释了为什么你看到了你所看到的。这不是发送字节的方式。你不能将字节发送到打印流(这是一个混乱的混乱,因为它试图让你将字符发送到基于字节的系统。正如我所说,你使用它进行调试,而不是其他。你不应该在这里使用它)。

new InputStreamReader(socket.getInputStream())

这很危险。你正在把一个基于字节的系统(InputStream)变成一个基于字符的系统(Reader),这总是意味着有人在对字符集编码做出明确的“带外”(不是基于该流中的数据)决定。在这种情况下,根据InputStreamReader的文档,你会得到“平台默认值”。幸运的是,从JDK18开始,它肯定是UTF-8,但在那之前,谁知道它是什么。你永远不想调用这个构造函数来避免混淆。新输入流读取器(socket.get输入流,标准字符集。UTF_8)

不过,最重要的是,首先不要成为一个读者。你对读取字符流毫无兴趣,你只需要字节。

如果字符串较小,并且关于它们“结束”的信息是“带外”完成的(例如:首先发送字节(而不是字符)的大小,然后发送字符串的X字节,UTF_8编码),您可以将其读取为字节,然后在此基础上生成字符串,绕过对Readers和Writer的任何需求。Reader和Writer只有在整个流都是基于字符的情况下才有用,或者如果您有巨大的字符串(数百兆字节),则只能通过首先将数据解释为字符来猜测它们的结束。(大多数情况下,这些协议都很糟糕,不应该使用)。

//Construct PrintWriter to write objects to the socket

不,不能将对象写入套接字。对象不是字节。您可以将字节写入套接字;有些对象会让自己变成ByTestStream,但这绝对不是一件小事,PrintWriter根本做不到。

   catch (IOException e) { System.err.println(e);

大多数代码都没有合理的途径来“处理”它们,但解决方法是继续使用它们。为了不抓住例外,打印一张绝望的便条,然后像什么都没发生一样继续下去。正确的做法也会减少代码,因此,双赢。

stdIn = new BufferedReader(new InputStreamReader(System.in));

        //scanner for menu option
        Scanner scanner = new Scanner(System.in);

你在用两种不同的方式读取标准输入。这没有意义。选一个。

我试着帮你修好它:

public class TCPClient {
  public static void main(String[] args) throws Exception { // always throw Exception from `main`.

    if (args.length != 2) {
        System.err.println("Usage: java EchoClient <address> <port>");
        System.exit(1);
        return; // Always return after System.exit.
    }

    byte[] mode = "octet".getBytes(Charset.forName("UTF-8"));
    String address = args[0];
    int port = Integer.parseInt(args[1]);

    Scanner scanner = new Scanner(System.in);
    scanner.useDelimiter("\\R"); // split on newlines, not spaces. So much more logical.
    // resources need to be safe-closed - use try-with!
    try (var socket = new Socket(address, port);
         var out = new BufferedOutputStream(socket.getOutputStream());
         var in = socket.getInputStream()) { 

        System.out.println("Press 1 to read from file or 2 to write to file");
        int menuOption = scanner.nextInt();

        if (menuOption == 1) {
            System.out.println("Enter file name");
            String filename = scanner.next();

            //Constructing the RRQ Packet
            //Adding the OPCODE
            out.write(1);
            out.write(filename.getBytes(StandardCharsets.UTF_8));
            out.write(0);
            // The above is dangerous; NUL (0) is actually a valid char.
            // A proper way to send strings is to send length in bytes
            // first. I'll leave it to you to fix your protocol.
            // If it can't be fixed, scan for `\0` chars and get rid of em.
            //adding the mode
            out.write(mode);
            out.write(0);
        }else if (menuOption == 2) {

        }
    }
}

一次发送一个字节可能很慢(因为它最终发送了整个包),但也可能很有用——数据只是被发送,而不是等待更多数据的很长时间。在你的情况下,你一次发送所有数据,所以非常快地发送所有数据不是一个好主意。因此,为什么输出流被包装在BufferedOutputStream中,它可以修复这个问题。如果你想保持连接打开,你可以随时使用flush()强制发送(关闭(),自然,也刷新)。

如果您想使用byte[]包,这很好,但是在这里看起来很复杂,也不必要。out.write(某个ByteArray),其中out是某种类型的输出流,工作正常。out.println(byteArray),其中out是某种类型的Writer,或PrintStream-根本不工作。(它需要数组,调用它不有用的toString(),然后使用未知的字符集转换这些字节并发送它,这些都不是您想要的)。

您同样需要从服务器代码中删除PrintStream等。

 类似资料:
  • 问题内容: 我在React中有一个表单,可以动态添加新的输入元素。这似乎 工作正常,但我似乎无法访问此 屏幕截图中所示的输入值… console.log数组 我尝试了以下 console.log(this.state.telephone.name) 和… console.log(this.state.telephone.tidx.name) 其中tidx是唯一键。 这是构造函数… and this

  • 我想访问来自该对象数组的ends\u数据。。。但我不能让它工作。输出未定义。请帮帮我。

  • 问题内容: 在Java中,如何获取byte []数组并从数组中删除前16个字节?我知道我可能必须通过将阵列复制到新阵列中来执行此操作。任何例子或帮助将不胜感激。 问题答案: 参见Java库中的类:

  • 问题内容: 我正在尝试减少分段数据的锁定对象的内存使用量。这里看到我的问题。或者只是假设您有一个字节数组,并且每16个字节可以(反)序列化为对象。让我们将其称为行长度为16个字节的“行”。现在,如果您从写程序线程中修改这样的行并从多个线程中读取,则需要锁定。如果字节数组大小为1MB(1024 * 1024),则意味着65536行和相同数量的锁。 这有点太多,而且我需要更大的字节数组,并且我想将其减

  • 我正在编写一个程序,处理两个原型消息,我需要处理从不同来源发送的字节[],这些来源发送foo消息或bar消息。由于我无法弄清楚它属于哪个消息,我使用任何类(附带的协议)来解析字节数组并找到它属于哪个类,但遇到了编译时错误。如果我将来添加更多的原型消息类,我可以使用其他方法来检测吗? 第二个原型 代码: 尝试调用any时if语句出错。is(): 方法是(类

  • 问题内容: 所以-我有一个需要a 作为输入的第三方库。我有一个。 我不想将字节写入磁盘..我想将其保留在内存中。我如何从提供的内容创建一个想法(无需写入磁盘)? 问题答案: 抱歉,不可能。除非您具有RAM磁盘,否则File本质上是磁盘上的实体-但这不是您可以用Java创建的东西。 这正是API不应该基于File对象(或过载以接受InputStream)的原因。