【问题标题】:how to send both binary file and text using the same socket如何使用同一个套接字发送二进制文件和文本
【发布时间】:2013-09-10 05:15:54
【问题描述】:

我必须将一个短字符串作为文本从客户端发送到服务器,然后再发送一个二进制文件。

如何使用同一个套接字连接发送二进制文件和字符串?

服务器是 java 桌面应用程序,客户端是 Android 平板电脑。我已经将其设置为在客户端和服务器之间双向发送短信。我还没有完成二进制文件发送部分。

一个想法是设置两个同时运行的独立服务器。我认为如果我使用两个不同的端口号并在应用程序中的两个不同线程上设置服务器,这是可能的。而且我必须在 Android 应用程序的两个服务上设置两个并发客户端。

另一个想法是以某种方式使用 if else 语句来确定正在发送两种类型的文件中的哪一种,无论是二进制文本,并使用适当的方法来接收正在发送的文件类型的文件。

发送文本的示例代码

 PrintWriter out;
 BufferedReader in;

out = new PrintWriter(new BufferedWriter
(new OutputStreamWriter(Socket.getOutputStream())) true,);
in = new BufferedReader(new InputStreamReader(socket.getInputStream()));

out.println("test out");
String message = in.readLine();

发送二进制文件的示例代码

 BufferedOutputStream out;
 BufferedInputStream in;
 byte[] buffer = new byte[];
 int length = 0;

 out = new BufferedOutputStream(new FileOutputStream("test.pdf));
 in = new BufferedInputStream(new FileOutputStream("replacement.pdf"));

 while((length = in.read(buffer)) > 0 ){
 out.write(buffer, 0, length);
 }

【问题讨论】:

标签: java android sockets networking concurrency


【解决方案1】:

我认为在您的情况下不需要使用两个线程。发送短信后,只需使用socketInputStreamOutputStream 即可发送二进制数据。

服务器代码

OutputStream stream = socket.getOutputStream();
PrintWriter out = new PrintWriter(
    new BufferedWriter(
        new OutputStreamWriter(stream)
    )
);
out.println("test output");
out.flush(); // ensure that the string is not buffered by the BufferedWriter

byte[] data = getBinaryDataSomehow();
stream.write(data);

客户代码

InputStream stream = socket.getInputStream();
String message = readLineFrom(stream);

int dataSize = getSizeOfBinaryDataSomehow();
int totalBytesRead = 0;
byte[] data = new byte[dataSize];

while (totalBytesRead < dataSize) {
    int bytesRemaining = dataSize - totalBytesRead;
    int bytesRead = stream.read(data, totalBytesRead, bytesRemaining);

    if (bytesRead == -1) {
        return; // socket has been closed
    }

    totalBytesRead += bytesRead;
}

为了在客户端确定正确的dataSize,您必须以某种方式传输二进制块的大小。您可以在服务器代码中的out.flush() 之前将其作为字符串发送,或者将其作为二进制数据的一部分。在后一种情况下,前四个或八个字节可以保存二进制数据的实际长度(以字节为单位)。

希望这会有所帮助。


编辑

正如@EJP 正确指出的那样,在客户端使用BufferedReader 可能会导致二进制数据损坏或丢失,因为BufferedReader 从二进制数据中“窃取”一些字节以填充其缓冲区。相反,您应该自己读取字符串数据,然后查找分隔符或通过其他方式传输字符串数据的长度。

/* Reads all bytes from the specified stream until it finds a line feed character (\n).
 * For simplicity's sake I'm reading one character at a time.
 * It might be better to use a PushbackInputStream, read more bytes at
 * once, and push the surplus bytes back into the stream...
 */
private static String readLineFrom(InputStream stream) throws IOException {
    InputStreamReader reader = new InputStreamReader(stream);
    StringBuffer buffer = new StringBuffer();

    for (int character = reader.read(); character != -1; character = reader.read()) {
        if (character == '\n')
            break;
        buffer.append((char)character);
    }

    return buffer.toString();
}

【讨论】:

  • 是的,这对我帮助很大!谢谢,兄弟:)
  • 这行不通。 BufferedReader 可能会窃取以下一些二进制数据?
  • @EJP 嗯,很公平。我已将答案编辑为不在客户端使用 BufferedReader
  • 还是不行。 InputStreamReader 也有一个缓冲区。您需要在此处使用DataInputStream.readUTF() 之类的内容,并在发件人处使用相反的内容。
【解决方案2】:

您需要先发送字符串,然后是字节数组的大小,然后是字节数组,使用String.startsWith() 方法检查正在发送的内容。

【讨论】:

    【解决方案3】:

    您可以了解 HTTP 协议的工作原理,它本质上发送“ascii 和人类可读”标头(可以这么说),然后可以使用适当的编码(例如 base64)添加任何内容。你可以自己创造类似的东西。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2011-12-03
      • 2017-08-10
      • 2011-07-26
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多