【websocket】如何用 curl模拟 websocket 协议请求?

如何用 curl模拟 websocket 协议请求?

要使用 curl 模拟 WebSocket 协议请求,需要注意以下事项:


问题说明

  1. WebSocket 协议是基于 HTTP 升级机制的双向通信协议,而 curl 本身是面向 HTTP/HTTPS 的单次请求-响应工具,默认不支持 WebSocket 的长连接和消息交互
  2. 替代方案
    • 若需完整的 WebSocket 交互(握手 + 消息收发),推荐使用专门工具(如 wscat)。
    • 若仅需模拟 WebSocket 握手阶段(验证连接是否建立),可通过 curl 手动构造 HTTP 升级请求。

方案 1:使用 curl 模拟 WebSocket 握手

以下命令可模拟 WebSocket 的 HTTP 升级请求(仅握手阶段):

curl -i -N \
  -H "Host: your-websocket-server.com" \
  -H "Connection: Upgrade" \
  -H "Upgrade: websocket" \
  -H "Sec-WebSocket-Version: 13" \
  -H "Sec-WebSocket-Key: dGhlIHNhbXBsZSBub25jZQ==" \
  https://your-websocket-server.com/ws-endpoint

参数解释

  • -i:显示响应头信息。
  • -N:禁用缓冲,避免 curl 等待响应体(因 WebSocket 是长连接)。
  • Host:目标服务器域名(某些服务器会校验此字段)。
  • Connection: Upgrade:请求协议升级。
  • Upgrade: websocket:升级到 WebSocket 协议。
  • Sec-WebSocket-Version: 13:指定 WebSocket 协议版本(必须为 13)。
  • Sec-WebSocket-Key:随机 Base64 字符串(客户端生成,服务器需返回 Sec-WebSocket-Accept 校验)。

结果验证

若服务器返回 HTTP/1.1 101 Switching Protocols,表示握手成功(但 curl 无法继续维持连接)。


方案 2:使用专用工具 wscat

wscat 是一个基于 Node.js 的 WebSocket 命令行工具,支持完整的消息交互:

  1. 安装

    npm install -g wscat
    
  2. 发起 WebSocket 请求

    # 明文 WebSocket(ws://)
    wscat -c ws://your-websocket-server.com/ws-endpoint
    
    # 加密 WebSocket(wss://)
    wscat -c wss://your-websocket-server.com/ws-endpoint
    
  3. 交互示例

    Connected to wss://your-websocket-server.com/ws-endpoint
    > Hello, Server!  # 发送消息
    < Hello, Client!  # 接收响应
    

方案 3:使用 Python 脚本

若需编程式测试,可用 Python 的 websockets 库:

  1. 安装库

    pip install websockets
    
  2. 示例代码

    import asyncio
    import websockets
    
    async def test_websocket():
        async with websockets.connect("wss://your-websocket-server.com/ws-endpoint") as ws:
            await ws.send("Hello, Server!")
            response = await ws.recv()
            print(f"Server response: {response}")
    
    asyncio.get_event_loop().run_until_complete(test_websocket())
    

总结

  • 仅需测试握手:使用 curl 手动构造 HTTP 升级请求。
  • 完整交互测试:使用 wscat 或 Python 的 websockets 库。
  • 生产环境:推荐使用客户端 SDK(如 JavaScript 的 WebSocket API)。

用 curl模拟 websocket 协议请求

$ curl  -v  http://172.16.0.5:11241/maco-platform/websocket/client \
  --no-buffer \
  -H 'Connection: keep-alive, Upgrade' \
  -H 'Upgrade: websocket' \
  -H 'Sec-WebSocket-Version: 13' \
  -H 'Sec-WebSocket-Key: TDyiIDMBXScLr5NGpsKHaA=='


