首页 > 编程 > C# > 正文

C#制作简单的多人在线即时交流聊天室

2020-01-24 01:22:34
字体:
来源:转载
供稿:网友

实现网页版的在线聊天室的方法有很多,在没有来到HTML5之前,常见的有:定时轮询、长连接+长轮询、基于第三方插件(如FLASH的Socket),而如果是HTML5,则比较简单,可以直接使用WebSocket,当然HTML5目前在PC端并没有被所有浏览器支持,所以我的这个聊天室仍是基于长连接+长轮询+原生的JS及AJAX实现的多人在线即时交流聊天室,这个聊天室其实是我上周周末完成的,功能简单,可能有些不足,但可以满足在线即时聊天需求,分享也是给大家提供一个思路,大家可以基于此来实现更好的在线即时聊天工具。

聊天室功能简介:

1。支持多人进入同一个聊天室聊天;

2。进入即离线均会自动生成通知信息显示在聊天室中,这样聊天的人们就知道谁进来了谁离开了;

3。实时显示在线人员表列;

4。无需数据库支持,全部存在内存中,当然有条件的可以采用分布式缓存或加一个数据库来存,这里演示就是用内存来存了。

下面就开始分享我的代码,由于采用原生的JS及AJAX,所以简单易懂,代码分别WEB前端及服务端(有点废话了)

WEB前端源代码如下:(ChatPage.html)

