项目在启动后,无缘无故报错,大意就是说tomcat接收到了不合法的字符,在解析http请求的方法时出错了,完整报错如下:
o.apache.coyote.http11.Http11Processor : Error parsing HTTP request header Note: further occurrences of HTTP header parsing errors will be logged at DEBUG level. java.lang.IllegalArgumentException: Invalid character found in method name. HTTP method names must be tokens at org.apache.coyote.http11.Http11InputBuffer.parseRequestLine(Http11InputBuffer.java:430) ~[tomcat-embed-core-8.5.43.jar:8.5.43] at org.apache.coyote.http11.Http11Processor.service(Http11Processor.java:684) ~[tomcat-embed-core-8.5.43.jar:8.5.43] at org.apache.coyote.AbstractProcessorLight.process(AbstractProcessorLight.java:66) [tomcat-embed-core-8.5.43.jar:8.5.43] at org.apache.coyote.AbstractProtocol$ConnectionHandler.process(AbstractProtocol.java:808) [tomcat-embed-core-8.5.43.jar:8.5.43] at org.apache.tomcat.util.net.NioEndpoint$SocketProcessor.doRun(NioEndpoint.java:1498) [tomcat-embed-core-8.5.43.jar:8.5.43] at org.apache.tomcat.util.net.SocketProcessorBase.run(SocketProcessorBase.java:49) [tomcat-embed-core-8.5.43.jar:8.5.43] at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1142) [na:1.8.0_102] at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:617) [na:1.8.0_102] at org.apache.tomcat.util.threads.TaskThread$WrappingRunnable.run(TaskThread.java:61) [tomcat-embed-core-8.5.43.jar:8.5.43] at java.lang.Thread.run(Thread.java:745) [na:1.8.0_102]
处理方法:
换端口,目前已知普通用户使用8888端口会报错,可能与常用的内网,或者某些常用软件通信有关。
原因分析:
既然是tomcat收到不合法的字符,所以我写了一个简单的 nio socket 服务器,想看看究竟收到了什么,nio socket 服务器代码如下,如果使用 ServerSocket 则接收不到,所以这个报错应该是出现在支持nio的tomcat服务器上。
import java.io.IOException;
import java.net.InetSocketAddress;
import java.nio.ByteBuffer;
import java.nio.channels.SelectionKey;
import java.nio.channels.Selector;
import java.nio.channels.ServerSocketChannel;
import java.nio.channels.SocketChannel;
import java.nio.charset.Charset;
import java.util.Iterator;
import java.util.Set;
public class NioServer {
public static void main(String[] args) throws IOException {
ServerSocketChannel serverSocketChannel = ServerSocketChannel.open();
serverSocketChannel.bind(new InetSocketAddress(8888));
serverSocketChannel.configureBlocking(false);
Selector selector = Selector.open();
serverSocketChannel.register(selector, SelectionKey.OP_ACCEPT);
while (true) {
// 注册完之后Selector就可以通过select方法来等等请求,
// select方法有一个long类型的参数,代表最长等待时间,
// 如果在这段时间里接收到了相应操作的请求则返回可以处理
// 的请求的数量,否则在超时后返回0
// // eg: 如果没接收到请求,10000毫秒后回返回0
// int num = selector.select(10000L);
int num = selector.select(); // 没有超时时间
// System.out.println("selector获取到" + num + "个channel");
if (num == 0) continue;
Set<SelectionKey> selectionKeys = selector.selectedKeys();
Iterator<SelectionKey> iterator = selectionKeys.iterator();
while (iterator.hasNext()) {
SelectionKey selectionKey = iterator.next();
iterator.remove();
boolean connectable = selectionKey.isConnectable();
boolean valid = selectionKey.isValid();
boolean acceptable = selectionKey.isAcceptable();
boolean readable = selectionKey.isReadable();
boolean writable = selectionKey.isWritable();
// System.out.println(connectable ? "connectable" : "");
// System.out.println(valid ? "valid" : "");
// System.out.println(acceptable ? "acceptable" : "");
// System.out.println(readable ? "readable" : "");
// System.out.println(writable ? "writable" : "");
if (acceptable) {
System.out.println("连接成功!");
SocketChannel socketChannel = serverSocketChannel.accept();
socketChannel.configureBlocking(false);
ByteBuffer byteBuffer = Charset.forName("UTF-8").encode("你已经成功连接服务器!");
socketChannel.write(byteBuffer);
socketChannel.register(selector, SelectionKey.OP_READ);
}
if (readable) {
SocketChannel socketChannel = (SocketChannel) selectionKey.channel();
socketChannel.configureBlocking(false);
try {
ByteBuffer buffer = ByteBuffer.allocate(1024);
// 写入buffer
socketChannel.read(buffer);
// 从buffer读取并转换为char
buffer.flip();
System.out.println(Charset.defaultCharset().decode(buffer));
} catch (Exception e) {
}
socketChannel.register(selector, SelectionKey.OP_WRITE);
}
if (writable) {
SocketChannel socketChannel = (SocketChannel) selectionKey.channel();
socketChannel.configureBlocking(false);
try {
socketChannel.write(Charset.defaultCharset().encode("server已经收到你的信息。"));
System.out.println("server write");
} catch (Exception e) {
}
socketChannel.register(selector, SelectionKey.OP_READ);
}
}
}
}
}
启动之后,每隔几秒就会收到如下字符:

这很明显不是http请求的格式,tomcat解析当然会出错。
然后我在浏览器请求了localhost:8888/test123,则会打印处http请求的内容,如下:

验证:
之后,我将 nio server 的端口修改为8000,重启后,等待一段时间,并未收到任何信息。由此说明,在网络中,有设备在向我电脑的8888端口发送信息,而且这些信息比较短,则很可能是检测心跳的信息。
而我把项目的端口换为8000之后,也不会出现这个错误了。