如何使用Java中的socket进行快速数据读取?

时间:2014-12-19 21:58:25

标签: java sockets blocking synchronous

我正在使用SocketChannel进行单一连接:

int sampleBufferSize = 50;
StringBuilder data = new StringBuilder();
ByteBuffer bf = ByteBuffer.allocate(sampleBufferSize);
SocketChannel sc = new SocketChannel();
while(true)
    if(sc.read(bf) > 0){
        bf.flip();
        while(bf.hasRemaining())
            data.append((char) bf.get());
        bf.clear();
    }else{
        fireDataReceived(data.toString());
        data.delete(0, data.length());
    }

此代码效率不高,但它在0.05秒内从同一台PC读取130 KB的HTTP POST请求。现在我正在尝试编写一个具有类似功能但使用Socket的类。这是代码:

private static final int TIMEOUT_MILLIS = 50;
private boolean reading = false;
private long readBeginTime = 0;
private StringBuilder buffer = new StringBuilder();
private Thread thread = new Thread(){
    public void run(){
        while(!isInterrupted()){
            try {
                int b = getInputStream().read();
                if(b == -1){
                    if(reading)
                        fireDataReceived();
                    close();
                }else{
                    if(!reading){
                        reading = true;
                        readBeginTime = System.currentTimeMillis();
                        setSoTimeout(TIMEOUT_MILLIS);
                    }
                    buffer.append((char) b);
                }
            } catch (SocketTimeoutException e){
                fireDataReceived();
            } catch (IOException e) {
                e.printStackTrace();
                if(reading)
                    fireDataReceived();
                close();
            }
        }
    }
};
private void fireDataReceived(){
    BufferedSocketEvent e = new BufferedSocketEvent(this, System.currentTimeMillis() - readBeginTime, buffer.toString());
    buffer.setLength(0);
    reading = false;
    try {
        setSoTimeout(0);
    } catch (SocketException e1) {
        e1.printStackTrace();
    }
    for(BufferedSocketListener listener: listeners)
        listener.dataReceived(e);
}

问题是同一个请求需要0.4秒,我不知道为什么需要这么长时间。请解释我的代码有什么问题。

2 个答案:

答案 0 :(得分:0)

您的流代码的问题在于您一次只读取一个字节,这个字节很慢,并且一次只能附加到StringBuilder个字节,同上。试试这个:

BufferedReader in = new BufferedReader(new InputStreamReader(socket.getInputStream());
char[] chars = new char[8192];
int count;
while ((count = in.read(chars)) > 0)
{
    buffer.append(chars, 0, count);
    fireDataReceived();
}

请注意,使用读取超时作为分隔请求的方法并不正确。您需要解析数据才能执行此操作。 TCP是没有消息边界的流协议,并且不保证单独接收单独的发送。

答案 1 :(得分:0)

您必须使用readLine()或类似方法。 HTTP标头的末尾用空行标记。如果您没有检测到线路,则无法知道何时停止读取数据。依赖超时或TCP片段不是正确的解决方案。如果请求不包含新行,则需要等到一个出现或连接终止/超时。你应该等待至少几秒钟。

我也会摆脱那些听众。能够为单个套接字添加多个侦听器似乎是一个好主意,但对HTTP标头唯一明智的做法是解析它。解析器还需要通知读者何时停止阅读。

我会从这样的事情开始:

ServerSocket serverSocket = new ServerSocket(8888);
Socket clientSocket = serverSocket.accept();
BufferedReader reader = new BufferedReader(new InputStreamReader(clientSocket.getInputStream(), "ASCII"));
String[] r = reader.readLine().split(" ");
String type = r[0];
String resource = r[1];
String version = r[2];
Map<String, String> headers = new HashMap<String,String>();
while(true) {
    String line = reader.readLine();
    if(line.isEmpty()) {
        break;
    }
    String headerLine[] = line.split(":",2);
    headers.put(headerLine[0],headerLine[1].trim());
}
processHeader(type, resource, version, headers);