【问题标题】:Java TCP - Sending a file using DataOutputStreamJava TCP - 使用 DataOutputStream 发送文件
【发布时间】:2017-10-02 14:14:08
【问题描述】:

您好,提前致谢。

所以目前,我正试图让我的 TCP 程序从目录(这是服务器端)读取文件,然后将该文件发送到请求它的套接字(客户端)。

这是我的代码:

服务器端:

File FileList = new File(".....filepath....");
Scanner scan = new Scanner(FileList);

//idToFile just searching for the name of the file the client asked for
String TheName = idToFile(Integer.toString(result));

//Opens the chosen file such as an image to be used     
File myImageFile = new File("....filepath...." + TheName);

//sendResponse is a function with a writeUTF (but only works for strings)
sendResponse("#OK");
sendResponse(TheName);
sendResponse(Long.toString(myImageFile.length()));

//This line causes the error and says that it expected a string and not a file          
output.writeUTF(myImageFile);


private void sendResponse(String response)
    {
        try 
        {
            output.writeUTF(response);
        } 
        catch (IOException ex) 
        {
            ex.printStackTrace();
        }
    }

客户端:

ClientHandler client = new ClientHandler();


//getResponse just catches the written strings, but it can't work with a file either
String TheMessage = client.getResponse();
String Name = client.getResponse();
long FileSize = Long.parseLong(client.getResponse());

ClientHandler 的代码如下:

public class ClientHandler 
{
    private static int port = 2016;

    private DataInputStream input;
    private DataOutputStream output;

    private Socket cSocket; 


    public ClientHandler()
    {
        cSocket = null;

        try
        {
            cSocket = new Socket("localhost", port); //connecting to the server 
            System.out.println("Client is connected on port " + port);

            output = new DataOutputStream(cSocket.getOutputStream());
            input = new DataInputStream(cSocket.getInputStream());
        }
        catch(IOException ex)
        {
            ex.printStackTrace();
        }
    }

    public void sendMessage(String message) //checking if the message is sent
    {
        try
        {
            output.writeUTF(message);
            output.flush();
            System.out.println("Message sent to the server");
        } 
        catch (IOException e) 
        {
            e.printStackTrace();
        }
    }

    public String getResponse()
    {
        String msg = "";
        try
        {
              msg = input.readUTF();
        }
        catch(IOException ex)
        {
            ex.printStackTrace(); 
        }
        return msg;
    }

}

那么我如何使用 DataOutputStream 将任何类型的文件从我的服务器发送到我的客户端,我的客户端如何使用 DataInputStream 捕获该文件并将其保存到磁盘?

谢谢。

【问题讨论】:

  • 什么是output?你能分享sendResponse的sn-p吗?
  • 这是什么?你在用什么? sendResponseoutputclient???
  • 我现在全部添加
  • 刚刚添加了其他内容。我希望它足够了。
  • 您可以考虑为此使用完善的协议,例如 HTTP。

标签: java tcp


【解决方案1】:

创建 DataOutputStreamDataInputStream 的新实例,如下所示。

DataOutputStream socketOut = new DataOutputStream(socketObject.getOutputStream());
DataInputStream  socketIn  = new DataInputStream(socketObject.getInputStream());

对于示例代码,请查看in this lecture notes.显示的示例类

为了快速参考,这里是示例类中的 sn-ps

DataInputStream streamIn = new 
              DataInputStream(new 
              BufferedInputStream(client.getInputStream()));
String incomingDataLine = streamIn.readLine();
...
...
DataOutputStream socketOut = new DataOutputStream(client.getOutputStream());
socketOut.writeBytes(line + '\n');

========= 编辑1:看完你的cmets

网络上的任何东西都是 1 和 0,即字节。 假设您要将图像从一台服务器传输到另一台服务器,那么推荐的方法是让服务器读取文件内容为 bytesjava.nio.Files.readAllBytes() 并将其写入套接字。

在客户端,从套接字读取所有字节(在实际项目中使用 BufferedReader)并将它们写入磁盘。您必须确保服务器和客户端都使用相同的编码。

使用 BufferedInputStream 和 BufferedOutputStream 代替 DataXXXStream。

这适用于任何文件类型 - mp3、mp4、jpeg、png、acc、txt、java。要使其在客户端系统中可用,请确保创建具有正确扩展名的文件。

