Проверка существования пользователя в чате script

Когда пользователь покидает страницу чата (либо путем выхода из системы, либо просто путем закрытия окна браузера). Чат script мгновенно обнаруживает, что пользователь ушел и показывает офлайн-знак. Этот процесс проходит через несколько секунд, как это действительно работает?

Я разработчик ASP.NET/c#, кроме этого, я использую JavaScripts и редко PHP. Пожалуйста, не превышайте ответы на другие языки.

Ответ 1

Просто отправьте свой код чата JavaScript на сервер каждые 2 секунды, используя XMLHttpRequest. Когда вы не получили сообщение, это означает, что пользователь закрыл окно.

Ответ 2

Как и было обещано, вот некоторые классы, используемые при реализации долгого опроса. В основном 6 классов (см. Ниже). Некоторые из этих классов могут оказаться ненужными для ВАШИХ целей, но они имеют смысл для моих. Они "в основном" были дезинфицированы для вас.

  • Контроллер: обрабатывает действия, необходимые для создания допустимого ответа (операции db и т.д.).
  • Процессор: управляет асинхронной связью с веб-страницей (сам)
  • IAsynchProcessor. Службы обрабатывают экземпляры, реализующие этот интерфейс.
  • Sevice: обрабатывает объекты запроса, которые реализуют IAsynchProcessor
  • Запрос: оболочка IAsynchProcessor, содержащая ваш ответ (объект)
  • Ответ: содержит пользовательские объекты или поля

Если вам нужна помощь с надстройкой JavaScript или HTML ниже, я напишу вам что-нибудь.

HTTP HANDLERS:

using System;
using System.Configuration;
using System.Web;
using System.Web.Script.Serialization;
using System.Web.Services;
using System.Web.SessionState;

namespace Concept.LongPolling.Handlers
{
    /// <summary>
    /// Summary description for Controller
    /// </summary>
    public class Controller : IHttpHandler, IReadOnlySessionState
    {
        #region CONSTRUCTORS
        #endregion

        #region PROPERTIES

        /// <summary>Gets a Boolean value indicating that another request can use the current instance of the DefaultHttpHandler class.</summary>
        /// <remarks>Returning true makes the same AsyncHttpHandler object be used for all requests.</remarks>
        /// <remarks>Returning false here makes ASP.Net create object per request.</remarks>
        public bool IsReusable { get { return true; } }

        #endregion

        #region METHODS

        /// <summary>Enables synchronous processing of HTTP Web requests</summary>
        /// <param name="context">An HttpContext object that provides references to the intrinsic server objects</param>
        /// /// <remarks>This is where you would send commands to the controller that would affect processing in some manner.</remarks>
        public void ProcessRequest(HttpContext context)
        {
            throw new NotImplementedException();
        }

        /// <summary>Creates the response object which is serialized back to the client</summary>
        /// <param name="response"></param>
        public static Response CreateResponse(Response response)
        {
            try
            {
                response.Generate();
            }
            catch (System.Exception ex)
            {
                response.SessionValid = false;
            }

            return response;
        }

        #endregion
    }
}

using System;
using System.Configuration;
using System.Web;
using System.Web.Script.Serialization;
using System.Web.Services;
using System.Web.SessionState;
using Concept.LongPolling.LongPolling;

namespace Concept.LongPolling.Handlers
{
    /// <summary>
    /// Summary description for Processor
    /// </summary>
    public class Processor : IHttpHandler, IHttpAsyncHandler, IReadOnlySessionState
    {
        #region CONSTRUCTORS
        #endregion

        #region PROPERTIES

        /// <summary>Gets a Boolean value indicating that another request can use the current instance of the DefaultHttpHandler class.</summary>
        /// <remarks>Returning true makes the same AsyncHttpHandler object be used for all requests.</remarks>
        /// <remarks>Returning false here makes ASP.Net create object per request.</remarks>
        public bool IsReusable { get { return false; } }

        #endregion

        #region METHODS

        /// <summary>Enables synchronous processing of HTTP Web requests</summary>
        /// <param name="context">An HttpContext object that provides references to the intrinsic server objects</param>
        public void ProcessRequest(HttpContext context)
        {
            throw new NotImplementedException();
        }

        #region IHttpAsyncHandler Members

        /// <summary>Enables asynchronous processing of HTTP Web requests</summary>
        /// <param name="context">An HttpContext object that provides references to the intrinsic server objects</param>
        /// <param name="cb">The method to call when the asynchronous method call is complete. If callback is null, the delegate is not called.</param>
        /// <param name="extraData"></param>
        /// <returns>Any state data that is needed to process the request.</returns>
        public IAsyncResult BeginProcessRequest(HttpContext context, AsyncCallback cb, object extraData)
        {
            Int32 someValueYouLikeToSendInYourClass = Convert.ToInt32(context.Request["Number"]);

            Request request = new Request(cb, context);
            request.Response.Number = someValueYouLikeToSendInYourClass;

            Service.Singleton.AddRequest(request);

            return request;
        }

