SignalR into the pit notes

What is SignalR

ASP.NET Core SignalR is an open source library that simplifies the real-time web-enabled add functionality to your application. Real-time Web feature enables server-side code can instantly push content to the client.

ASP.NET Core SignalR some of the features:

  • Automatic connection management
  • Send a message to all connected clients. For example, chat room
  • Sending a message to a specific client or client group
  • It can be scaled to handle increased traffic

SignalR supports the following technologies for processing real-time communication:

  • WebSockets
  • Server-Sent Events
  • Long polling

Which Web Socket support only more modern browsers, Web servers can not be too old.

The Server Sent Events situation may be better, but there are the same problems.

So SignalR using the drop mechanism , SignalR have the ability to negotiate the transfer type of support .

Web Socket is the best most effective means of transmission, if the browser or Web server does not support it, it would downgrade SSE , it is impossible to use Long Polling .

Once the connection is established, SignalR will begin sending keep alive messages to check whether the connection is also normal. If there are problems, will throw an exception.

Because SignalR is abstract in the top three transmission mode, so no matter which way the underlying employed, SignalR usage is the same .

SignalR - Hub (Hub)

SignalR, we mainly need to do is inheriting Hubclass to send messages to each other and clients; can discern, SignalR server is a message center, the client sends a message to SignalR server, and then have we to deal with these messages, you can these messages are broadcasted, a client may be sent, the message is forwarded to another client, communications between the two clients;

Started SignalR - CountHub

Here you will complete a simple example of the use of SignalR service and client real-time communication:

  1. The client initiates a connection request
  2. The server accepts the request, the client sends to count from 0 to 10
  3. When the count to 10, the server calls the client Finished method, the client Finished close the connection

Server:

Build ASP.NET Core project, choose a blank template

New CountServiceclasses and CountHubclass

public class CountService
{
    private int _count;

    public int GetLastestCount() => _count++;
}
public class CountHub : Hub
{
    private readonly CountService _countService;

    public CountHub(CountService countService, 
        ILoggerFactory loggerFactory)
    {
        _countService=countService;
    }

    public async Task GetLastestCount()
    {
        IClientProxy client = Clients.Caller;
        int count;
        do
        {
            count = _countService.GetLastestCount();

            await Task.Delay(1000);
            
            await client.SendAsync("ReceiveUpdate", $"ConnectionId: {Context.ConnectionId}, count: {count}");
        } while (count < 10);

        await client.SendAsync("Finished");
    }
}

Registration Service Hub routing and configuration Startup class
services.AddScoped<CountService>();
services.AddSignalR();
endpoints.MapHub<CountHub>("/countHub");

Startup
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Hosting;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;
using SingleRStudy.Hubs;
using SingleRStudy.Services;

namespace SingleR
{
    public class Startup
    {
        // This method gets called by the runtime. Use this method to add services to the container.
        // For more information on how to configure your application, visit https://go.microsoft.com/fwlink/?LinkID=398940
        public void ConfigureServices(IServiceCollection services)
        {
            services.AddScoped<CountService>();

            services.AddSignalR();

            services.AddCors(options =>
            {
                options.AddPolicy("NgClientPolicy", p =>
                {
                    p.WithOrigins("http://localhost:4200")
                    .AllowAnyHeader()
                    .AllowAnyMethod()
                    .AllowCredentials();
                });
            });
        }

        // This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
        public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
        {
            if (env.IsDevelopment())
            {
                app.UseDeveloperExceptionPage();
            }
            app.UseCors("NgClientPolicy");

            app.UseRouting();

            app.UseEndpoints(endpoints =>
            {
                endpoints.MapHub<CountHub>("/countHub");
            });
        }
    }
}

Since I am here to Angular used as a client, so the Startup both cross-domain configuration.

The client (Angular)

Signalr introduced by npm: npm i @microsoft/signalr @types/node

Import signalr: import * as signalR from '@microsoft/signalr/'

Complete the following:

Chat Component
import { Component, OnInit } from '@angular/core';
import * as signalR from '@microsoft/signalr/'

/**
 * 创建连接
 */
const connection = new signalR.HubConnectionBuilder()
  .withUrl('//localhost:5000/countHub')
  .build();

@Component({
  selector: 'app-chat',
  templateUrl: './chat.component.html',
  styleUrls: ['./chat.component.css']
})
export class ChatComponent implements OnInit {

  constructor() { }

  async ngOnInit() {
    connection.on('ReceiveUpdate', (message: string) => {
      console.log(message);
    });

    connection.on('Finished', () => {
      console.log('count finished');
      connection.stop();
    });

    connection.onclose(error => {
      console.error('signalR connection closed. error: ', error);
    });

    // 开始通信
    await connection.start().catch(error => {
      console.error(error);
    });
    if(connection.state === signalR.HubConnectionState.Connected)
      await connection.send('GetLastestCount', 'aaa');
  }

}

The client defines ReceiveUpdateand Finishedallows the server method invocation.