【讨论】:

  • 不是我要发送字符串。我希望能够发送文件,例如文本文件、图像文件或视频文件。基本上我需要能够通过我的流发送这些类型的文件(或技术上的任何其他文件)。
  • @Duckman 网络上的任何东西都是 1 和 0,即字节。如果您想发送文件内容,则序列化 java.nio.File 对象(不推荐)或使用 java.nio.Files.readAllBytes() 将其内容作为字节获取。
【解决方案2】:

这里以一个简单的服务器为例,它向每个客户端发送一个二进制文件,无需进一步交互。

套接字提供输出流以将字节写入客户端。我们有一个要发送的文件“picin.jpg”,因此我们需要将该文件的每个字节写入套接字的输出流。为此,我们使用 FileInputStream 包裹在 BufferedInputStream 中。

package networking;

import java.io.BufferedInputStream;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.InetSocketAddress;
import java.net.ServerSocket;
import java.net.Socket;

public class Server {

  public static void main(String[] args) {
    try {
      ServerSocket ss = new ServerSocket();
      ss.bind(new InetSocketAddress("localhost", 4711));
      for(;;) {
        Socket sock = ss.accept();
        System.out.println("Connected to " + sock.getInetAddress());
        try (
          InputStream in = new BufferedInputStream(new FileInputStream("picin.jpg"));
          OutputStream out = sock.getOutputStream();
        ) 
        {
          int bytesSent = Util.copy(in, out);
          System.out.println(String.format("%d bytes sent.", bytesSent));
        }
      }
    } catch (IOException e) {
      e.printStackTrace();
    }
  }

}

我将复制方法提取到一个 util 类中,因为我们在客户端中需要完全相同的方法。我们使用一个小字节数组作为缓冲区,并从一个流中一个接一个地复制一个块。

package networking;

import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;

public class Util {
  public static int copy(InputStream in, OutputStream out) throws IOException {
    byte[] buf = new byte[2048];
    int bytesRead = 0;
    int totalBytes = 0;
    while((bytesRead = in.read(buf)) != -1) {
      totalBytes += bytesRead;
      out.write(buf, 0, bytesRead);
    }
    return totalBytes;
  }

}

客户端打开与服务器的连接并将接收到的字节写入文件。套接字为输入流提供来自服务器的字节。我们使用包裹在BufferedOutputStream 中的FileOutputStream 来写入文件。我们使用相同的Util.copy() 方法将字节从一个流实际复制到另一个流。

package networking;

import java.io.BufferedOutputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.InetAddress;
import java.net.Socket;

public class Client {

  public static void main(String[] args) {
    try {
      Socket sock = new Socket(InetAddress.getByName("localhost"), 4711);
      try(
        InputStream in = sock.getInputStream();
        OutputStream out = new BufferedOutputStream(new FileOutputStream("picout.jpg"))
      )
      {
        System.out.println("Connected to " + sock.getRemoteSocketAddress());
        int bytesCopied = Util.copy(in, out);
        System.out.println(String.format("%d bytes received", bytesCopied));
      }
    } catch (IOException e) {
      e.printStackTrace();
    }
  }

}

注意:流由 try-with-ressources 语句自动关闭。套接字关闭,而相应的流关闭。

Java 8 包含一些方便的方法来将字节从文件复制到输出流或从输入流复制到文件。见java.nio.file.Files

【讨论】:

  • 非常感谢。这解决了我需要做的大部分事情。所以现在它成功地在客户端保存了一个图像文件(但似乎确实切断了图像的最后几个字节),但它仍然显示异常。这里是:java.lang.ArrayIndexOutOfBoundsException: len == -1 off == 0 缓冲区长度 == 4096。知道可能是什么问题吗?
  • 您还提到流会自动关闭,但我需要它们保持打开状态以进行更多操作(客户端使用我的注销按钮)。知道如何保持流打开吗?
  • 您应该将此视为一个起点。自己做更多。如果有异常,请阅读异常类的文档。阅读引发 execption 的特定函数的文档。有时在抛出此类异常时会有提示。了解如何使用调试器。试验你的代码。试着理解每一行代码的作用。如果你想保持一个流打开,那么在你的工作完成之前不要关闭它。
猜你喜欢
  • 2011-11-28
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2015-09-03
相关资源
最近更新 更多