* About to connect() to 172.16.0.5 port 11241 (#0)
*   Trying 172.16.0.5...
* Connected to 172.16.0.5 (172.16.0.5) port 11241 (#0)
> GET /maco-platform/websocket/client HTTP/1.1
> User-Agent: curl/7.29.0
> Host: 172.16.0.5:11241
> Accept: */*
> Connection: keep-alive, Upgrade
> Upgrade: websocket
> Sec-WebSocket-Version: 13
> Sec-WebSocket-Key: TDyiIDMBXScLr5NGpsKHaA==
> 
< HTTP/1.1 101 Switching Protocols
< Upgrade: WebSocket
< Connection: Upgrade
< Sec-WebSocket-Accept: PiTH8qQbYEZAX+2Sg0nRWU8hvEc=
* Empty reply from server
* Connection #0 to host 172.16.0.5 left intact
curl: (52) Empty reply from server
$ curl -v  http://10.86.9.20:12613/maco-platform/websocket/client  \
  --no-buffer \
  -H 'Connection: keep-alive, Upgrade' \
  -H 'Upgrade: websocket' \
  -H 'Sec-WebSocket-Version: 13' \
  -H 'Sec-WebSocket-Key: TDyiIDMBXScLr5NGpsKHaA==' 
  
  
*   Trying 10.86.9.20:12613...
* Connected to 10.86.9.20 (10.86.9.20) port 12613 (#0)
> GET /maco-platform/websocket/client HTTP/1.1
> Host: 10.86.9.20:12613
> User-Agent: curl/7.71.1
> Accept: */*
> Connection: keep-alive, Upgrade
> Upgrade: websocket
> Sec-WebSocket-Version: 13
> Sec-WebSocket-Key: TDyiIDMBXScLr5NGpsKHaA==
>
* Mark bundle as not supporting multiuse
< HTTP/1.1 500
< vary: accept-encoding
< Content-Type: text/html;charset=utf-8
< Content-Language: zh-CN
< Content-Length: 5166
< Date: Sat, 24 May 2025 05:14:55 GMT
< Connection: close
<
<!doctype html><html lang="zh"><head><title>HTTP状态 500 - 内部服务器错误</title><style type="text/css">body {font-family:Tahoma,Arial,sans-serif;} h1, h2, h3, b {color:white;background-color:#525D76;} h1 {font-size:22px;} h2 {font-size:16px;} h3 {font-size:14px;} p {font-size:12px;} a {color:black;} .line {height:1px;background-color:#525D76;border:none;}</style></head><body><h1>HTTP状态 500 - 内部服务器错误</h1><hr class="line" /><p><b>类型</b> 异常报告</p><p><b>消息</b> Servlet执行抛出一个异常</p><p><b>描述</b> 服务器遇到一个意外的情况,阻止它完成请求。</p><p><b>例外情况</b></p><pre>javax.servlet.ServletException: Servlet执行抛出一个异常
        com.apusic.aas.websocket.server.WsFilter.doFilter(WsFilter.java:53)
        com.weibaobiao.support.spring.AppLogServiceFilter.doFilter(AppLogServiceFilter.java:54)
        org.apache.shiro.web.servlet.ProxiedFilterChain.doFilter(ProxiedFilterChain.java:61)
        org.apache.shiro.web.servlet.AdviceFilter.executeChain(AdviceFilter.java:108)
        org.apache.shiro.web.servlet.AdviceFilter.doFilterInternal(AdviceFilter.java:137)
        org.apache.shiro.web.servlet.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:154)
        org.apache.shiro.web.servlet.ProxiedFilterChain.doFilter(ProxiedFilterChain.java:66)
        org.apache.shiro.web.servlet.AdviceFilter.executeChain(AdviceFilter.java:108)
        org.apache.shiro.web.servlet.AdviceFilter.doFilterInternal(AdviceFilter.java:137)
        org.apache.shiro.web.servlet.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:154)
        org.apache.shiro.web.servlet.ProxiedFilterChain.doFilter(ProxiedFilterChain.java:66)
        org.apache.shiro.web.servlet.AbstractShiroFilter.executeChain(AbstractShiroFilter.java:458)
        org.apache.shiro.web.servlet.AbstractShiroFilter$1.call(AbstractShiroFilter.java:373)
        org.apache.shiro.subject.support.SubjectCallable.doCall(SubjectCallable.java:90)
        org.apache.shiro.subject.support.SubjectCallable.call(SubjectCallable.java:83)
        org.apache.shiro.subject.support.DelegatingSubject.execute(DelegatingSubject.java:387)
        org.apache.shiro.web.servlet.AbstractShiroFilter.doFilterInternal(AbstractShiroFilter.java:370)
        org.apache.shiro.web.servlet.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:154)
        org.springframework.web.filter.DelegatingFilterProxy.invokeDelegate(DelegatingFilterProxy.java:347)
        org.springframework.web.filter.DelegatingFilterProxy.doFilter(DelegatingFilterProxy.java:263)
        org.springframework.web.filter.CharacterEncodingFilter.doFilterInternal(CharacterEncodingFilter.java:197)
        org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:107)
