如何使用相同的套接字发送二进制文件和文本

Kev*_*vik 7 java sockets concurrency networking android

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

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

服务器是一个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();
Run Code Online (Sandbox Code Playgroud)

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

 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);
 }
Run Code Online (Sandbox Code Playgroud)

PoB*_*lek 6

我不认为在你的情况下使用两个线程是必要的.只需使用socket's',InputStream然后OutputStream在发送短信后发送二进制数据.

服务器代码

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);
Run Code Online (Sandbox Code Playgroud)

客户代码

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;
}
Run Code Online (Sandbox Code Playgroud)

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

希望这可以帮助.


编辑

正如@EJP正确指出的那样,BufferedReader在客户端使用a 可能会导致二进制数据损坏或丢失,因为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();
}
Run Code Online (Sandbox Code Playgroud)