ReceiveUpdateMethod, the method parameters printed on the console;

FinishedThe method is used to close the connection.

operation result:

image

Started SignalR - ChatRoom

Server :

Establish ChatHubclass

ChatHub
using Microsoft.AspNetCore.SignalR;

namespace SignalRStudy.Hubs
{
    public class ChatHub : Hub
    {
        public async void SendMessage(string username, string message)
        {
           await Clients.All.SendAsync("ReceiveMessage", username, message);
        }
    }
}

Startup To configure it:

app.UseEndpoints(endpoints =>
{
    ...
    endpoints.MapHub<ChatHub>("/chat");
});

The server is very simple, it is to put forward a received message to all connected clients

The client (Angular):

ChatService

Chat Service
import { Injectable, EventEmitter } from '@angular/core';
import * as signalr from '@microsoft/signalr'
import { Observable, of, Subscribable, Subscriber } from 'rxjs';

const connection = new signalr.HubConnectionBuilder()
  .withUrl('http://localhost:5000/chat')
  .build();

@Injectable()
export class ChatService {
  receivedMessage$ = new Observable<any>(observer => {
    connection.on('ReceiveMessage', (username: string, message: string) => {
      observer.next({
        username,
        message
      });
    });
  });
  username: string = '匿名用户';

  constructor() { 
    // connection.on('ReceiveMessage', this.receiveMessage);
  }

  async startChat() {
    await connection.start();
  }

  async sendMessage(message: string) {
    // 等待连接或断开连接操作完成
    while(connection.state === signalr.HubConnectionState.Connecting 
      || connection.state === signalr.HubConnectionState.Disconnecting);
    // 如果未连接服务器, 则尝试进行连接
    if(connection.state === signalr.HubConnectionState.Disconnected) {
      await connection.start().catch(err => console.error('signalr failed to connect to server.'));
    }
    if(connection.state === signalr.HubConnectionState.Connected) {
      await connection.send('SendMessage', this.username, message);
    }
  }
}

ChatService processing logic SignalR interactive component can subscribe ReceivedMessage$to get the latest news ...

The following put the code related components:

chat.component.ts
import { Component, OnInit, ViewChild, ElementRef } from '@angular/core';
import { ChatService } from '../../services/chat.service';


@Component({
  selector: 'app-chat',
  templateUrl: './chat.component.html',
  styleUrls: ['./chat.component.css']
})
export class ChatComponent implements OnInit {
  messageToSend: string = '';
  receivedMessages: any[] = [];
  @ViewChild('messageBox', { static: true }) messageBox:ElementRef;

  constructor(
    private chatServ: ChatService
  ) { 
  }

  async ngOnInit() {
    this.chatServ.username = 'laggage';
    
    await this.chatServ.startChat();
    this.chatServ.receivedMessage$.subscribe(r => {
      this.receivedMessages.push(r);
      // 滚动条滑动到最底部, 等待5ms是为了等待angular渲染完界面, 否则可能无法滚动到底部
      setTimeout(() => {
        let ele = this.messageBox.nativeElement as HTMLDivElement;
        ele.scrollTop = ele.scrollHeight;
      }, 5);
    });
  }

  get username() {
    return this.chatServ.username;
  }

  set username(value: string) {
    if(value != this.chatServ.username)
      this.chatServ.username = value;
  }

  sendMessage() {
    this.chatServ.sendMessage(this.messageToSend);
    this.messageToSend = '';
  }
}
chat.component.html
<div id="wrapper">
    <!-- chat works -->
    <div id="message-receive-area">
        <div id="message-container"
             #messageBox
             class="px-3 py-2 overflow-auto">
            <div class="message-item jumbotron p-0 px-3 py-2 mb-3"
                 *ngFor="let message of receivedMessages">
                <span> {{message.username}}说: </span>
                <span class="d-block"> {{message.message}} </span>
            </div>
        </div>
    </div>

    <div id="message-send-area"
         class="container-fluid mx-0 row">
        <div id="write-message"
             class="col col-8 pl-0">
            <textarea name="message"
                      class="h-100 w-100"
                      [(ngModel)]="messageToSend"></textarea>
        </div>
        <div class="col col-4 overflow-hidden pr-0">
            <div class="mb-3">
                <label for="">
                    用户名
                    <input type="text"
                           class="w-100"
                           name="username"
                           placeholder="用户名"
                           [(ngModel)]="username">
                </label>
            </div>
            <div class="w-100"> <button class="w-100 overflow-hidden"
                        (click)="sendMessage()">Send</button> </div>
        </div>
    </div>
</div>
chat.component.css
#message-receive-area {
    height: 60vh;
    padding: .6rem;
}

#message-container {
    border: 1px solid black;
    height: 100%;
}

#message-send-area {
    height: 40vh;
    padding: .6rem;
}

Firstly...

Guess you like

Origin www.cnblogs.com/Laggage/p/12387561.html