</pre><p><b>根本原因。</b></p><pre>java.lang.NoClassDefFoundError: org/eclipse/jetty/server/AbstractHttpConnection
        org.eclipse.jetty.websocket.WebSocketFactory.upgrade(WebSocketFactory.java:233)
        org.eclipse.jetty.websocket.WebSocketFactory.acceptWebSocket(WebSocketFactory.java:382)
        org.eclipse.jetty.websocket.WebSocketServlet.service(WebSocketServlet.java:104)
        javax.servlet.http.HttpServlet.service(HttpServlet.java:764)
        com.apusic.aas.websocket.server.WsFilter.doFilter(WsFilter.java:53)
        com.weibaobiao.support.spring.AppLogServiceFilter.doFilter(AppLogServiceFilter.java:54)
        org.apache.shiro.web.servlet.ProxiedFilterChain.doFilter(ProxiedFilterChain.java:61)
        org.apache.shiro.web.servlet.AdviceFilter.executeChain(AdviceFilter.java:108)
        org.apache.shiro.web.servlet.AdviceFilter.doFilterInternal(AdviceFilter.java:137)
        org.apache.shiro.web.servlet.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:154)
        org.apache.shiro.web.servlet.ProxiedFilterChain.doFilter(ProxiedFilterChain.java:66)
        org.apache.shiro.web.servlet.AdviceFilter.executeChain(AdviceFilter.java:108)
        org.apache.shiro.web.servlet.AdviceFilter.doFilterInternal(AdviceFilter.java:137)
        org.apache.shiro.web.servlet.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:154)
        org.apache.shiro.web.servlet.ProxiedFilterChain.doFilter(ProxiedFilterChain.java:66)
        org.apache.shiro.web.servlet.AbstractShiroFilter.executeChain(AbstractShiroFilter.java:458)
        org.apache.shiro.web.servlet.AbstractShiroFilter$1.call(AbstractShiroFilter.java:373)
        org.apache.shiro.subject.support.SubjectCallable.doCall(SubjectCallable.java:90)
        org.apache.shiro.subject.support.SubjectCallable.call(SubjectCallable.java:83)
        org.apache.shiro.subject.support.DelegatingSubject.execute(DelegatingSubject.java:387)
        org.apache.shiro.web.servlet.AbstractShiroFilter.doFilterInternal(AbstractShiroFilter.java:370)
        org.apache.shiro.web.servlet.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:154)
        org.springframework.web.filter.DelegatingFilterProxy.invokeDelegate(DelegatingFilterProxy.java:347)
        org.springframework.web.filter.DelegatingFilterProxy.doFilter(DelegatingFilterProxy.java:263)
        org.springframework.web.filter.CharacterEncodingFilter.doFilterInternal(CharacterEncodingFilter.java:197)
        org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:107)
