PHP转发Webhook至VB.Net TCP监听器的问题咨询
问题:Webhook转发时的HTTP头传递与TCP监听器数据流读取问题
我需要在r1节点接收Webhook后,转发到不同端口的r2目标地址。目前在r1部署了PHP脚本,但脚本无法保留并传递原始HTTP头,只能生成新请求头。现有PHP脚本能触发r2端的VB.Net TCP监听器,但监听器的ReadDataFromClient函数触发后,读取到的流数据为空。
已验证将Webhook转发至webhook.site可正常接收完整载荷,说明转发逻辑基础可行;尝试用HTTP监听器未收到数据,因此倾向继续使用TCP监听器。后续尝试通过getallheaders()获取并传递所有原始HTTP头,但转发失败,webhook.site未收到任何数据。现在希望仅传递4个特定HTTP头,并解决VB.Net TCP监听器无法读取流数据的问题。
具体问题:
- 如何将r1接收的原始HTTP头(仅指定4个)传递至r2?
- 为何r2的TCP监听器无法正常读取流数据,该如何修正?
一、传递指定HTTP头到r2的PHP实现
不要直接依赖getallheaders()(部分环境如FastCGI下兼容性差),改用$_SERVER数组提取指定头,同时必须构建完整的HTTP请求格式(请求行+头+空行+请求体),因为TCP监听器接收的是原始字节流,而非解析后的HTTP请求。
示例代码:
<?php // 读取原始请求体 $requestBody = file_get_contents('php://input'); // 定义需要传递的4个目标HTTP头 $targetHeaders = [ 'X-Webhook-Signature', 'Content-Type', 'X-Event-Type', 'X-Timestamp' ]; // 构建转发用的请求头数组 $forwardHeaders = []; foreach ($targetHeaders as $header) { // 转换为$_SERVER兼容的键名格式 $serverKey = 'HTTP_' . strtoupper(str_replace('-', '_', $header)); if (isset($_SERVER[$serverKey])) { $forwardHeaders[$header] = $_SERVER[$serverKey]; } } // 配置r2的连接信息 $r2Host = 'r2的IP地址'; $r2Port = 你的目标端口; // 建立TCP连接 $socket = fsockopen($r2Host, $r2Port, $errno, $errstr, 30); if (!$socket) { die("连接失败: $errno - $errstr"); } // 构建完整HTTP请求 $requestMethod = $_SERVER['REQUEST_METHOD']; $requestUri = $_SERVER['REQUEST_URI']; // 请求行 $requestLine = "$requestMethod $requestUri HTTP/1.1\r\n"; // 拼接头信息 $headerLines = []; foreach ($forwardHeaders as $name => $value) { $headerLines[] = "$name: $value\r\n"; } // 必须添加Content-Length,告知TCP端请求体长度 $headerLines[] = "Content-Length: " . strlen($requestBody) . "\r\n"; // 头与体之间用空行分隔 $headerLines[] = "\r\n"; // 拼接完整请求数据并发送 $fullRequest = $requestLine . implode('', $headerLines) . $requestBody; fwrite($socket, $fullRequest); // 可选:读取r2响应 $response = fread($socket, 8192); echo $response; // 关闭连接 fclose($socket); ?>
二、VB.Net TCP监听器读取流数据的修正
TCP监听器读取空数据的核心原因通常是:PHP端HTTP请求格式不完整,或VB端读取流的逻辑错误(提前关闭流、未按长度读取)。以下是修正后的实现:
Imports System.Net Imports System.Net.Sockets Imports System.Text Module TCPListenerModule Private listener As TcpListener Sub Main() ' 绑定r2的监听地址和端口 Dim ipAddr As IPAddress = IPAddress.Parse("0.0.0.0") listener = New TcpListener(ipAddr, 你的监听端口) listener.Start() Console.WriteLine("TCP监听器已启动,等待连接...") ' 异步接受新连接 listener.BeginAcceptTcpClient(AddressOf OnClientConnected, Nothing) Console.ReadLine() listener.Stop() End Sub Private Sub OnClientConnected(ByVal ar As IAsyncResult) Dim client As TcpClient = listener.EndAcceptTcpClient(ar) Console.WriteLine("新客户端连接") ' 继续监听下一个连接 listener.BeginAcceptTcpClient(AddressOf OnClientConnected, Nothing) ' 读取客户端数据 ReadDataFromClient(client) End Sub Private Sub ReadDataFromClient(ByVal client As TcpClient) Dim stream As NetworkStream = client.GetStream() Dim buffer(4096) As Byte Dim totalBytesRead As Integer = 0 Dim requestData As New StringBuilder() Try ' 先读取HTTP头,直到遇到头结束的空行(\r\n\r\n) While True Dim bytesRead As Integer = stream.Read(buffer, 0, buffer.Length) If bytesRead = 0 Then Exit While totalBytesRead += bytesRead requestData.Append(Encoding.UTF8.GetString(buffer, 0, bytesRead)) ' 检查是否读取到完整的HTTP头 If requestData.ToString().Contains(vbCrLf & vbCrLf) Then Exit While End If End If ' 解析Content-Length,获取请求体长度 Dim contentLength As Integer = 0 Dim headerEndIndex As Integer = requestData.ToString().IndexOf(vbCrLf & vbCrLf) Dim headers As String = requestData.ToString().Substring(0, headerEndIndex) For Each headerLine As String In headers.Split(vbCrLf) If headerLine.StartsWith("Content-Length:", StringComparison.OrdinalIgnoreCase) Then Integer.TryParse(headerLine.Split(":")(1).Trim(), contentLength) Exit For End If Next ' 读取剩余的请求体数据 If contentLength > 0 Then Dim remainingBytes As Integer = contentLength - (totalBytesRead - (headerEndIndex + 4)) While remainingBytes > 0 Dim bytesRead As Integer = stream.Read(buffer, 0, Math.Min(buffer.Length, remainingBytes)) If bytesRead = 0 Then Exit While requestData.Append(Encoding.UTF8.GetString(buffer, 0, bytesRead)) remainingBytes -= bytesRead End If End If ' 输出完整请求内容 Console.WriteLine("接收到的完整请求:") Console.WriteLine(requestData.ToString()) Catch ex As Exception Console.WriteLine("读取数据出错:" & ex.Message) Finally stream.Close() client.Close() End Try End Sub End Module
关键修正点:
- 先读取HTTP头直到识别到
\r\n\r\n,再根据Content-Length读取对应长度的请求体,避免提前中断读取 - 循环读取缓冲区,确保获取完整的请求数据
- 读取完成后再关闭流和客户端连接,避免数据丢失
内容的提问来源于stack exchange,提问作者Tym
相关产品推荐
相关产品推荐

