在以前的項目中,就曾聽說過swoole的大名,想用來進行消息推送,但是當時只是有了初步的了解,並不敢大膽的運用到線上產品。所謂 識不足則多慮,威不足則多怒、所以就是怕,只能跟領導說了運用極光的推送功能,而且還說出了一定的理由,領導自然也只有相信了,那就用極光推送吧!
最近閑來無事,又重新溫習了一下swoole的消息通知功能,雖然在項目開發當時,也曾實現過簡單的操作的,但是這次溫習更加深了學習,配合vb.net進行開發,感覺還是棒棒噠,下面是一套實現過程,如有需要參考,請拿去,不謝!
首先,我的開發平台是windows,雖然本人也裝有virtual box虛擬機,但是那玩意兒太耗內存cpu了,開起來搞實在不想搞。所以,就用了cygwin進行配置開發環境搭建,這里我就不說具體安裝步驟了,給個鏈接吧,一步步按着操作就行了。 查看php在window下安裝swoole擴展的方法 請點擊。 ps: 如果你win系統里已經安裝了一套wamp環境,在cygwin中使用php或其他和win有相同名稱命令時,請記得使用全路徑 如:/bin/php index.php , /bin/find / -name "init*"
第二步、編寫php服務端程序,命名 test_tcp_server.php , 代碼如下:
<?php $serv = new swoole_server("127.0.0.1", 9501); $serv->set(array( 'worker_num' => 2, //工作進程數量 // 'daemonize' => true, //是否作為守護進程,玩玩就不要開啟這東西了,不然你都不知道跑到哪里去了 )); $serv->on('connect', 'my_onConnect'); $serv->on('receive', 'my_onReceive'); $serv->on('close', 'my_onClose'); echo "swoole_server is running on 127.0.0.1 9501 \n"; $serv->start(); //receive msg respond function my_onReceive ($serv, $fd, $from_id, $data) { // var_dump($serv); $data = trim($data); // 發送給誰, 如:@id msg... , 最好的當然是使用redis或者數據庫存入號碼並生成一個映射了,不過玩玩就不要認真了 if(substr($data, 0, 1) == '@'){ $splitStart = strpos($data, ' '); $toUserId = substr($data, 1, $splitStart - 1); echo 'Send to:' . $toUserId . ';'; $content = substr($data, $splitStart + 1); if(is_numeric($toUserId)){ $serv->send($toUserId, "From Client[$fd@$from_id]:" . $content); }elseif(strtolower($toUserId) == 'all'){ broadcast($serv, $fd, $content); }else{ $serv->send($fd, 'Swoole: Unkown to user ' . strtolower($toUserId) . ' infomation.'); } }else{ echo "From Client[$fd@$from_id],received data: " . $data . PHP_EOL; $serv->send($fd, 'Swoole: '.$data); } // $serv->close($fd); } //connect action function my_onConnect ($serv, $fd){ echo "Client:Connect. id:{$fd}\n"; } //close action function my_onClose ($serv, $fd) { echo "Client: Close. id:{$fd}\n"; } //broadcast except me, 只會廣播from_id相同的用戶,再研究了 function broadcast(swoole_server $serv, $fd = 0, $data = "hello") { $start_fd = 0; $sendNum = 0; echo "broadcast to "; $from = "From Client[$fd]:"; while(true) { $conn_list = $serv->connection_list($start_fd, 10); if($conn_list === false) { break; } var_dump($conn_list); $start_fd = end($conn_list); foreach($conn_list as $conn) { if($conn === $fd) continue; $ret1 = $serv->send($conn, $from . $data); $sendNum ++; } } echo $sendNum . ' numbers.'; }
記得把它跑起來哦
/bin/php test_tcp_server.php
第三步,你可以編寫一個客戶端程序,也可以不用編寫,因為其他工具也完全可以連接進行的呢!如果要定,可以參考swoole官網的tcp_client代碼,如下:
<?php $client = new swoole_client(SWOOLE_SOCK_TCP, SWOOLE_SOCK_ASYNC); //設置事件回調函數 $client->on("connect", function($cli) { $cli->send("hello world\n"); }); $client->on("receive", function($cli, $data){ echo "Received: ".$data."\n"; }); $client->on("error", function($cli){ echo "Connect failed\n"; }); $client->on("close", function($cli){ echo "Connection close\n"; }); //發起網絡連接 $client->connect('127.0.0.1', 9501, 0.5);
正常的話,運行這個腳本,你就可以收到消息了如: 'From Client[1@0],received data: hello,world'
第四步、你肯定不滿足於使用php語言去連接服務器端,你當然不應該滿足!你還可以使用windows的cmd命令行嘛,如下:
telnet 127.0.0.1 9501
然后,你會發現,怎么只輸入了一個字符就發送了。原因是什么win的telnet協議之類的,使用send命令,就可以了。操作為:按鍵 ctrl + ] ,進入telnet 命令行模式,輸入send aaaddd
ok,多開幾個客戶端,看起來會話就可以了呢(要查看返回的內容,直接按Enter鍵即可,切換回來使用ctrl + ]),來看看服務端都什么樣子吧:
都是有響應的吧,ok了,現在再試試一個功能, send @10 hello,10, you ok ? ,則另一個客戶端就收到消息了
ok,到此為止,基本上測試完成了。但是使用cmd畢竟看起來不爽,於是想到用vb.net去實現一個客戶端。代碼就簡要的貼出來,看看吧。
第五步,使用vb.net實現客戶端訪問:
Imports System Imports System.Net Imports System.Net.Sockets Imports System.Text Public Class SynchronousSocketClient Protected Shared port As Integer = 9501 Protected Shared host As String = "localhost" Private Shared sender As Socket Public Shared bytes(1024) As Byte ' A way to the connect to the server Public Shared Sub Main() If (IsNothing(sender)) OrElse Not sender.Connected Then If Not IsNothing(Form1.TextBox1.Text) Then host = Form1.TextBox1.Text End If If (Form1.TextBox2.Text) Then port = Form1.TextBox2.Text End If sender = ConnectSocket(host, port) End If End Sub ' send something to server Public Shared Sub send() If (IsNothing(sender)) OrElse Not sender.Connected Then If Not IsNothing(Form1.TextBox1.Text) Then host = Form1.TextBox1.Text End If If (Form1.TextBox2.Text) Then port = Form1.TextBox2.Text End If sender = ConnectSocket(host, port) End If ' Encode the data string into a byte array. Dim msg As Byte() = Encoding.ASCII.GetBytes(Form1.myContent.Text) ' Send the data through the socket. Dim bytesSent As Integer = sender.Send(msg) ' Receive the response from the remote device. Dim bytesRec As Integer = sender.Receive(bytes) Console.WriteLine("ServerReturn = {0}", Encoding.ASCII.GetString(bytes, 0, bytesRec)) Form1.infoText.Text &= "ServerReturn = " & Encoding.ASCII.GetString(bytes, 0, bytesRec) & vbNewLine End Sub ' connect initialize Private Shared Function ConnectSocket(ByVal host As String, ByVal port As Integer) As Socket '創建連接 Dim s As Socket = Nothing Dim hostEntry As IPHostEntry = Nothing 'address = System.Net.IPAddress.Parse(host) Dim ipHostInfo As IPHostEntry = Dns.Resolve(host) Dim address As IPAddress = ipHostInfo.AddressList(0) Dim endPoint As New IPEndPoint(address, port) Dim tempSocket As New Socket(endPoint.AddressFamily, SocketType.Stream, ProtocolType.Tcp) tempSocket.Connect(endPoint) If tempSocket.Connected Then s = tempSocket Console.WriteLine("Socket connected to {0}", tempSocket.RemoteEndPoint.ToString()) Form1.infoText.Text &= "Socket connected to " & tempSocket.RemoteEndPoint.ToString() End If Return s End Function ' close socket Public Shared Sub closeSocket() If (sender.Connected) Then Form1.infoText.Text &= "Socket closed : " & sender.RemoteEndPoint.ToString() & vbNewLine sender.Shutdown(SocketShutdown.Both) sender.Close() End If End Sub End Class 'SynchronousSocketClient
如上是訪問連接服務器的代碼,觸發代碼則是在另一個頁面進行的,界面如下:
運行后效果如下:
觸發事件的代碼就比較簡單了,還是貼一下吧:
Public Class Form1 Private Sub Button1_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Button1.Click 'Console.WriteLine("console ", Rnd) ' 無法讀取最后一行console其實 SynchronousSocketClient.Main() 'AsynchronousClient.Main() ' 異步功能並未實現呢 End Sub Private Sub Form1_Load(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles MyBase.Load End Sub Private Sub Button2_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Button2.Click infoText.Text = "" End Sub Private Sub Button3_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Button3.Click SynchronousSocketClient.send() End Sub Private Sub Button4_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Button4.Click SynchronousSocketClient.closeSocket() End Sub End Class
到此,打完,收工。由於異步的調試沒有成功,就此打住吧,以后有興趣再去搞了!