这个异常怎么来的?
大致看了下tomcat源码,是web容器里抛出的一个异常。
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.apache.catalina.connector;
import java.io.IOException;
/**
* Wrap an IOException identifying it as being caused by an abort
* of a request by a remote client.
*
* @author Glenn L. Nielsen
*/
public final class ClientAbortException extends IOException {
private static final long serialVersionUID = 1L;
//------------------------------------------------------------ Constructors
/**
* Construct a new ClientAbortException with no other information.
*/
public ClientAbortException() {
super();
}
/**
* Construct a new ClientAbortException for the specified message.
*
* @param message Message describing this exception
*/
public ClientAbortException(String message) {
super(message);
}
/**
* Construct a new ClientAbortException for the specified throwable.
*
* @param throwable Throwable that caused this exception
*/
public ClientAbortException(Throwable throwable) {
super(throwable);
}
/**
* Construct a new ClientAbortException for the specified message
* and throwable.
*
* @param message Message describing this exception
* @param throwable Throwable that caused this exception
*/
public ClientAbortException(String message, Throwable throwable) {
super(message, throwable);
}
}
这个是tomcat源码中的一段,定义了这个异常。最开始的注释里写到,当远程客户端关闭时,tomcat服务端会抛出这个异常。
在java里,使用socket编程,底层是tcp协议,通信的双方需要tcp三次握手,建立连接后才能通信。如果服务端在读取客户端输入或者向客户端输出字节时,远程客户端主动断开了连接,那么根据socket接口,服务端会抛出一个IOException。tomcat里针对这种场景下抛出的IOException,做了一层专门的异常封装,就是上面的ClientAbortException。
看下抛出异常的部分:
@Override
public int realReadBytes() throws IOException {
if (closed) {
return -1;
}
if (coyoteRequest == null) {
return -1;
}
if (state == INITIAL_STATE) {
state = BYTE_STATE;
}
try {
return coyoteRequest.doRead(this);
} catch (IOException ioe) {
// An IOException on a read is almost always due to
// the remote client aborting the request.
throw new ClientAbortException(ioe);
}
}
读取字节。
public void realWriteBytes(ByteBuffer buf) throws IOException {
if (closed) {
return;
}
if (coyoteResponse == null) {
return;
}
// If we really have something to write
if (buf.remaining() > 0) {
// real write to the adapter
try {
coyoteResponse.doWrite(buf);
} catch (CloseNowException e) {
// Catch this sub-class as it requires specific handling.
// Examples where this exception is thrown:
// - HTTP/2 stream timeout
// Prevent further output for this response
closed = true;
throw e;
} catch (IOException e) {
// An IOException on a write is almost always due to
// the remote client aborting the request. Wrap this
// so that it can be handled better by the error dispatcher.
throw new ClientAbortException(e);
}
}
}
写入字节。
另外,抛出这个异常时,一般是高并发场景,需要客户端主动断开的时机恰好是服务端读取或者写入时,如果已经io完了,再断开不会抛出这个异常。