這個異常怎麼來的?
大致看了下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完了,再斷開不會拋出這個異常。