如何从PHP服务器向Visual Basic客户端推送通知?是否需定时同步数据库?
嘿,针对你的两个问题,我来给你梳理清楚可行的方案,不用非得依赖定时同步数据库这种低效的方式!
问题2先明确:不需要定时同步,有主动推送的方法
你完全可以从PHP服务器主动向VB客户端发送通知,和给Android发FCM通知的逻辑类似,只是换了对应的推送机制,不用每隔x分钟去同步数据库查变更。
问题1的具体实现方案
下面是几种靠谱的实现方式,你可以根据自己的场景选择:
方案1:使用WebSocket实现实时双向通信
WebSocket是实现服务器主动推送给客户端的理想方式,支持全双工通信,延迟很低。
PHP服务器端(用Ratchet库实现)
首先安装Ratchet库:
composer require cboden/ratchet
然后写一个简单的WebSocket服务端脚本(比如notification_server.php):
<?php use Ratchet\MessageComponentInterface; use Ratchet\ConnectionInterface; use Ratchet\Server\IoServer; use Ratchet\Http\HttpServer; use Ratchet\WebSocket\WsServer; require __DIR__ . '/vendor/autoload.php'; class NotificationServer implements MessageComponentInterface { protected $clients; public function __construct() { $this->clients = new \SplObjectStorage; } public function onOpen(ConnectionInterface $conn) { // 可以在这里记录客户端的标识(比如用户ID),存到数据库或者内存里 $this->clients->attach($conn); echo "New connection! ({$conn->resourceId})\n"; } public function onMessage(ConnectionInterface $from, $msg) { // 客户端发送的消息,比如注册用户ID // 你可以把$from和用户ID关联起来存储 } public function onClose(ConnectionInterface $conn) { $this->clients->detach($conn); echo "Connection {$conn->resourceId} has disconnected\n"; } public function onError(ConnectionInterface $conn, \Exception $e) { echo "An error has occurred: {$e->getMessage()}\n"; $conn->close(); } // 自定义方法:给指定用户发送通知 public function sendToUser($userId, $message) { foreach ($this->clients as $client) { // 这里需要根据之前存储的用户ID和客户端连接的关联关系判断 // 比如如果当前client对应的是目标userId,就发送消息 $client->send(json_encode($message)); } } } // 启动WebSocket服务,监听8080端口 $server = IoServer::factory( new HttpServer( new WsServer( new NotificationServer() ) ), 8080 ); $server->run();
然后在你现有的PHP业务代码里,当需要发通知给VB客户端时,可以通过HTTP接口触发WebSocket服务的发送逻辑(比如写一个简单的API来调用sendToUser)。
Visual Basic客户端
VB可以使用System.Net.WebSockets.ClientWebSocket来连接WebSocket服务器,接收通知:
Imports System.Net.WebSockets Imports System.Text Public Class NotificationClient Private Async Sub ConnectToWebSocket() Dim client As New ClientWebSocket() Await client.ConnectAsync(New Uri("ws://your-php-server-ip:8080"), CancellationToken.None) Dim buffer As Byte() = New Byte(1023) {} While client.State = WebSocketState.Open Dim result As WebSocketReceiveResult = Await client.ReceiveAsync(New ArraySegment(Of Byte)(buffer), CancellationToken.None) Dim message As String = Encoding.UTF8.GetString(buffer, 0, result.Count) // 接收到通知后,显示Windows通知 ShowNotification(message) End While End Sub Private Sub ShowNotification(message As String) // 这里实现Windows原生通知的显示逻辑 Dim notifyIcon As New NotifyIcon() notifyIcon.Icon = SystemIcons.Information notifyIcon.BalloonTipTitle = "服务器通知" notifyIcon.BalloonTipText = message notifyIcon.Visible = True notifyIcon.ShowBalloonTip(3000) End Sub End Class
方案2:使用Windows Notification Service (WNS)
就像Android用FCM一样,Windows客户端可以用WNS来接收推送通知,PHP服务器直接调用WNS的API发送通知。
步骤1:VB客户端注册WNS令牌
VB客户端需要向WNS注册,获取推送令牌,然后把这个令牌存储到你的MySQL数据库里(和Android的company_token逻辑一致)。
步骤2:PHP服务器调用WNS API发送通知
在你的现有PHP代码里,添加发送WNS通知的函数:
function sendVbNotification($wnsToken) { // 根据通知类型选择WNS端点,这里以Toast通知为例 $url = "https://db5.notify.windows.com/w/?token=" . urlencode($wnsToken); $headers = [ "Content-Type: application/xml", "Authorization: Bearer YOUR_WNS_ACCESS_TOKEN", "X-WNS-Type: wns/toast" ]; $notificationXml = '<?xml version="1.0" encoding="utf-8"?> <toast> <visual> <binding template="ToastText01"> <text id="1">你的服务器通知内容</text> </binding> </visual> </toast>'; $ch = curl_init($url); curl_setopt($ch, CURLOPT_POST, true); curl_setopt($ch, CURLOPT_HTTPHEADER, $headers); curl_setopt($ch, CURLOPT_POSTFIELDS, $notificationXml); curl_setopt($ch, CURLOPT_RETURNTRANSFER, true); $response = curl_exec($ch); curl_close($ch); return $response; }
然后在你的业务代码里,获取VB客户端的WNS令牌(类似getCompanyToken,写个getVbClientToken函数),调用这个方法:
// 原有的Android通知代码 sendNotification($company_token); // 添加VB通知代码 $vbToken = getVbClientToken($user["id"]); sendVbNotification($vbToken);
注意:WNS需要你在微软开发者平台注册应用,获取对应的密钥和包名,才能生成有效的访问令牌。
方案3:长轮询(备选方案)
如果暂时不想用WebSocket或WNS,可以用长轮询:VB客户端向PHP服务器发起请求,服务器保持连接直到有新通知或者超时,超时后客户端再重新发起请求。这种方式比定时轮询高效,但不如WebSocket实时。
PHP服务器端示例
<?php require("connection.php"); $userId = $_GET['user_id']; $lastCheckTime = $_GET['last_check']; // 检查是否有新通知 $query = "SELECT * FROM notifications WHERE user_id = ? AND created_at > ?"; $stmt = mysqli_prepare($conn, $query); mysqli_stmt_bind_param($stmt, "is", $userId, $lastCheckTime); mysqli_stmt_execute($stmt); $result = mysqli_stmt_get_result($stmt); if (mysqli_num_rows($result) > 0) { $notification = mysqli_fetch_assoc($result); echo json_encode($notification); } else { // 等待30秒,期间如果有新通知就返回 sleep(30); // 再次检查 $query = "SELECT * FROM notifications WHERE user_id = ? AND created_at > ?"; $stmt = mysqli_prepare($conn, $query); mysqli_stmt_bind_param($stmt, "is", $userId, $lastCheckTime); mysqli_stmt_execute($stmt); $result = mysqli_stmt_get_result($stmt); echo json_encode(mysqli_fetch_assoc($result) ?: ["status" => "no_notification"]); } mysqli_close($conn);
VB客户端示例
Imports System.Net Imports System.IO Imports Newtonsoft.Json Public Class LongPollClient Private Async Sub StartLongPoll() Dim userId As String = "your-user-id" Dim lastCheckTime As String = DateTime.Now.ToString("Y-m-d H:i:s") While True Dim url As String = $"http://your-php-server/long_poll.php?user_id={userId}&last_check={lastCheckTime}" Dim request As HttpWebRequest = CType(WebRequest.Create(url), HttpWebRequest) request.Method = "GET" Using response As HttpWebResponse = CType(Await request.GetResponseAsync(), HttpWebResponse) Using reader As New StreamReader(response.GetResponseStream()) Dim responseText As String = Await reader.ReadToEndAsync() Dim notification = JsonConvert.DeserializeObject(Of Notification)(responseText) If notification IsNot Nothing AndAlso notification.status <> "no_notification" Then ShowNotification(notification.message) lastCheckTime = DateTime.Now.ToString("Y-m-d H:i:s") End If End Using End Using End While End Sub Private Sub ShowNotification(message As String) // 同之前的通知显示逻辑 Dim notifyIcon As New NotifyIcon() notifyIcon.Icon = SystemIcons.Information notifyIcon.BalloonTipTitle = "服务器通知" notifyIcon.BalloonTipText = message notifyIcon.Visible = True notifyIcon.ShowBalloonTip(3000) End Sub End Class Public Class Notification Public Property status As String Public Property message As String End Class
总结
优先选择WebSocket或者WNS,这两种都是服务器主动推送的方式,比定时同步高效得多。如果你已经熟悉FCM的逻辑,WNS的使用方式和它很类似,都是客户端注册令牌,服务器存储令牌后调用API发送通知。
内容的提问来源于stack exchange,提问作者TonyR81

