구현기능
- 입장을 하면, 자신의 캐릭터(도형)가 랜덤생성됩니다.
실행 샘플
메시지 설계
웹소켓(브라우져)와 서버메시지(액터)의 정의를 통합할수 있습니다. - 블레이즈 특성
namespace BlazorChatApp.Shared { public class ChatData { } public class UserInfo { public string Id { get; set; } public string Name { get; set; } public string Color { get; set; } } public class RoomInfo { public string Id { get; set; } public string Name { get; set; } } public class UpdateUserPos :UserInfo { public double PosX{get; set; } public double PosY{get; set; } } public class ChatMessage { public UserInfo From { get; set; } public string Message { get; set; } } public class JoinRoom { public RoomInfo RoomInfo { get; set; } public UserInfo UserInfo { get; set; } } public class SyncRoom { public RoomInfo RoomInfo { get; set; } public UserInfo UserInfo { get; set; } } public class LeaveRoom { public RoomInfo RoomInfo { get; set; } public UserInfo UserInfo { get; set; } } public class BaseCmd { public string Command {get;set; } } public class RoomCmd : BaseCmd { public UserInfo UserInfo{ get; set; } public object Data{get;set; } } }
웹소켓 처리코드
시그널 R이 이용되었으며, 일부 서버에서 상태처리를 해야하는경우 액터를 연결하여 처리가 가능합니다.
- Cilent To Server : 브라우저에서 출발한 메시지를 서버에 전송합니다.
- Server To Client : 서버에서 연산된 메시지를, 브라우저에게 전송합니다.
using System.Collections.Generic; using System.Threading.Tasks; using Akka.Actor; using BlazorChatApp.Shared; using Microsoft.AspNetCore.SignalR; namespace BlazorChatApp.Server.Hubs { public class ChatHub : Hub { private ActorSystem actorSystem; private ActorSelection roomActor; public ChatHub(ActorSystem _actorSystem) { actorSystem = _actorSystem; roomActor = actorSystem.ActorSelection("user/room1"); } // Client To Server public async Task JoInRoom(JoinRoom joinRoom) { roomActor.Tell(joinRoom); } public async Task SyncRoom(SyncRoom syncRoom) { roomActor.Tell(syncRoom); } public async Task LeaveRoom(LeaveRoom leaveRoom) { roomActor.Tell(leaveRoom); } public async Task UpdateUserPos(UpdateUserPos updateUserPos) { roomActor.Tell(updateUserPos); } // Server To Client public async Task OnJoinRoom(RoomInfo roomInfo, UserInfo user, UpdateUserPos updateUserPos) { await Clients.All.SendAsync("OnJoinRoom", user, roomInfo, updateUserPos); } public async Task OnSyncRoom(UserInfo user, List<UpdateUserPos> updateUserPos) { await Clients.All.SendAsync("OnSyncRoom", user, updateUserPos); } public async Task OnLeaveRoom(LeaveRoom leaveRoom) { await Clients.All.SendAsync("OnLeaveRoom", leaveRoom); } public async Task OnUpdateUserPos(UpdateUserPos updateUserPos) { await Clients.All.SendAsync("OnUpdateUserPos", updateUserPos); } public async Task SendMessage(string user, string message) { await Clients.All.SendAsync("ReceiveMessage", user, message); } public async Task SendRoomMessage(RoomCmd roomCmd) { roomActor.Tell(roomCmd); } } }
서버코드
using System; using System.Collections.Generic; using System.Linq; using System.Text.Json; using System.Threading.Tasks; using Akka.Actor; using Akka.Event; using BlazorChatApp.Shared; using Microsoft.AspNetCore.SignalR.Client; namespace BlazorChatApp.Server.Hubs { public class RoomActor : ReceiveActor { private readonly ILoggingAdapter log = Context.GetLogger(); public Dictionary<string,UpdateUserPos> users = new Dictionary<string,UpdateUserPos>(); private string roomName; private int userAutoNo = 0; public HubConnection hubConnection { get; set; } Random random= new Random(); public RoomActor(string _roomName) { string baseUrl = "http://localhost:5000"; var _hubUrl = baseUrl.TrimEnd('/') + "/chathub"; hubConnection = new HubConnectionBuilder().WithUrl(_hubUrl).Build(); hubConnection.StartAsync().Wait(); roomName = _roomName; log.Info($"Create Room{roomName}"); Receive<RoomCmd>(cmd => { log.Info("Received String message: {0}", cmd); //Sender.Tell(message); }); Receive<JoinRoom>(async cmd => { userAutoNo++; string jsonString = JsonSerializer.Serialize(cmd); log.Info("Received JoinRoom message: {0}", jsonString); string RandomColor = string.Format("#{0:X6}", random.Next(0xFFFFFF)); UserInfo userInfo = new UserInfo() { Id=cmd.UserInfo.Id, Name=$"User-{userAutoNo}", Color=RandomColor }; UpdateUserPos updateUserPos= new UpdateUserPos() { Id=cmd.UserInfo.Id, Name=$"User-{userAutoNo}", PosX=random.NextDouble()*500,PosY=random.NextDouble()*500 }; users[cmd.UserInfo.Id] = updateUserPos; await OnJoinRoom(cmd.RoomInfo, userInfo, updateUserPos); }); Receive<SyncRoom>(async cmd => { userAutoNo++; string jsonString = JsonSerializer.Serialize(cmd); log.Info("Received SyncRoom message: {0}", jsonString); List<UpdateUserPos> updateUserPosList = users.Values.ToList(); //await hubConnection.SendAsync("OnSyncRoom", cmd.UserInfo, updateUserPosList); string RandomColor = string.Format("#{0:X6}", random.Next(0xFFFFFF)); UserInfo userInfo = new UserInfo() { Id=cmd.UserInfo.Id, Name=$"User-{userAutoNo}", Color=RandomColor }; await OnSyncRoom(userInfo, users.Values.ToList()); }); Receive<UpdateUserPos>(async cmd => { string jsonString = JsonSerializer.Serialize(cmd); log.Info("Received SyncRoom message: {0}", jsonString); if(users.ContainsKey(cmd.Id)) { users[cmd.Id].PosX+=cmd.PosX; users[cmd.Id].PosY+=cmd.PosY; } UpdateUserPos updateUserPos = new UpdateUserPos() { Id = cmd.Id, Name = cmd.Name, PosX = cmd.PosX, PosY = cmd.PosY }; await OnUpdateUserPos(updateUserPos); }); Receive<LeaveRoom>(async cmd => { string jsonString = JsonSerializer.Serialize(cmd); log.Info("Received LeaveRoom message: {0}", jsonString); if(users.ContainsKey(cmd.UserInfo.Id)) { users.Remove(cmd.UserInfo.Id); await OnLeaveRoom(cmd); } }); } public async Task OnJoinRoom(RoomInfo roomInfo, UserInfo user, UpdateUserPos updateUserPos) { await hubConnection.SendAsync("OnJoinRoom", user, roomInfo, updateUserPos); } public async Task OnSyncRoom(UserInfo user, List<UpdateUserPos> updateUserPos ) { await hubConnection.SendAsync("OnSyncRoom", user, updateUserPos); } public async Task OnLeaveRoom(LeaveRoom leaveRoom) { await hubConnection.SendAsync("OnLeaveRoom", leaveRoom); } public async Task OnUpdateUserPos(UpdateUserPos updatePos) { await hubConnection.SendAsync("OnUpdateUserPos", updatePos); } } }