</pre><p><b>):注意</b> 主要问题的全部 stack 信息可以在 server logs 里查看</p><hr class="line" /></body></html>* Closing connection 0

从你的 curl 请求和服务器返回的 500 错误日志分析,问题出在服务器端依赖缺失java.lang.NoClassDefFoundError: org/eclipse/jetty/server/AbstractHttpConnection),而非客户端请求配置。以下是详细分析和解决方案:


问题原因

  1. 服务器依赖缺失

    • 错误日志显示 AbstractHttpConnection 类未找到,这是 Jetty 服务器旧版本(Jetty 8 或更早)中的类。
    • 服务器使用的 WebSocket 实现(可能是旧版 Jetty 或相关库)依赖 AbstractHttpConnection,但实际部署环境中缺少对应的 Jetty 库。
  2. 版本不兼容

    • 如果服务器升级了 Jetty 到较新版本(如 Jetty 9+),AbstractHttpConnection 已被移除,导致旧代码无法运行。

解决方案

1. 检查服务器依赖

  • 确认服务器使用的 Jetty 版本
    • 若使用 Jetty 8 或更早版本,确保 jetty-server 库已正确部署。
    • 若使用 Jetty 9+,需修改 WebSocket 实现代码,替换对 AbstractHttpConnection 的依赖(参考 Jetty 9 的 WebSocket API)。

2. 修正 curl 命令

你的 curl 请求头部存在两个问题:

  • Connection 头格式错误:应仅包含 Upgrade,而非 keep-alive, Upgrade
  • 未使用 wss:// 协议:如果服务器启用 TLS,需使用 https://wss://

修正后的命令:

curl -v -N \
  http://10.86.9.20:12613/maco-platform/websocket/client \
  --no-buffer \
  -H 'Connection: Upgrade' \
  -H 'Upgrade: websocket' \
  -H 'Sec-WebSocket-Version: 13' \
  -H 'Sec-WebSocket-Key: TDyiIDMBXScLr5NGpsKHaA=='

3. 使用专用工具验证

即使 curl 握手成功,也无法进行后续消息交互。建议使用 wscat 测试完整流程:

wscat -c ws://10.86.9.20:12613/maco-platform/websocket/client

如果服务器依赖问题已修复,wscat 会显示连接成功。

4. 服务器端修复步骤

  1. 检查依赖树

    # Maven 项目
    mvn dependency:tree | grep jetty
    
    # Gradle 项目
    gradle dependencies | grep jetty
    

    确保 jetty-serverjetty-websocket 版本一致且兼容。

  2. 更新或降级 Jetty

    • 降级 Jetty(临时方案):
      <!-- Maven 示例 -->
      <dependency>
          <groupId>org.eclipse.jetty</groupId>
          <artifactId>jetty-server</artifactId>
          <version>8.2.0.v20160908</version>
      </dependency>
      
    • 升级代码(推荐):
      迁移到 Jetty 9+ 的 WebSocket API,删除对 AbstractHttpConnection 的依赖。

验证流程

  1. 本地模拟服务器(快速验证):

    // Jetty 8 示例
    Server server = new Server(8080);
    WebSocketHandler wsHandler = new WebSocketHandler() {
        @Override
        public WebSocket doWebSocketConnect(HttpServletRequest request, String protocol) {
            return new MyWebSocket();
        }
    };
    server.setHandler(wsHandler);
    server.start();
    

    检查本地服务是否抛出相同错误。

  2. 对比测试

    • 使用 wscat 或浏览器 WebSocket 客户端连接,观察是否返回相同错误。

总结

  • 直接原因:服务器缺少 Jetty 旧版本依赖或代码版本不兼容。
  • 修复方向:修正依赖版本或升级服务器 WebSocket 实现。
  • 临时验证:通过修正后的 curlwscat 确认服务可用性。
©著作权归作者所有,转载或内容合作请联系作者
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。

推荐阅读更多精彩内容