最近RDP协议数据转发程序,有一个错误百思不得其解。通过多方尝试,才发现是BufferedInputStream搞得鬼。
转发程序从一个socket接收数据, 然后处理,通过另外一个socket发送出去。
为了提高效率,转发后面部分是采用nio的select来完成。但是转发前面部分,为了协商协议方便采用的blocking模式。在同步转异步的过程中,偶尔会出现协议错误的问题。
错误体现在一个packet解密出来是乱码。通过抓包,前后对比,才发现中间掉了一个packet。最后分析定位,是BufferedInputStream将socket接收的数据会缓存下来,在同步转异步的过程中,丢失BufferedInputStream中缓存的数据。
具体的模拟代码如下:
server:
public class Server {
public static void main(String[] args) throws IOException {
Server s = new Server();
s.test();
}
private ServerSocket serverSocket;
static int port = 8000;
private Selector selector;
private SocketChannel channel;
public void test() throws IOException {
ServerSocketChannel serverChannel = ServerSocketChannel.open();
serverSocket = serverChannel.socket();
serverSocket.bind(new InetSocketAddress(port));
serverChannel.configureBlocking(false);
selector = Selector.open();
serverChannel.register(selector, SelectionKey.OP_ACCEPT);
for (;;) {
selector.select();
Iterator<SelectionKey> it = selector.selectedKeys()
.iterator();
while (it.hasNext()) {
SelectionKey key = it.next();
if (key.isValid())
handle0(key);
it.remove();
}
}
}
private void handle0(SelectionKey key) throws IOException {
if (key.isAcceptable()) {
ServerSocketChannel server = (ServerSocketChannel) key.channel();
SocketChannel channel = server.accept();
this.channel = channel;
DataInputStream dis = new DataInputStream(new BufferedInputStream(
channel.socket().getInputStream()));
// ByteBuffer dsts = ByteBuffer.allocate(2);
// int o = channel.read(dsts);
int length = 4;
byte[] packet = new byte[length];
dis.readFully(packet, 0, length);
System.out.println("received data:" +packet[length-1]);
channel.configureBlocking(false);
// ByteBuffer dsts1 = ByteBuffer.allocate(2);
// o = channel.read(dsts1);
// logger.info("received data:" + o + dsts1);
channel.register(selector, SelectionKey.OP_READ);
}
else if (key.isReadable()){
ByteBuffer dsts1 = ByteBuffer.allocate(2);
int o = channel.read(dsts1);
System.out.println("received data:" + o + dsts1.get());
}
}
}
client代码:
public class Client {
/**
* @param args
* @throws IOException
* @throws InterruptedException
*/
public static void main(String[] args) throws IOException, InterruptedException {
Socket s = new Socket();
SocketAddress endpoint = new InetSocketAddress("localhost",Server.port);
s.connect(endpoint);
byte[] ba = new byte[]{1,2,3,4,5,6,8,9,10,11,12};
s.getOutputStream().write(ba );
Thread.sleep(1000);
byte[] ba1 = new byte[]{13,14,15};
s.getOutputStream().write(ba1);
}
}
服务端就只能收到1,2,13,14,15,中间的其他数据就丢失了。