Reading Lines and byte[] from input stream

放肆的年华 提交于 2020-01-07 03:04:49

问题


I have a network socket which recieves the following types of messages: (about 24 per second)

  • Commands EG: [Pos,1,2,3,4,1,2,3,4]\n

  • Images: [IMG,36000]\nbyte[36000] (byte[36000] is an array of 36000 bytes)

So I would need an Reader for that stream that is able to read lines and byte[]. The problem is, that the BufferedReader converts the charset, which is very, very, very bad for images, so reading a string and converting it to byte seems not to be an option.

I tried to connect a DataInputStreamReader and a BufferedReader to the stream but it seems to break after the first change of the reader, but I am not really sure about what caused the problem there.

The obvious solution is:

char c;
String s = "";
do{
   c= (char)read.read(); //maybe charset conversion here
   s+=c;
}while(c!= "\n");
if(s.startsWith("[IMG")){
  int len = Integer.parseInt(s.split(",")[1]);
  byte[] img = new byte[len];
  read.read(img);
  ...   

But I am searching for a better one where I need to do less manually.

  • What is the recommended way to deal with this problem?
  • Is there a way to connect two or more readers to an input stream?
  • Is there a reader which can read byte[] and strings?
  • Is it probably easier to write everything to byte[] and read from that? (How do I know then, when to stop reading?)
  • Can I prevent the chartype conversion which messes the byte[] up? (Then i could use the Buffered Reader)

In the easiest case I could write that code: (Which is actually not much more :D)

String s = read.readLine();
String[] parts = s.split(",");
if(parts[0].equals("[IMG")){
  byte[] img = new byte[Integer.parseInt(parts[1])];
  read.readByte(img);
  ...

回答1:


Changing the viewpoint can help: Look at it as a byte stream that gets converted to strings as needed.

In my case I have a TCP client/server pair that must handle messages containing several ASCII control characters including CR & LF (= the java "newline" on Windows) - and everything must be passed through "as is".

A string can be used to build a message with embedded control codes but embedding any "newline" character (e.g. VT) means that a string cannot be used send or receive the message - the workaround is to pass the data as bytes instead.

Heres the essence of what I did (on server and client) :

// ASCII control codes 

char SOH = (char) 0x01;
char STX = (char) 0x02;
char ETX = (char) 0x03;

String CRLF = "" + (char) 0x0D + (char) 0x0A;

int    characterCount = 0;
String characterSet   = "US-ASCII"; // "UTF-8", UTF-16", etc.
String incomingData   = "";
String outgoingData   = "";

ServerSocket   servSock  =  new ServerSocket( servPort );
Socket         clntSock  =  servSock.accept();                  

InputStream    incomingData  =  clntSock.getInputStream();
OutputStream   outgoingData  =  clntSock.getOutputStream();

while ( true ) 
{
    characterCount = in.read( byteBuffer ); // blocks, waiting for remote machine

    incomingData = new String( byteBuffer, characterSet ); // omit 2nd parameter to use default encoding

    outgoingData = SOH + STX + "Whatever it is" + CRLF + CRLF + CRLF + "you need" + CRLF + "to do" + ETX;

    byteBuffer   = outgoingData.getBytes( CharacterSet );   

    out.write( byteBuffer );
}

The far end gets exactly what was sent (38 characters): SOH + STX + "Whatever it is" + CRLF + CRLF + CRLF + "you need" + CRLF + "to do" + ETX

A final thought : If there was a way/method to specify the "newline" used by the I/O packages something like this should be possible:

//   My first attempt was for a scanner that is functionally equivalent to System.out but 
//   automatically adjusts itself to the SYSTEM-DEPENDENT newline:
//
//String  LineSeparator     = System.getProperty( "line.separator" );
//String  MessageSeparator  = Pattern.quote( LineSeparator );      
//Pattern EndOfMessageRegEx = Pattern.compile( MessageSeparator ); 

String  MessageEndsWith   = "" + STX + ETX; // Change this to whatever you need
String  MessageSeparator  = Pattern.quote( MessageEndsWith );   
Pattern EndOfMessageRegEx = Pattern.compile( MessageSeparator );

ServerSocket   thisMachine  = new ServerSocket( portNumber );   
Socket         otherMachine = thisMachine.accept();     

PrintWriter    messageToClient = new PrintWriter( otherMachine.getOutputStream(), true );

BufferedReader messageFromClient  = new BufferedReader( 
               new DataInputStreamReader( otherMachine.getInputStream(),
                                          CharacterEncoding ) );
Scanner     ReceivedData =  new Scanner( messageFromClient ).useDelimiter( EndOfMessageRegEx );                                             



回答2:


First accept all as binary data, using an InputStream.

InputStream stream = ...

And then on reading when recognizing text:

int b = stream.read();
if (b == (byte)'E') { // Text stream recognized.
    BufferedReader reader = new BufferedReader(
        new InputStreamReader(stream, charset));
    ... use read no longer stream
}

You may create a Reader on an InputStream already read from. For the control, you'll probably use a separate method and try-with-resources. A BufferedInputStream might be wrapping the original InputStream for the binary data.



来源:https://stackoverflow.com/questions/33149940/reading-lines-and-byte-from-input-stream

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!