<!DOCTYPE html><html xmlns="http://www.w3.org/1999/xhtml"><head>  <meta http-equiv="Content-Type" content="text/html; charset=utf-8" />  <title></title>  <style type="text/css">    html, body {      margin: 0px;      padding: 0px;      width: 100%;      height: 100%;      background-color: #f8f7f7;      font-family: arial,sans-serif;    }     #layouttable {      margin:0px;      padding:0px;      width:100%;      height:100%;      border:2px solid green;      border-collapse:collapse;      min-width:800px;    }       #layouttable td {        border: 1px solid green;      }     .h100p {      height:100%;    }     .midtr{height:auto;}      .midtr tr td {        height: 100%;      }     #chatmsgbox, #chatonlinebox {      background-color:white;      overflow-x: hidden;      overflow-y: auto;      overflow-wrap: break-word;      height: 100%;    }     #chatonlinebox {      background-color:#f5d0a8;    }     .rc, .sd {      overflow:hidden;    }      .rc p {      float: left;      color: green;    }      .sd p {        float: right;        color: orange;      }  </style> </head><body>  <table id="layouttable">    <colgroup>      <col style="width:auto" />      <col style="width: 200px;" />    </colgroup>    <tr style="height:30px; background-color:lightblue;color:yellow;">      <td>        欢迎进入梦在旅途的网页即时在线大众聊天室 - www.zuowenjun.cn:      </td>      <td>        当前在线人员      </td>    </tr>    <tr style="height:auto;" id="midtr">      <td>        <div id="chatmsgbox">        </div>      </td>      <td>        <div id="chatonlinebox">          <ul id="chatnames"></ul>        </div>      </td>    </tr>    <tr style="height:50px;">      <td colspan="2">        <label for="name">聊天妮称:</label>        <input type="text" id="name" style="width:80px;" />        <input type="button" id="btnsavename" value="确认进入" />        <label for="msg">输入内容:</label>        <input type="text" id="msg" style="width:400px;" />        <input type="button" id="btnSend" value="发送消息" disabled="disabled" />      </td>    </tr>  </table>  <script type="text/javascript">    var chatName = null;    var oChatmsgbox, oMsg, oChatnames;    var ajaxforSend, ajaxforRecv;     //页面加载初始化    window.onload = function () {      document.getElementById("btnsavename").onclick = function () {        this.disabled = true;        var oName = document.getElementById("name");        oName.readOnly = true;        document.getElementById("btnSend").disabled = false;        //receiveMsg();        setChatStatus(oName.value,"on");      }       document.getElementById("btnSend").onclick = function () {        sendMsg(oMsg.value);      };       //init      oChatmsgbox = document.getElementById("chatmsgbox");      oMsg = document.getElementById("msg");      oChatnames = document.getElementById("chatnames");      ajaxforSend = getAjaxObject();      ajaxforRecv = getAjaxObject();    }     //离开时提醒    window.onbeforeunload = function () {      event.returnValue = "您确定要退出聊天室吗?";    }     //关闭时离线    window.onunload = function () {      setChatStatus(chatName, "off");    }     //设置聊天状态:在线 OR 离线    function setChatStatus(name, status) {      callAjax(getAjaxObject(), "action=" + status + "&name=" + name, function (rs) {        if (!rs.success) {          alert(rs.info);          return;        }        if (status == "on") {          chatName = document.getElementById("name").value;          setTimeout("receiveMsg()",500);        }        loadOnlineChatNames();      });    }     //加载在线人员名称列表    function loadOnlineChatNames(){      callAjax(getAjaxObject(), "action=onlines", function (rs) {        var lis = "";        for(var i=0;i<rs.length;i++)        {          lis += "<li>"+ rs[i] +"</li>";        }        oChatnames.innerHTML = lis;      });    }     //接收消息列表    function receiveMsg() {      callAjax(ajaxforRecv, "action=receive&name=" + chatName, function (rs) {        if (rs.success) {          showChatMsgs(rs.msgs, "rc");        }        setTimeout("receiveMsg()", 500);      });    }    //发送消息    function sendMsg(msg) {      callAjax(ajaxforSend, "action=send&name=" + chatName + "&msg=" + escape(msg), function (rs) {        if (rs.success) {          showChatMsgs(rs.msgs, "sd");          oMsg.value = null;          //alert("发送成功!");        }      });    }     //显示消息    function showChatMsgs(msgs, cssClass) {      var loadonline = false;      for (var i = 0; i < msgs.length; i++) {        var msg = msgs[i];        oChatmsgbox.innerHTML += "<div class='" + cssClass + "'><p>[" + msg.name + "] - " + msg.sendtime + " 说:<br/>" + msg.content + "</p></div>";        if (msg.type == "on" || msg.type == "off")        {          loadonline = true;        }      }      if (loadonline)      {        loadOnlineChatNames();      }    }     //调用AJAX    function callAjax(ajax, param, callback) {       ajax.open("post", "ChatHandler.ashx", true);      ajax.setRequestHeader("Content-type", "application/x-www-form-urlencoded");      ajax.onreadystatechange = function () {        if (ajax.readyState == 4 && ajax.status == 200) {          var json = eval("(" + ajax.responseText + ")");          callback(json);        }      };      ajax.send(param);    }     //获取AJAX对象(XMLHttpRequest)    function getAjaxObject() {      var xmlhttp;      if (window.XMLHttpRequest) {// code for IE7+, Firefox, Chrome, Opera, Safari        xmlhttp = new XMLHttpRequest();      }      else {// code for IE6, IE5        xmlhttp = new ActiveXObject("Microsoft.XMLHTTP");      }      return xmlhttp;    }   </script></body></html>

代码很简单,并都有注释,在此就不作说明了,如果有疑问欢迎在下方评论。

服务端(ChatHandler.ashx) 

<%@ WebHandler Language="C#" Class="ChatHandler" %> using System;using System.Web;using System.Collections;using System.Collections.Generic;using System.Linq;using System.Web.Script.Serialization;using System.Threading;using System.Collections.Concurrent; public class ChatHandler : IHttpHandler{   private class Msg  {    public string name { get; set; }    public string sendtime { get; set; }    public string content { get; set; }    public string readednams { get; set; }    public int readedCount { get; set; }    public string type { get; set; }  }   private static List<Msg> msgs = new List<Msg>();  private static ReaderWriterLockSlim rwLock = new ReaderWriterLockSlim();  private static object syncObject = new object(),syncObject1 = new object();  private static List<string> onLineNames = new List<string>();   public void ProcessRequest(HttpContext context)  {    string chatName = context.Request.Form["name"];    string msg = context.Request.Form["msg"];    string actionName = context.Request.Form["action"];    JavaScriptSerializer jsSerializer = new JavaScriptSerializer();     object responseObject = null;     switch (actionName)    {      case "receive":        {          responseObject = GetNewMessages(chatName);          break;        }      case "send":        {          responseObject = SendMessage(chatName, msg, "normal");          break;        }      case "on":      case "off":        {          responseObject = SetChatStatus(chatName, actionName);          break;        }      case "onlines":        {          responseObject = onLineNames;          break;        }    }     context.Response.ContentType = "text/json";    context.Response.Write(jsSerializer.Serialize(responseObject));   }   private object SetChatStatus(string chatName, string status)  {    if (status == "on")    {      if (onLineNames.Exists(s => s == chatName))      {        return new { success = false, info = "该聊天妮称已经存在,请更换一个名称吧!" };      }      lock (syncObject1)      {        onLineNames.Add(chatName);      }      SendMessage(chatName, "大家好,我进入聊天室了!", status);      return new { success = true, info = string.Empty };    }    else    {      lock (syncObject1)      {        onLineNames.Remove(chatName);      }      SendMessage(chatName, "再见,我离开聊天室了!", status);      return new { success = true, info = string.Empty };    }  }   /// <summary>  /// 获取未读的新消息  /// </summary>  /// <param name="chatName"></param>  /// <returns></returns>  private object GetNewMessages(string chatName)  {    //第一种:循环处理    while (true)    {       var newMsgs = msgs.Where(m => m.name != chatName && !(m.readednams ?? "").Contains(chatName)).OrderBy(m => m.sendtime).ToList();      if (newMsgs != null && newMsgs.Count() > 0)      {        lock (syncObject)        {          newMsgs.ForEach((m) =>          {            m.readednams += chatName + ",";            m.readedCount++;          });          int chatNameCount = onLineNames.Count();          msgs.RemoveAll(m => m.readedCount >= chatNameCount);        }         return new { success = true, msgs = newMsgs };      }       Thread.Sleep(1000);    }      //第二种方法,采用自旋锁    //List<Msg> newMsgs = null;    //SpinWait.SpinUntil(() =>    //{    //  newMsgs = msgs.Where(m => m.name != chatName && !(m.readednams ?? "").Contains(chatName)).OrderBy(m => m.sendtime).ToList();    //  return newMsgs.Count() > 0;    //}, -1);     //rwLock.EnterWriteLock();    //newMsgs.ForEach(m =>    //{    //  m.readednams += chatName + ",";    //  m.readedCount++;    //});    //rwLock.ExitWriteLock();    //return new { success = true, msgs = newMsgs };  }   /// <summary>  ///  /// </summary>  /// <param name="chatName"></param>  /// <param name="msg"></param>  /// <returns></returns>  private object SendMessage(string chatName, string msg, string type)  {    var newMsg = new Msg() { name = chatName, sendtime = DateTime.Now.ToString("yyyy/MM/dd HH:mm"), content =HttpContext.Current.Server.HtmlEncode(msg), readednams = null, type = type };    //rwLock.EnterWriteLock();    lock (syncObject)    {      msgs.Add(newMsg);    }    //rwLock.ExitWriteLock();    return new { success = true, msgs = new[] { newMsg } };  }     public bool IsReusable  {    get    {      return false;    }  } }

代码也相对简单,实现原理主要是:

1。聊天消息:循环获取未读的消息,在取出读的消息同时,将其标识为已读,全部已读的消息则删除;--我这里采用了两种方法,第二种方法被注释掉了,大家可以取消注释试试,也是不错的,比第一种更直观,建议使用;

2。发送消息:实例化一个消息实例并加入到聊天消息集合中;

3。状态切换:上线则加入到在线人员集合中,并生成一条上线消息放入到聊天消息集合中,离线则从在线人员集合中移除该人员信息,并生成一条离线消息放入聊天消息集合中;

注意事项,由于采用了全局静态集合,所以线程同步比较重要。

最终的实现效果展示如下:

 张三:

李四:

小美:

如果觉得不错的话,给个推荐吧,你的支持是推动我不断前进的动力及写作的源泉,我一直坚持:知识在于分享,分享的同时自己也在成长,希望与大家共同成长,谢谢!

发表评论 共有条评论
用户名: 密码:
验证码: 匿名发表