SignalR 有三塊,
Server:
Hub Server
Custom Message Sender Service (Sender Proxy)
Client:
Message Sender
Message Receiver
1. Hub Server & Sender Proxy Service
Hub Server 和 Custom Message Sender Service 在一個app中,“Custom Message Sender Service” 通過api直接控制Hub Server行為。(connection filtering, status, deliver message, etc )
2. 跨域
Sender, Receiver 和 Hub Server 可能都不在同一個域里。 會有跨域訪問的問題。
3. 發消息的客戶端,
可以有兩種方式發消息給Hub Server
3.1 通過hubproxy.server.func(), 走的是signalR的persistent connection. (這種情形下一般只發一次消息,persistent connection占用了資源,浪費。 當然也可以建立完釋放,persistent connection -> send message -> close persistent connection)。 這種模式下,跨域由SignalR Hub框架控制。
3.2 通過ajax調用 "Custom Message Sender Service (Proxy)", 由Proxy調用Hub的服務。 這種結構更干凈,發送者和SignalR connection沒有關系, 走單獨Http請求。 跨域由 "Custom Message Sender Service (Proxy)", 一般由服務端暴露服務(MVC/WebAPI). 但是MVC和WebApi的跨域管理不太一樣。(見“MVC和WebApi的跨域管理”)
3.3 有一種情況可以考慮用signalR的persistent connection發消息。 就是發消息的人,同時又是收消息的人。 那么一個persistent connection可以同時用來收發消息。
4. 收消息的客戶端
這個只有一種,“持久連接,保持監聽”。
4.1 SignalR保持連接的方式由協議本身定義,由服務器和瀏覽器支持。 WebSocket / iFrame / longPooling。 具體可google
4.2 WebSocket的效率要比其他高很多,比較reliable,比如disconnect, 其他方式可能不能直接反應客戶端關閉的動作,需要等待超時發生觸發disconnect. 代碼和排錯上有時比較費解。
4.3 【Hub Server服務端】微軟的WebApp PaaS上, by default, "WebSocket"不enable。要去管理界面把它打開。
5. Hub Server
5.1 Owin中map /signalr 作為SignalR routing的開始
(https://docs.microsoft.com/en-us/aspnet/signalr/overview/guide-to-the-api/hubs-api-guide-server#tracing If you are adding SignalR functionality to an ASP.NET MVC application, make sure that the SignalR route is added before the other routes. For more information, see Tutorial: Getting Started with SignalR 2 and MVC 5.)
5.2 跨域訪問由Owin框架自己處理, 如果再由IIS web.config來控制CORS, 會產生沖突。
5.3 Hub Server中的方法,只是個簽名,具體實現不重要(無需寫實現,除非客戶端用hub.server.func()發消息)。 主要作用
5.3.1 生成客戶端代理類。
5.3.2 讓代理類綁定本地方法,以被服務端調用
6. Sender Message Proxy
6.1 如果用MVC做,有個很大的問題就是,跨域。
6.1.1 如果瀏覽器是IE, 問題不是很大。 貌似preflight request 被省略了。 request 被直接提交到MVC層, 可以自定義Action屬性, 加入”Access-Control-Allow-Origin“。
6.1.2 如果瀏覽器是Chrome, "preflight request"不會被忽略. http option verb會發過來。 這個時候MVC就有問題了, 無論從owin還是application_beginRequest,都無法獲得這個request,無法修改http頭,使得CORS通過。 (MVC 不能intercept http option request,而webapi能,webapi 是不是因為引用了system.web.http.webhost 才能intercept?)
6.1.3 對於simple CORS, IE 和 Chrome (Chrome 無須preflight), 如果用Get/Post傳數據,由於不走preflight, 自定義Action屬性修改http header是可行的。 但是如果用ajax, 有content type header的話, 那就必須要走preflight.
7. IIS setting, web.config
實際使用當中,如果使用MVC 和 SignalR, 客戶端用ajax post json (content type = application / json), 那么需要考慮直接使用直接從服務器設置應用CORS。 這個時候SignalR的CORS需要被取消(//map.UseCors(CorsOptions.AllowAll) )
<add name="Access-Control-Allow-Origin" value="https://localhost:44300" />
<add name="Access-Control-Allow-Headers" value="Content-Type" />
<add name="Access-Control-Allow-Credentials" value="true" />
CORS 介紹
一篇很好的文章介紹CORS:https://staticapps.org/articles/cross-domain-requests-with-cors/
注意Simple CORS / Complex CORS 的區別
備注
盡量少用IE來做測試,用Chrome或firefox比較標准,會得到和預期一致的結果。 IE的結果經常莫名其妙,比如SignalR不enable CORS (map.UseCors(CorsOptions.AllowAll);)在localhost下,跨域的連接居然也能成功。
常用代碼
var heartBeat = GlobalHost.DependencyResolver.Resolve<ITransportHeartbeat>();
var connectionAlive = heartBeat.GetConnections().FirstOrDefault(c=>c.ConnectionId == connection.ConnectionId);
if (connectionAlive.IsAlive)
{//Do whatever...}
======================================================
IHubContext context = GlobalHost.ConnectionManager.GetHubContext<MyHub>();
IHubContext context = GlobalHost.ConnectionManager.GetHubContext(“MyHub”);
======================================================
public class EnableCorsAttribute : ActionFilterAttribute
{
public override void OnActionExecuting(ActionExecutingContext filterContext)
{
filterContext.RequestContext.HttpContext.Response.AddHeader("Access-Control-Allow-Origin", "*");
base.OnActionExecuting(filterContext);
}
}