BeetleX's Quick Build Web Multi-room Chat Room

Keywords: Programming Session Javascript Vue github

In fact, it is not a difficult technology to build a Web multi-room chat room. With the help of websocket s, it is easy to achieve multi-user real-time online communication interaction. Here we mainly introduce how to make this function more simple and efficient with the support of BeetleX and BeetleXjs.Next, I'll walk you through the implementation of a Web multi-room chat room step by step using BeetleX.

Information Logic

Since it is a multi-room chat room, it has two main elements, user and room; these two elements are described by classes below:

user

    public class User
    {
        public string Name { get; set; }

        public string Address { get; set; }

        [JsonIgnore]
        public ISession Session { get; set; }

        [JsonIgnore]
        public Room Room { get; set; }

        public void Send(BeetleX.FastHttpApi.WebSockets.DataFrame frame)
        {
            frame.Send(Session);
        }

        public void Exit()
        {
            Room?.Exit(this);
        }
    }

The information description is relatively simple, mainly including information using: name, conversation and room; the behaviors involved are sending information and exiting the room.

Room

    public class Room
    {

        public string Name { get; set; }

        public List<User> Users { get; private set; } = new List<User>();

        public HttpApiServer HttpServer { get; set; }

        public void Send(Command cmd)
        {
            cmd.Room = Name;
            var frame = HttpServer.CreateDataFrame(cmd);
            lock (Users)
            {
                foreach (var item in Users)
                    item.Send(frame);
            }

        }

        public User[] GetOnlines()
        {
            lock (Users)
                return Users.ToArray();
        }

        public void Enter(User user)
        {
            if (user == null)
                return;
            if (user.Room != this)
            {
                user.Room?.Exit(user);
                lock (Users)
                    Users.Add(user);
                user.Room = this;
                Command quit = new Command { Type = "enter",Message=$"enter room", User = user };
                Send(quit);
            }
        }

        public void Exit(User user)
        {
            if (user == null)
                return;
            lock (Users)
                Users.Remove(user);
            user.Room = null;
            Command quit = new Command { Type = "quit", Message = $"exit room", User = user };
            Send(quit);
        }
    }

Room information mainly includes name and user information. The specific behaviors are entering, leaving and sending information to the room.

Service logic

With logical information, this information needs to be provided to external access operations through the service mode of the interface. Next, a simple controller class is defined to describe the related interface service behavior.

    [BeetleX.FastHttpApi.Controller]
    public class Home : BeetleX.FastHttpApi.IController
    {
        [BeetleX.FastHttpApi.NotAction]
        public void Init(HttpApiServer server, string path)
        {
            for (int i = 0; i < 10; i++)
            {
                string key = $"{i:00}";
                mRooms[key] = new Room { Name = key, HttpServer = server };
            }
            server.HttpDisconnect += (o, e) =>
            {
                GetUser(e.Session)?.Exit();
            };
        }
        private ConcurrentDictionary<string, Room> mRooms 
		= new ConcurrentDictionary<string, Room>(StringComparer.OrdinalIgnoreCase);
        public object Rooms()
        {
            return from a in mRooms.Values orderby a.Name select new {a.Name};
        }
        public void Enter(string room, IHttpContext context)
        {
            User user = GetUser(context.Session);
            mRooms.TryGetValue(room, out Room result);
            result?.Enter(user);
        }
        public void Talk(string message, IHttpContext context)
        {
            if (!string.IsNullOrEmpty(message))
            {
                var user = GetUser(context.Session);
                Command cmd = new Command { Type = "talk", Message = message, User = user };
                user?.Room?.Send(cmd);
            }
        }
        public void Login(string name, IHttpContext context)
        {
            User user = new User();
            user.Name = name;
            user.Session = context.Session;
            user.Address = context.Request.RemoteIPAddress;
            SetUser(context.Session, user);
        }
        private User GetUser(ISession session)
        {
            return (User)session["__user"];
        }

        private void SetUser(ISession session, User user)
        {
            session["__user"] = user;
        }
    }

Init method

Used to initialize room information and bind connection disconnection events, and execute user exit if user disconnects.

Login Method

Log on to the user

Rooms method

Get all room information

Enter method

User Entry Room

Talk

User sends a message to the room

Start Services

When the functional logic is written, the next step is to deploy these interfaces to the websocket service, which is fairly simple to deploy:

    class Program
    {
        static void Main(string[] args)
        {
            var builder = new HostBuilder()
                .ConfigureServices((hostContext, services) =>
                {
                    services.UseBeetlexHttp(o =>
                    {
                        o.LogToConsole = true;
                        o.ManageApiEnabled = false;
                        o.Port = 80;
                        o.SetDebug();
                        o.LogLevel = BeetleX.EventArgs.LogType.Warring;
                    },
                    typeof(Program).Assembly);
                });
            builder.Build().Run();
        }
    }

When the service is deployed, you can concentrate on front-end implementation.

Web front end

For ease of integration with Beetlex services, the corresponding JavaScript components are encapsulated separately and specifically; in addition to the Self-encapsulated javascript, the use of vuejs is also involved.Integrating the front-end code with the above components is simpler than the service-side code, with the following details:

<body>
    <div id="page">
        <page-header> </page-header>
        <div class="container" style="margin-top:110px;">
            <div class="row">
                <ul style="list-style:none;">
                    <li v-for="item in messages" class="message">
                        <h4>
                            <span class="label label-success">[{{item.Room}}]</span>
                            <span class="label label-info">{{item.User.Name}}</span>
                            <span class="label label-default">{{new Date()}}</span>
                        </h4>
                        <div style="padding-left:20px;">
                            {{item.Message}}
                        </div>
                    </li>
                </ul>
            </div>
        </div>
        <page-footer :status="loginStatus" @login="onLogin($event)"
                     @talk="onTalk($event)" @select="onSelectRoom($event)" :rooms="getRooms.result">
        </page-footer>
    </div>
    <script>
        beetlex.websocket.receive = function (r) {
            page.messages.push(r);
        };
        beetlex.websocket.disconnect = function () {
            page.loginStatus = false;
        };
        beetlex.useWebsocket();
        var login = new beetlexAction("/Login");
        var getRooms = new beetlexAction('/Rooms', null, []);
        var enterRoom = new beetlexAction('/Enter');
        var talk = new beetlexAction('/Talk');
        login.requested = function (r) {
            page.loginStatus = true;
        };
        var model = {
            getRooms: getRooms,
            loginStatus: false,
            login: login,
            talk: talk,
            enterRoom: enterRoom,
            messages: [],
        };
        var page = new Vue({
            el: '#page',
            data: model,
            methods: {
                onSelectRoom: function (r) {
                    // alert(r);
                    this.enterRoom.post({ room: r });
                },
                onLogin: function (r) {
                    this.login.post({ name: r });
                },
                onTalk: function (msg) {
                    talk.post({ message: msg });
                },
            },
        });
        getRooms.get();
    </script>
</body>

beetlex

Is a feature class encapsulated for HTTP and websocket, which is automatically compatible with both requests; by default, http requests, all requests after calling useWebsocket take precedence over websockets; when WebSockets are not available, the group is automatically cut back to http.

beetlexAction

Used to describe a request, the post and get methods are provided; they are handled the same way in a websocket.

Running effect

Demo address

http://chat.ikende.com

Code Address

https://github.com/IKende/BeetleX-Samples/tree/master/WebSocket.Chat

Posted by Azu on Sat, 21 Dec 2019 22:13:34 -0800