        /// <summary>Provides an end method for an asynchronous process.</summary>
        /// <param name="result">An object that contains information about the status of the process.</param>
        public void EndProcessRequest(IAsyncResult result)
        {
            Request request = result as Request;
            JavaScriptSerializer serializer = new JavaScriptSerializer();

            request.HttpContext.Response.ContentType = "text/json";
            request.HttpContext.Response.Write(serializer.Serialize(request.Response));
            request.HttpContext.Response.End();
        }

        #endregion

        #endregion
    }
}

ПОДДЕРЖИВАЮЩИЕ КЛАССЫ:

using System;
using System.Runtime.InteropServices;

namespace Concept.LongPolling.LongPolling
{
    /// <summary>Represents the executable instance of an asynchronous operation.</summary>
    [ComVisible(true)]
    public interface IAsynchProcessor : IAsyncResult
    {
        /// <summary>
        /// Gets a value that indicates whether the operation completed sucessfully.
        /// </summary>
        /// <returns>true if the operation completed sucessfully; otherwise, false.</returns>
        bool ProcessRequest();
    }
}


using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Threading;

namespace Concept.LongPolling.LongPolling
{
    public class Service
    {
        #region CONSTRUCTORS

        private Service()
        {
            requests = new List<IAsynchProcessor>();
            backgroundThread = new Thread(new ThreadStart(MainLoop));
            backgroundThread.IsBackground = true;
            backgroundThread.Start();
        }

        #endregion

        #region PROPERTIES

        static readonly object _padlock = new object();

        private static Service singleton;
        private Thread backgroundThread;
        private List<IAsynchProcessor> requests;

        public static Service Singleton
        {
            get
            {
                lock (_padlock)
                {
                    if (_singleton == null)
                        _singleton = new Service();
                    return _singleton;
                }
            }
        }

        #endregion

        #region METHODS

        private void MainLoop()
        {
            while (true)
            {
                foreach (IAsynchProcessor request in requests.ToArray())
                {
                    if (request.ProcessRequest())
                        requests.Remove(request);
                }
                Thread.Sleep(500);
            }
        }

        public void AddRequest(IAsynchProcessor request)
        {
            requests.Add(request);
        }

        #endregion
    }
}

using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using Concept.LongPolling.Business;
using System.Data;

namespace Concept.LongPolling.Handlers
{
    public class Response
    {
        #region CONSTRUCTORS

        public Response()
        {
            SessionValid = true;
            Exception = String.Empty;
        }

        #endregion

        #region PROPERTIES

        public const int TimeOffset = 120;

        public Int32 Number { get; set; }
        public bool SessionValid { get; set; }
        public String Exception { get; set; }

        #endregion

        #region METHODS

        public void Generate()
        {
            // do some desired operation
            Number += 1;
        }

        #endregion
    }
}

using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using Concept.LongPolling.LongPolling;

namespace Concept.LongPolling.Handlers
{
    public class Request : IAsynchProcessor
    {
        #region CONSTRUCTORS

        public Request(AsyncCallback callback, HttpContext context)
        {
            asyncCallback = callback;
            httpContext = context;
            createdTime = DateTime.Now;

            Response = new Response();
        }

        #endregion

        #region PROPERTIES

        public const int TimeoutSeconds = 15;

        private AsyncCallback asyncCallback;
        private HttpContext httpContext;
        private DateTime createdTime;

        public bool TimedOut
        {
            get
            {
                return ((DateTime.Now - createdTime).TotalSeconds >= TimeoutSeconds);
            }
        }

        public Response Response { get; set; }

        #region IAsyncResult Members

        public HttpContext HttpContext
        {
            get
            {
                return httpContext;
            }
        }
        public object AsyncState { get; set; }

        System.Threading.WaitHandle IAsyncResult.AsyncWaitHandle
        {
            get { throw new NotImplementedException(); }
        }

        bool IAsyncResult.CompletedSynchronously
        {
            get { return false; }
        }

        public bool IsCompleted
        {
            get { return isCompleted; }
            set
            {
                if (!value) return;

                this.isCompleted = true;
                asyncCallback(this);
            }
        }
        bool isCompleted = false;

        #endregion

        #endregion

        #region METHODS

        public bool ProcessRequest()
        {
            this.Response = Controller.CreateResponse(this.Response);
            this.IsCompleted = true;

            return this.IsCompleted;
        }

        #endregion
    }
}

Ответ 3

Чат может отправлять сообщение о выходе с использованием события onunload, которое запускается, если пользователь покидает страницу/закрывает браузер, однако он не является надежным. Второй вариант для сервера - начать отсчет таймаута, как только закрытое TCP-соединение будет закрыто, и покажет пользователю как "автономное", если он не будет повторно подключаться вовремя.