问题
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