Tomcat的WebSocket代码在本地主机上成功,但在托管并修改为远程主机时失败。

huangapple 未分类评论52阅读模式
英文:

Tomcat websocket code succeeds on localhost but fails when hosted and modified for remote host

问题

这个问题以前已经讨论过,但似乎从来没有成功解决过(至少根据我所知)。我正在使用Tomcat 9、Eclipse,当然还有Java和Javascript。

第一章

无论是使用写得很好的javaCodeGeeks Websocket示例,还是为了提供一个最小的框架,将Tomcat示例中的chat/echo代码重新托管到他们神奇的领域之外,我都无法使它们工作。(我将在下面更详细地谈论这个问题。)

我还一直在查看Tomcat日志,并仔细审查那些返回404错误和神秘的7xx错误代码的GET请求,其中一些我已经能够推断出来。(没有关于7xx代码的一般文档。)我将在底部讨论这些问题。

所以问题的关键是,在本地主机上由Tomcat托管时,Websocket代码可以端到端地工作,但当在远程主机上托管时,由"new WebSocket"创建的连接会失败。(这个问题已经在多个论坛上讨论过多次,但更可能的是提问者已经放弃,而不是取得成功并继续前进。)

所以我想知道两件事情。首先,如何使我的示例代码在远程主机上(风雨无阻)工作,其次,我想知道Tomcat示例是如何工作的。我所构思的示例并不是什么新的、可疑的东西;它只是Tomcat示例中的聊天注解。(有一些减少,使它在最小环境中工作,已经注意到这些。特别是日志记录例程和一个从Tomcat中取出的解析HTML类。)

在远程服务器上执行此示例的结果是404错误(以及一些奇怪的7xx错误代码,我将在下面观察并报告这些错误代码)。

如果你想直接查看代码,这里有一个包含源代码的war文件的链接;它们只是从Tomcat示例聊天服务器中采用的,以在最小配置中运行。三个文件,两个Java文件和一个index.html。你可以直接在Tomcat的webapps中运行/部署它,或者将源代码导入Eclipse以进行比较和编辑。

代码示例
文件夹

https://1drv.ms/u/s!AqbqlA2vzkjRcrGjW8rDRLW3o50?e=fgeh0U

这是关键部分:

   var target;
   // 显示问题:除非位置解析为本地主机,否则无法工作
       	target = 'ws://' + window.location.host + 
       '/SocketBugMinimum/websocket/chat';
		//
		// 为了测试目的,
        // 访问Tomcat中的魔法目录下的那个,而不是这一个...
        //target = 'ws://' + window.location.host +      
        //     '/examples/websocket/chat';
        // 还可以尝试:
        //    examples/websocket/echoStreamAnnotation
        // 它也会工作...

当它进入幕后时

    Chat.socket = new WebSocket(target);

它会在控制台上返回错误:
> WebSocket连接到ws:REMOTEHOST:8080/SocketBugMinimum/websocket/chat失败:WebSocket握手期间的错误:意外的响应代码:404

现在有几件事要注意。

1. 当主机是localhost时工作正常。
2. 即使与Tomcat示例WebSocket无关,你也可以连接到远程主机的Tomcat示例WebSocket。这既有趣又有教育意义。
3. 即使你自己托管在localhost上,这也可以工作。

在从Eclipse运行SocketBugMinimun应用程序时,通过Chrome或Firefox从localhost或远程主机访问时,以及从Tomcat的html-manager(即主机...8080:/Manager/html)访问时,都会产生相同的结果。

现在浏览Tomcat日志文件(用今天的日期grep GET以查看所有连接活动),你会在报告的404错误旁边找到一个有趣的数字代码。

正如我之前承诺的,我已经试图推断出日志文件中产生的一些7xx代码的含义;它们似乎修改了404错误。(7xx代码不是HTTP的一部分,根据RFC报告;它们似乎是被循环利用和增强的调制解调器代码。)如果有人有更好的理解,请分享。如果有关这些代码的Tomcat开发者认可的描述,那就太好了。显然,Tomcat的WebSocket会不断产生不同和丰富的错误,但是却没有办法知道它们是什么。(我搜索了Tomcat的文档,Google也没有找到任何内容。)按有用性排序:

727 监听器存在,但终端端点错误
733 端点存在,状态良好,但我不会让你连接,因为我很刻薄
719 没有根元素
746 有效的根,但路径的其余部分有问题
726 你尝试了/a/b,但你需要/a/b/c
737 我不知道
748 没有想法

第二章

现在让我困惑的是Tomcat示例是如何工作的。

