Warning: file_get_contents(/data/phpspider/zhask/data//catemap/9/java/365.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
如果在写入后读取过快,则java.net.Socket会阻止超时_Java_Sockets - Fatal编程技术网

如果在写入后读取过快,则java.net.Socket会阻止超时

如果在写入后读取过快,则java.net.Socket会阻止超时,java,sockets,Java,Sockets,AWS机器上Java 7上运行的Java套接字代码的奇怪行为: 我的服务器有一个自定义协议,我们在其中打开一个套接字,然后发送和接收BSON消息。测试客户端创建并打开一个套接字,发送一个请求,然后在套接字的InputStream上驻留,等待响应。当收到响应或读取超时时,将发送下一个请求 我发现,如果在通过OutputStream发送请求后,我过快地触摸套接字的InputStream,套接字偶尔会阻塞,直到读取超时。我已经尝试了socket.getInputStream().read(…)和soc

AWS机器上Java 7上运行的Java套接字代码的奇怪行为:

我的服务器有一个自定义协议,我们在其中打开一个套接字,然后发送和接收BSON消息。测试客户端创建并打开一个套接字,发送一个请求,然后在套接字的InputStream上驻留,等待响应。当收到响应或读取超时时,将发送下一个请求

我发现,如果在通过
OutputStream
发送请求后,我过快地触摸套接字的
InputStream
,套接字偶尔会阻塞,直到读取超时。我已经尝试了
socket.getInputStream().read(…)
socket.getInputStream().available()两个调用都会导致问题。如果我只是在发送后等待200毫秒左右,我就可以从服务器上获得近100%的成功读取。如果在同一子网上的系统上,如果我在写入后立即触摸套接字(
socket.getOutputStream().write(…);
socket.getOutputStream().flush()
),则套接字会阻塞,直到达到其20秒超时,占所有尝试的1%到7%

还有人见过这种行为吗?你知道是什么引起的吗?你对如何处理这件事有什么建议吗?我预计,在高速网络上,大多数读取都会在20到40毫秒之间返回(除了那些阻塞和超时的读取之外,大多数都是这样)

实际使用的代码相当复杂,但以下是一些相关的代码片段:

高级读/写:

InputStream is = sock.getInputStream();
OutputStream os = sock.getOutputStream();
String req = getRequestData();

String uuid = UUID.randomUUID().toString();

long start = System.currentTimeMillis();
protocolHandler.write(uuid, getUsername(), os, req);

long dt = System.currentTimeMillis() - start;
if (dt < 125l) {
    try { Thread.sleep(125-dt); } catch (InterruptedException ex) {}
}

String in = protocolHandler.read(uuid, is, timer, getResponseCount(), getTimeout());
套接字写入:

public void write(String messageId, String playerId, OutputStream os, String hexEncodedBinary) throws IOException {
    String messageHexBytes = substituteVariables(hexEncodedBinary);

    AbstractMessage messageObject = MessageRewriter.parseRequestData(messageHexBytes);
    int seq = MessageRewriter.getSequence(messageHexBytes);

    messageObject.setPassthrough(messageId);
    byte[] messageBytes = MessageRewriter.serialize(seq, messageObject);

    os.write(messageBytes);
    os.flush();
}
套接字读取:

public String read(String messageId, InputStream socket, LatencyTimer latencyTimer, int requiredResponseCount, int socketTimeoutMillis) 
        throws ReadException {
    String threadName = "thread "+Thread.currentThread().getId();

    StringBuilder result = new StringBuilder("passthrough "+messageId+"\n");
    int nBytesThisRead = -1;
    byte[] buffer=null;
    try {
        int nResponses = 0;

        // As long as we have bytes or need a response, continue to produce messages.  Messages we don't use will be cached.
        // The socket object throws an exception on timeout (20 seconds-ish) to terminate on "nothing read".
        //
        // TODO: refactor this abortion so it's readable and understandable
        //
        while ((! interrupted) && (nResponses < requiredResponseCount || socket.available() > 0)) {
            // clear "buffer" to make log messages less confusing (because on 0-byte reads, residual data is logged if we don't do this)
            buffer = new byte[0];

            // read the size bytes
            int totalBytesRead = 0;
            byte[] sizeBuffer = new byte[4];
            while (totalBytesRead < BYTES_PER_INTEGER) {
                try {
                    nBytesThisRead = socket.read(sizeBuffer, totalBytesRead, BYTES_PER_INTEGER-totalBytesRead);
                    if (nBytesThisRead > 0) {
                        latencyTimer.stop()
                        totalBytesRead += nBytesThisRead;
                    }
                }
                //
                // this is the timeout we get ~5% of the time if I don't wait ~ 100ms
                //
                catch (java.net.SocketTimeoutException e) {
                    log.error(threadName+" timeout waiting for size bytes");
                    latencyTimer.stop();
                    return "";
                }
            }

            int messageSize = getLittleEndianInteger(sizeBuffer);
            log.debug(threadName+": message size: " + messageSize);

            buffer = Arrays.copyOf(sizeBuffer, BYTES_PER_INTEGER+messageSize);

            // reset; now read the message body
            totalBytesRead = 0;
            while (totalBytesRead < messageSize) {
                nBytesThisRead = socket.read(buffer, BYTES_PER_INTEGER+totalBytesRead, messageSize-totalBytesRead);
                if (nBytesThisRead > 0)
                    totalBytesRead += nBytesThisRead;
            }

            if (totalBytesRead != messageSize) {
                log.error(String.format("%s abandoning attempt to read %d responses for id %s. Read %d bytes; needed %d.", 
                        threadName, requiredResponseCount, messageId, totalBytesRead, messageSize));
                throw new ReadException(
                        "Unable to read complete Gluon message.", null, result.toString()+toHex(buffer, BYTES_PER_INTEGER + nBytesThisRead));
            }

            message = MessageRewriter.deserialize(buffer);
            String hexString = toHex(buffer, BYTES_PER_INTEGER + messageSize);
            String uuid = message.getPassthrough();

            if (messageId.equals(uuid)) {
                ++nResponses;
            }
            else {
                log.debug(String.format("Read: %s message type %s with msgId %s to cache", 
                        threadName, message.getClass().getSimpleName(), uuid));
                messageCache.put(uuid, new MessageToClient(message, hexString));
            }

            // even ignored messages get sent to the verifiers
            if (log.isDebugEnabled()) {
                log.debug(String.format("Read message for %s (%d bytes): %s", uuid, BYTES_PER_INTEGER + messageSize, hexString));
                log.debug(String.format("%s Read: message type %s with msgId %s from socket; still need %d response messages.", 
                        threadName, message.getClass().getSimpleName(), messageId, requiredResponseCount-nResponses));
            }
            result.append(hexString);
            result.append("\n");
        }

    } catch (IOException e) {
        // TODO: clear out the socket? We'd need a new "open new socket" checkbox in the UI, and fail-fast when unchecked.
        String msg = result.toString()+"partial:"+toHex(buffer, BYTES_PER_INTEGER + nBytesThisRead);
        log.error(threadName+" throwing read exception; read message so far is:\n"+msg,e);
        throw new ReadException("Unable to read expected result.", e, msg);
    }

    return result.toString();
}
公共字符串读取(字符串消息ID、InputStream套接字、LatencyTimer LatencyTimer、int requiredResponseCount、int socketTimeoutMillis)
抛出ReadException{
字符串threadName=“thread”+thread.currentThread().getId();
StringBuilder结果=新的StringBuilder(“传递”+messageId+“\n”);
int nBytesThisRead=-1;
字节[]缓冲区=空;
试一试{
int nResponses=0;
//只要我们有字节或需要响应,就继续生成消息。我们不使用的消息将被缓存。
//套接字对象在超时(20秒)时抛出异常,以在“未读取”时终止。
//
//TODO:重构此文件,使其可读性和可理解性
//
而((!interrupted)&(nResponses0)){
//清除“缓冲区”以减少日志消息的混乱(因为在0字节读取时,如果不这样做,将记录剩余数据)
缓冲区=新字节[0];
//读取大小字节
int totalBytesRead=0;
字节[]sizeBuffer=新字节[4];
while(totalBytesRead<字节/整数){
试一试{
nBytesThisRead=socket.read(sizeBuffer,totalBytesRead,字节/INTEGER-totalBytesRead);
如果(nBytesThisRead>0){
latencyTimer.stop()
totalBytesRead+=nBytesThisRead;
}
}
//
//如果我不等待约100毫秒,我们将获得约5%的超时时间
//
catch(java.net.SocketTimeoutException e){
log.error(threadName+“等待大小字节超时”);
latencyTimer.stop();
返回“”;
}
}
int messageSize=getLittleEndianInteger(sizeBuffer);
log.debug(threadName+“:消息大小:“+messageSize”);
buffer=Arrays.copyOf(sizeBuffer,字节/整数+消息大小);
//重置;现在阅读消息正文
totalBytesRead=0;
while(totalBytesRead0)
totalBytesRead+=nBytesThisRead;
}
if(totalBytesRead!=消息大小){
log.error(String.format(“%s放弃读取id%s的%d个响应的尝试。读取%d个字节;需要%d.”,
threadName、requiredResponseCount、messageId、TotalByteRead、messageSize);
抛出新的ReadException(
“无法读取完整的胶子消息。”,null,result.toString()+toHex(缓冲区,字节/整数+nBytesThisRead));
}
message=MessageRewriter.反序列化(缓冲区);
字符串hexString=toHex(缓冲区,字节/整数+消息大小);
字符串uuid=message.getPassthrough();
if(messageId.equals(uuid)){
++答复;
}
否则{
log.debug(String.format(“读取:%s消息类型%s,要缓存的msgId为%s”),
threadName,message.getClass().getSimpleName(),uuid));
put(uuid,newmessagetoclient(message,hexString));
}
//甚至被忽略的消息也会被发送到验证器
if(log.isDebugEnabled()){
log.debug(String.format(“读取%s的消息(%d字节):%s)”,uuid,字节/u整数+消息大小,hexString));
log.debug(String.format)(“%s已读:消息类型%s,msgId为%s,来自套接字;仍需要%d条响应消息。”,
threadName、message.getClass().getSimpleName()、messageId、requiredResponseCount(响应次数));
}
result.append(十六进制字符串);
结果。追加(“\n”);
}
}捕获(IOE异常){
//TODO:清除套接字?我们需要在UI中添加一个新的“打开新套接字”复选框,如果不选中,则会快速失败。
字符串msg=result.toString()+“partial:”+toHex(缓冲区,字节/整数+nBytesThisRead);
log.error(threadName+“引发读取异常;读取
public String read(String messageId, InputStream socket, LatencyTimer latencyTimer, int requiredResponseCount, int socketTimeoutMillis) 
        throws ReadException {
    String threadName = "thread "+Thread.currentThread().getId();

    StringBuilder result = new StringBuilder("passthrough "+messageId+"\n");
    int nBytesThisRead = -1;
    byte[] buffer=null;
    try {
        int nResponses = 0;

        // As long as we have bytes or need a response, continue to produce messages.  Messages we don't use will be cached.
        // The socket object throws an exception on timeout (20 seconds-ish) to terminate on "nothing read".
        //
        // TODO: refactor this abortion so it's readable and understandable
        //
        while ((! interrupted) && (nResponses < requiredResponseCount || socket.available() > 0)) {
            // clear "buffer" to make log messages less confusing (because on 0-byte reads, residual data is logged if we don't do this)
            buffer = new byte[0];

            // read the size bytes
            int totalBytesRead = 0;
            byte[] sizeBuffer = new byte[4];
            while (totalBytesRead < BYTES_PER_INTEGER) {
                try {
                    nBytesThisRead = socket.read(sizeBuffer, totalBytesRead, BYTES_PER_INTEGER-totalBytesRead);
                    if (nBytesThisRead > 0) {
                        latencyTimer.stop()
                        totalBytesRead += nBytesThisRead;
                    }
                }
                //
                // this is the timeout we get ~5% of the time if I don't wait ~ 100ms
                //
                catch (java.net.SocketTimeoutException e) {
                    log.error(threadName+" timeout waiting for size bytes");
                    latencyTimer.stop();
                    return "";
                }
            }

            int messageSize = getLittleEndianInteger(sizeBuffer);
            log.debug(threadName+": message size: " + messageSize);

            buffer = Arrays.copyOf(sizeBuffer, BYTES_PER_INTEGER+messageSize);

            // reset; now read the message body
            totalBytesRead = 0;
            while (totalBytesRead < messageSize) {
                nBytesThisRead = socket.read(buffer, BYTES_PER_INTEGER+totalBytesRead, messageSize-totalBytesRead);
                if (nBytesThisRead > 0)
                    totalBytesRead += nBytesThisRead;
            }

            if (totalBytesRead != messageSize) {
                log.error(String.format("%s abandoning attempt to read %d responses for id %s. Read %d bytes; needed %d.", 
                        threadName, requiredResponseCount, messageId, totalBytesRead, messageSize));
                throw new ReadException(
                        "Unable to read complete Gluon message.", null, result.toString()+toHex(buffer, BYTES_PER_INTEGER + nBytesThisRead));
            }

            message = MessageRewriter.deserialize(buffer);
            String hexString = toHex(buffer, BYTES_PER_INTEGER + messageSize);
            String uuid = message.getPassthrough();

            if (messageId.equals(uuid)) {
                ++nResponses;
            }
            else {
                log.debug(String.format("Read: %s message type %s with msgId %s to cache", 
                        threadName, message.getClass().getSimpleName(), uuid));
                messageCache.put(uuid, new MessageToClient(message, hexString));
            }

            // even ignored messages get sent to the verifiers
            if (log.isDebugEnabled()) {
                log.debug(String.format("Read message for %s (%d bytes): %s", uuid, BYTES_PER_INTEGER + messageSize, hexString));
                log.debug(String.format("%s Read: message type %s with msgId %s from socket; still need %d response messages.", 
                        threadName, message.getClass().getSimpleName(), messageId, requiredResponseCount-nResponses));
            }
            result.append(hexString);
            result.append("\n");
        }

    } catch (IOException e) {
        // TODO: clear out the socket? We'd need a new "open new socket" checkbox in the UI, and fail-fast when unchecked.
        String msg = result.toString()+"partial:"+toHex(buffer, BYTES_PER_INTEGER + nBytesThisRead);
        log.error(threadName+" throwing read exception; read message so far is:\n"+msg,e);
        throw new ReadException("Unable to read expected result.", e, msg);
    }

    return result.toString();
}