从SSLSocket读取的最快或最好的方式

时间:2013-10-31 19:35:59

标签: java sockets ssl https serversocket

我正在运行多线程简约http(s)服务器(不是网络服务器),它接受三个服务器套接字上的连接:本地,互联网和互联网-ssl。

每个套接字的超时时间为1000毫秒(将来可能会降低)。

工作线程读取这样的请求:

byte[] reqBuffer = new byte[512];
theSocket.getInputStream().read(reqBuffer);

现在的问题是,对于新实现的ssl套接字,出现了1 / n-1记录分割技术的问题。当使用ssl(4 / n-4等)时,一些客户端以其他奇怪的方式分裂,所以我想我可能会执行这样的多次读取:

byte[] reqBuffer = new byte[512];
InputStream is = theSocket.getInputStream();
int read = is.read(reqBuffer, 0, 128); // inital read - with x/n-x this is very small
int pos = 0;
if (read > 0) {
   pos = read;
}
int i = 0;
do {
   read = is.read(reqBuffer, pos, 128);
   if (read > 0) {
      pos += read;
   }
   i++;
} while(read == 128 && i < 3); // max. 3 more reads (4 total = 512 bytes) or until less than 128 bytes are read (request should be completely read)

适用于firefox或chrome等浏览器以及使用该技术的其他客户端。

现在我的问题是新方法要慢得多。对本地套接字的请求非常慢,以及2秒超时超时请求的脚本(我不明白为什么)。也许我的代码中存在一些逻辑问题?

有没有更好的方法从SSL套接字读取?因为每秒最多有数百甚至上千个请求,所以即使是http请求,新的read方法也会减慢。

注意:ssl-socket目前尚未使用,在我可以解决此问题之前不会使用。

我还尝试使用缓冲读取器读取行,因为我们在这里讨论的是http,但服务器爆炸用完了文件描述符(限制为20 000)。可能是因为我的实施。

我很感谢关于这个问题的每一个建议。如果您需要有关代码的更多信息,请告诉我,我会尽快发布。

修改 我实际上更多地考虑了我想要做的事情,我意识到它归结为读取HTTP标头。因此,最好的解决方案是实际读取行(或字符的字符)的请求行,并在x行之后停止读取或直到达到空行(标记标题的结尾)。 我当前的方法是在套接字的InputStream周围放置一个BufferedInputStream,并使用由BufferedReader“读取”的InputStreamReader读取它(问题:当我使用BufferedReader时使用BufferedInputStream是否有意义?)。 此BufferedReader读取字符的请求字符,检测行尾(\ r \ n)并继续读取,直到达到超过64个字符的行,最多读取8行或到达空行(标记HTTP标头的结尾)。我将在明天测试我的实现并相应地编辑此编辑。

修改 我差点忘了把结果写在这里:它有效。在每个套接字上,甚至比以前的工作方式更快。谢谢大家指点我正确的方向。我最终实现了这样:

List<String> requestLines = new ArrayList<String>(6);
InputStream is = this.cSocket.getInputStream();
bis = new BufferedInputStream(is, 1024);
InputStreamReader isr = new InputStreamReader(bis, Config.REQUEST_ENCODING);
BufferedReader br = new BufferedReader(isr);

/* read input character for character
* maximum line size is 768 characters
* maximum number of lines is 6
* lines are defined as char sequences ending with \r\n
* read lines are added to a list
* reading stops at the first empty line => HTTP header end
*/
int readChar; // the last read character
int characterCount = 0; // the character count in the line that is currently being read
int lineCount = 0; // the overall line count
char[] charBuffer = new char[768]; // create a character buffer with space for 768 characters (max line size)

// read as long as the stream is not closed / EOF, the character count in the current line is below 768 and the number of lines read is below 6
while((readChar = br.read()) != -1 && characterCount < 768 && lineCount < 6) {
    charBuffer[characterCount] = (char) readChar; // fill the char buffer with the read character
    if (readChar == '\n' && characterCount > 0 && charBuffer[characterCount-1] == '\r') { // if end of line is detected (\r\n)
        if (characterCount == 1) { // if empty line
            break; // stop reading after an empty line (HTTP header ended)
        }
        requestLines.add(new String(charBuffer, 0, characterCount-1)); // add the read line to the readLines list (and leave out the \r)
        // charBuffer = new char[768]; // clear the buffer - not required
        characterCount = 0; // reset character count for next line
        lineCount++; // increase read line count
    } else {
        characterCount++; // if not end of line, increase read character count
    }
}

2 个答案:

答案 0 :(得分:3)

由于您正在等待另一端发送更多数据(可能是永远不会发送的数据),因此这种情况很可能会变慢。

更好的方法是给它一个更大的缓冲区,如32KB(128小),只读取一次可用的数据。如果需要在某种消息中重新组装此数据,则不应使用超时或固定数量的循环,因为read()仅保证至少返回一个字节。

答案 1 :(得分:3)

你应该当然围绕SSLSocket的输入流包装一个BufferedInputStream。

您一次读取128个字节并推进偏移的技术完全没有意义。只需尽可能多地阅读并处理它。或者从缓冲流中一次一个字节。

同样,你应该把SSLSocket的输出流包装在BufferedOutputStream中。