如果您发现自己通过对 REST 服务的反复调用且频繁地检查信息,这可能是您应该转向 WebSocket的好兆头。比如一个网站的个人信息通知栏, 通过使用 WebSocket来减少带宽使用,因为它可以帮助人们不用花时间在主页上按 F5 来查看他们是否有新消息。
如果您发现您打开了 WebSocket连接,使用它们进行单个调用,然后关闭它们,或者如果您的连接保持打开但服务器仅在客户端请求时向客户端发送某些内容,请切换到 REST。
WebStock占用了连接数,节约了带宽; REST节约了连接数, 占了带宽. 看你如何取舍
Differences between SignalR and ASP.NET Core SignalR | Microsoft Docs
服务器端主动发消息给客户端:
IHubContext _myHubContext = GlobalHost.ConnectionManager.GetHubContext<MyHub>(); _myHubContext.Clients.Group("Group1").addMessage("系统广播",@"{ 'people': [ { 'firstName': 'Bret', 'lastName':'Mc', 'email': 'brett@new.com' }]}");
客户端
<!DOCTYPE html> <html> <head> <title>SignalR Simple Chat</title> <style type="text/css"> .container { background-color: #99CCFF; border: thick solid #808080; padding: 20px; margin: 20px; } </style> </head> <body> <div class="container"> <input type="text" id="message" /> <input type="button" id="sendmessage" value="Send" /> <input type="hidden" id="displayname" /> <ul id="discussion"></ul> </div> <!--Script references. --> <!--Reference the jQuery library. --> <script src="Scripts/jquery-1.6.4.min.js"></script> <!--Reference the SignalR library. --> <script src="Scripts/jquery.signalR-2.4.3.min.js"></script> <!--Reference the autogenerated SignalR hub script. --> <script src="http://localhost:9021/signalr/hubs"></script> <!--Add script to update the page and send messages.--> <script type="text/javascript"> $(function () { //Set the hubs URL for the connection $.connection.hub.url = "http://localhost:9021/signalr"; // Declare a proxy to reference the hub. var chat = $.connection.myHub; // Create a function that the hub can call to broadcast messages. chat.client.addMessage = function (name, message) { // Html encode display name and message. var encodedName = $('<div />').text(name).html(); var encodedMsg = $('<div />').text(message).html(); // Add the message to the page. $('#discussion').append('<li><strong>' + encodedName + '</strong>: ' + encodedMsg + '</li>'); }; // Get the user name and store it to prepend to messages. $('#displayname').val(prompt('Enter your name:', '')); // Set initial focus to message input box. $('#message').focus(); //querystring $.connection.hub.qs = { "version": "1.0", "company": "Microsoft", "name": $('#displayname').val() }; // Start the connection. $.connection.hub.start().done(function () { console.log("web socket connected...") $('#sendmessage').click(function () { console.log("Call the Send method on the hub.") // Call the Send method on the hub. chat.server.send($('#displayname').val(), $('#message').val()); // Clear text box and reset focus for next comment. $('#message').val('').focus(); }); }); }); </script> </body> </html>
服务器端
using Microsoft.AspNet.SignalR; using Newtonsoft.Json; using System.Threading.Tasks; namespace KD.LCSM { /// <summary> /// 这个就是整个SignalR的核心了,它主要有几个函数需要重写, /// 如OnConnected、OnDisconnected、OnReconnected、以及一个通用的消息发送AddMessage函数。 /// </summary> /// //Hub的别名,方便前台调用, 没有指定时 ,JavaScript 客户端采用camel 形式 MyHub=>myHub //[HubName("MyHub")] 使用 HubName 属性时,JavaScript 客户端上不会更改为 camel 大小写的名称。 public class MyHub : Hub { /// <summary> /// 编写发送信息的方法 /// </summary> /// <param name="name"></param> /// <param name="message"></param> public void Send(string name, string message) //首页的方法 { GlobalVariable.MainForm.WriteToConsole(string.Format("客户端 【{0}】发送:{1}", name, message)); //调用所有客户注册的本地的JS方法(addMessage) Clients.All.addMessage(name, message); //转发到所有客户端,类似公开聊天室 //Clients.Group("首页组").addMessage(name, message); } public void RemoveFromGroup(string name, string message) //从首页组移除 { } public void ShowStratorInfo(string name, string message) //显示定子数据 { } /// <summary> /// 在连接上时 /// </summary> /// <returns></returns> public override Task OnConnected() { var IP = Context.Request.Environment["server.RemoteIpAddress"]; var name = Context.QueryString.Get("name"); var version = Context.QueryString.Get("version"); var Company = Context.QueryString.Get("Company"); //var client = JsonConvert.DeserializeObject<ClientModel>(Context.QueryString.Get("Param")); //if (client != null) { //client.ConnId = Context.ConnectionId; //将客户端连接加入界面列表, 并对客户进行分组 GlobalVariable.MainForm.AddClient(Context.ConnectionId,name); this.Groups.Add(Context.ConnectionId, "Client"); //向服务端写入一些数据 GlobalVariable.MainForm.WriteToConsole(string.Format("客户端 【{0}】接入:{1} ,IP地址:{2}客户端总数:{3}", name, Context.ConnectionId, IP, GlobalVariable.MainForm.ClientCount)); return base.OnConnected(); } } public override Task OnDisconnected(bool stopCalled) { Groups.Remove(Context.ConnectionId, "Client"); GlobalVariable.MainForm.RemoveClient(Context.ConnectionId); GlobalVariable.MainForm.WriteToConsole(string.Format("客户端断开:{0}客户端总数:{1}", Context.ConnectionId, GlobalVariable.MainForm.ClientCount)); return base.OnDisconnected(stopCalled); } } }
SignalR 默认只能在Localhost, 外网访问 考虑用nginx来端口转发,把signalr路径开头的都转到localhost
, client========> server(192.168.0.X) ====> signalR(localhost:9021)
http://localhost:9021/signalr/negotiate?clientProtocol=2.1&version=1.0&company=Keding&name=apple&connectionData=[{"name":"myhub"}]&_=1647332664509 ws://localhost:9021/signalr/connect?transport=webSockets&clientProtocol=2.1&version=1.0&company=ms&name=apple&connectionToken=AQAAANCMnd8BFdERjHoAwE/Cl+sBAAAAsh3lDa2xnkSO5gf0fNXg9QAAAAACAAAAAAAQZgAAAAEAACAAAAAk+DIeew21HpF4AAab6oqj4Yb1NUUU0SDc6ZlBDoDKwgAAAAAOgAAAAAIAACAAAACGq+iSbpCACodCipNe8Cy/Y8PNHQFLYGH3btG2wsOAuzAAAAAujl4u+sTrCKQ5Z4g2XILvvm6o9nQrBfSX10akqA4HGQtmGiHJOpK9+XkECBdQBsJAAAAAzCxM9dY3PYM0e63JVjLvs+03E/WLKY5yYq9VFT12wQm4YVhVZ/5mnxihCLv2PeOpEPaXYtPG5CjJfU5H5wof+Q==&connectionData=[{"name":"myhub"}]&tid=5 http://localhost:9021/signalr/start?transport=webSockets&clientProtocol=2.1&version=1.0&company=ms&name=apple&connectionToken=AQAAANCMnd8BFdERjHoAwE/Cl+sBAAAAsh3lDa2xnkSO5gf0fNXg9QAAAAACAAAAAAAQZgAAAAEAACAAAAAk+DIeew21HpF4AAab6oqj4Yb1NUUU0SDc6ZlBDoDKwgAAAAAOgAAAAAIAACAAAACGq+iSbpCACodCipNe8Cy/Y8PNHQFLYGH3btG2wsOAuzAAAAAujl4u+sTrCKQ5Z4g2XILvvm6o9nQrBfSX10akqA4HGQtmGiHJOpK9+XkECBdQBsJAAAAAzCxM9dY3PYM0e63JVjLvs+03E/WLKY5yYq9VFT12wQm4YVhVZ/5mnxihCLv2PeOpEPaXYtPG5CjJfU5H5wof+Q==&connectionData=[{"name":"myhub"}]&_=1647332664551
nginx的conf文件
#user nobody; worker_processes 1; #error_log logs/error.log; #error_log logs/error.log notice; #error_log logs/error.log info; #pid logs/nginx.pid; events { worker_connections 1024; } http { include mime.types; default_type application/octet-stream; #log_format main '$remote_addr - $remote_user [$time_local] "$request" ' # '$status $body_bytes_sent "$http_referer" ' # '"$http_user_agent" "$http_x_forwarded_for"'; #access_log logs/access.log main; sendfile on; #tcp_nopush on; #keepalive_timeout 0; keepalive_timeout 65; map $http_upgrade $connection_upgrade { default keep-alive; #默认为keep-alive 可以支持 一般http请求 'websocket' upgrade; #如果为websocket 则为 upgrade 可升级的。 } #gzip on; server { listen 80; server_name localhost; #charset koi8-r; #access_log logs/host.access.log main; location / { root html; index index.html index.htm; } location ~ /signalr/ { proxy_pass http://localhost:9021; #启用http长连接支持websocket proxy_http_version 1.1; proxy_set_header Upgrade $http_upgrade; proxy_set_header Connection $connection_upgrade; } #error_page 404 /404.html; # redirect server error pages to the static page /50x.html # error_page 500 502 503 504 /50x.html; location = /50x.html { root html; } # proxy the PHP scripts to Apache listening on 127.0.0.1:80 # #location ~ \.php$ { # proxy_pass http://127.0.0.1; #} # pass the PHP scripts to FastCGI server listening on 127.0.0.1:9000 # #location ~ \.php$ { # root html; # fastcgi_pass 127.0.0.1:9000; # fastcgi_index index.php; # fastcgi_param SCRIPT_FILENAME /scripts$fastcgi_script_name; # include fastcgi_params; #} # deny access to .htaccess files, if Apache's document root # concurs with nginx's one # #location ~ /\.ht { # deny all; #} } # another virtual host using mix of IP-, name-, and port-based configuration # #server { # listen 8000; # listen somename:8080; # server_name somename alias another.alias; # location / { # root html; # index index.html index.htm; # } #} # HTTPS server # #server { # listen 443 ssl; # server_name localhost; # ssl_certificate cert.pem; # ssl_certificate_key cert.key; # ssl_session_cache shared:SSL:1m; # ssl_session_timeout 5m; # ssl_ciphers HIGH:!aNULL:!MD5; # ssl_prefer_server_ciphers on; # location / { # root html; # index index.html index.htm; # } #} }
SignalR WebSocket localhost测试,第一次连接时间要一秒,重新刷新也要500ms
我用Microsoft.AspNet.SignalR.Core在winform里建立WebSocket服务器,
现在有个问题,我启动服务器, 客户端可以连接上, 但是我点停止服务器再点重新启动, 客户端就连不上了.
一定要退出exe,重新打开才能连上.
启动服务器用的是 ServerUri = "http://localhost"; SignalR = WebApp.Start(ServerUri); // 启动SignalR服务,
停止服务器用的 SignalR.Dispose();
SignalR = null;