它似乎通过直接在示例中托管,赋予了它们一些使用套接字的神奇能力。我试图使用catalina权限重现这些结果,但没有成功。(如果你是Tomcat权限的鉴赏家

英文:

This has been discussed before, but never successfully (as far as I can tell) resolved. I'm using Tomcat 9, eclipse, and of course Java and Javascript.

Chapter 1

I can make work neither the well-written javaCodeGeeks websocket example, or for the purposes of providing a minimal framework, re-hosting the Tomcat example chat/echo code outside of their magic realm. (I'll have more to say about this below.)

I've also been poring over the Tomcat logs and scrutinizing the GET requests which return 404 errors and mystery 7xx error codes, some of which I've been able to infer. (There is no general documentation of the 7xx codes.) I'll discuss those at the bottom.

So the nub of the issue is that Websocket code works end-to-end when hosted by Tomcat on localhost, but when hosted on a remote host, the connection created by "new WebSocket" fails. (This issue has been discussed several times, in multiple forums, but it seems more likely that the questioners have given up rather than succeeded and moved on.)

So I would like to know two things. First, how to make my example code work when hosted remotely (any port in a storm) and second, I would like to know how the tomcat examples work at all. The sample I've concocted is not something new and suspect; it is simply the chat annotation from the Tomcat examples. (There are a few reductions so that it works in a minimal environment, and those are noted. In particular the logging routines, and a parse-html class was pulled from its home on Tomcat.)

The result of this sample when executed against a remote server is a 404 error (with some curious 7xx error codes, which I observe and report on below).

If you want to cut to the code, here is a link to a war file that includes sources; They are just adopted from the Tomcat sample chat server to run in a minimum configuration. Three files, 2 java and one index.html. You can run/deploy it directly on tomcat in webapps or pull the sources into eclipse to compare and edit.

SampleCode
Dossier

https://1drv.ms/u/s!AqbqlA2vzkjRcrGjW8rDRLW3o50?e=fgeh0U

Here is the point:

   var target;
   // Shows the bug: unless location resolves to local host won't work
       	target = 'ws://' + window.location.host + 
       '/SocketBugMinimum/websocket/chat';
		//
		// For testing purposes,
        // access the tomcat one in the magic directory, not this one...
        //target = 'ws://' + window.location.host +      
        //     '/examples/websocket/chat';
        // also try:
        //    examples/websocket/echoStreamAnnotation
        // it will work too...

And when it goes behind the curtain

    Chat.socket = new WebSocket(target);

it returns with the error on the console:
> WebSocket connection to ws:REMOTEHOST:8080/SocketBugMinimum/websocket/chat failed: Error during WebSocket handshake: Unexpected response code: 404

Now a couple of things to note.

1. Works fine when host is localhost.
2. You can connect to remote host accessing the Tomcat examples websocket, even though they have nothing to do with this code. This is both fun and instructive.
3. This works even when you are yourself hosted on localhost.

The SocketBugMinimun app produces the same result run from eclipse, accessing localhost or remotehost from either chrome or firefox, and from the tomcat html-manager (i.e. host...8080:/Manager/html).

Now looking through the Tomcat logfile (grep the ones with today's date for GET to see all the connect activity) you will find an interesting numeric code beside the reported 404 error.

As I promised above, I've tried to infer meanings of some of the 7xx codes produced in the logfile; they seem to modify the 404 error. (7xx codes are not part of HTTP as reported in an RFC; they seem to be recycled and augmented Modem codes, of all things.) If anyone has a better understanding, please share it. Tomcat-developer endorsed description of these codes it would be great to know. apparently tomcat websockets busily produce different and rich errors, but there is just no way to know what they are. (I've scoured the tomcat docs, and Google finds nothing.) In order of usefulness:

727 Listener exists but terminal endpoint is bad
733 Endpoint exists and is hunky dory but I won't let you connect to it because I'm mean
719 No root element
746 Valid root, but the rest of the path is bad
726 You tried /a/b but you need /a/b/c
737 Beats me
748 No idea

Chapter 2

Now the mystery to me is how the Tomcat examples work at all.

It appears that by being hosted in the examples directly they seem to have conferred on them some magical powers to use sockets. I've tried to reproduce those results using catalina privileges but am unsuccessful. (And if you are a connoisseur of Tomcat privileges you will note that the sample files says "this is what you should do to generate these effects" but those lines are COMMENTED OUT and the examples work anyway!)

I also experimented with Valves, but have nothing useful to report. If it wouldn't kill the Tomcat people to provide a few more documented examples, it would be helpful.

There is also a voodoo file in Tomcat called servers.json (you will note that no other files with a .json extension are shipped with the product). This file and its curious and undocument incantations change the names of the network pathname to the endpoints. (This file is why I chose to repro this with chat, which uses no other magic, rather than echo.)

One likely solution might be that the examples are conferred special powers in catalina.policy. But no dice. I tried to add WebSocket privs, and it doesn't do any good. Here is the diff between the original and what I added.

 // ++
< // everyone needs sockets!
< grant {
<    permission java.net.SocketPermission "*:8080", "accept,connect,listen,resolve";
< };
< 
< // Granting permission for Spades to be reached via websocket
< grant codeBase "war:file:${catalina.base}/webapps/Spades.war*/-" {
<    permission java.net.SocketPermission "*:8080", "accept,connect,listen,resolve";
<    permission java.lang.RuntimePermission "accessClassInPackage.org.apache.tomcat.websocket";
<    permission java.lang.RuntimePermission "accessClassInPackage.org.apache.tomcat.websocket.server";
<    permission java.sercurity.AllPermission;
< };
< //--
< 
275c260
< // };

As I said, it didn't seem to do any good. Same sad errors in the logfiles.

Thanks in advance for your help.
Marklan

答案1

得分: 0

问题已解决。

我进行了更多的调查,了解到在运行较旧版本的Java时,Tomcat结果存在差异。Coderanch有这篇有用的文章:

https://coderanch.com/t/659638/application-servers/WebSocket-deploying-Server
我检查后发现我的服务器上安装了Java 1.8。 (哎呀!)

感谢ServerWorld的有用网站,这里提供了关于升级Java、查找所需版本等有用信息。以下是英文版本的链接,您可以从那里选择操作系统配置等。

https://www.server-world.info/en/
因此,我升级到了11 JDK版本(即从oracle.com下载jdk-11.0.1_linux-x64_bin.rpm),并运行以下命令以安装,即从下载目录:

rpm -Uvh jdk-11*
以安装。
最后,在不删除旧版本的情况下进行配置(以防万一这不起作用!)

alternatives --config java
以选择并安装最新版本。重新启动Tomcat,一切正常运行。
感谢与我一起查看此问题并为此头疼的人们。我感谢他们的努力。

愉快的编码。希望这次的冒险能对其他遇到相同问题的人有所帮助。

如果Tomcat团队能够记录这些7xx错误,那将是非常好的。我不知道为什么较旧版本的Java不起作用。我无法想象为什么这种情况会与版本有关,但我想我的想象力并不那么好...

我找到了一些其他有用的信息要分享。特别是使示例文件正常工作的神奇Cookie 藏在对ServerApplicationConfig的调用中,它实质上是导出和部署子目录中的端点。查看位于webapps/WEB-INF/classes/websocket中的文件ExamplesConfig.java。它利用了Tomcat的钩子,为您扫描子目录并导出端点,或根据需要拒绝导出它们。

英文:

Problem solved.

I did some more snooping around and learned that there are discrepancies in Tomcat results when you are running older versions of Java. Coderanch had this helpful article:

    https://coderanch.com/t/659638/application-servers/WebSocket-deploying-Server

I checked and realized I had Java 1.8 on my server. (Dooh!)

Thanks to ServerWorld for its helpful site, here is helpful information on upgrading Java, finding the version you need, etc. Here is a link to the English version, from there you can select OS config, etc.

    https://www.server-world.info/en/

So, I upgraded to version 11 JDK (i.e. downloading jdk-11.0.1_linux-x64_bin.rpm from oracle.com) running rpm to install, i.e. from the download directory

    rpm -Uvh jdk-11*

to install.
Finally configuring away from the old version without deleting it (in case this doesn't work!)

    alternatives --config java

to select and install the latest. Restarted Tomcat and it all worked.
Thanks to the people who looked at this with me, and scratched their heads. I appreciated the efforts.

Happy coding. Hopefully this mis-adventure can be helpful to anyone else who has the same problem.

And it would sure be nice if the Tomcat team could document the 7xx errors. I have no idea why the older versions of Java did not work. I can't imagine how something like this would be version dependent, but I guess my imagination is just not that good...

I did find some other useful stuff that I want to share. In particular the magic cookie that makes the examples files work is buried in a call to ServerApplicationConfig, wheich essentially exports and deploys the endpoints in subdirectories. Check out the file ExamplesConfig.java buried off in webapps/WEB-INF/classes/websocket. It takes advantage of a Tomcat hook that scans your subdirectories for you, and exports endpoints, or declines to export them as you wish.

M

huangapple
  • 本文由 发表于 2020年4月5日 03:05:59
  • 转载请务必保留本文链接:https://java.coder-hub.com/61033359.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定