利用AJAX和ASP.NET实现简单聊天室

正文为大家大饱眼福了Redis辅助三个人多闲聊室成效的两全代码,供大家参谋,具体内容如下

概述

使用AJAX和ASP.NET达成轻易闲话室

两全原理

环信IM的意义一向在衍变,譬喻推送成效和闲聊室功效是近期的版本中增多的。

  介绍

左侧的二个数据域,代表五个闲聊室,聊天室id分别是827,729

在前直面环信IM有了着力领会后,下来大家通过解析德姆o,来拜见它是怎么选拔UI库和SDK库,它的最首要协会怎么设计的,进而为开辟FSIM做好幼功。

  笔者之第四个简易之Chat room 是用ASP 3.0
写成之。那无外乎有三个TextBox,他们发送消息给程序变量然后显示在两个每秒刷新之页面上。在万分时代,一个着实之闲聊室必得利用Java
Applet或ActiveX
control。可是这一切都在Ajax到来之后修改了。AJAX是叁个组成了XML 和
javascript之异步通讯机制。今后我们能够只用服务器代码和一点JavaScript
。那篇小说就是介绍怎样用AJAX手艺来塑造叁个回顾之谈天室。

在谈天室827里,有2个人,分别是jason22,jeff24他们各自已经阅读过闲谈室内的id为5和6的消息

第一数据构造和操作

  自己要作为表率信守规则程序

右侧的三个数据域,代表了客户在分歧的闲话室,jason22参加了827与729闲谈室,在此三个闲聊室里,他个别阅读到了id为5和id为10的新闻

1. 消息Message

  示例程序是一个单纯之多客商谈天室。此中间维护着贰个已登陆客户之列表。列表将免去session过期之顾客。同临时间它还扶助部分发令比方/admin Clear 淹没闲聊室 /nick [Name] 退换客商姓名。

除此以外827闲谈室内id为5的消息与729聊天房内id为5的音信不等同。

相应的数据构造是EMMessage。

  你还亟需知道

同一时候还会有五个域 msgs:chatid
那是一个zset,有序聚焦,member是音信体,score是新闻id
代表的是某些谈心房间里已经发生的音讯 其它这里面存的是平价的新闻,已经被全部人都阅读的新闻就可以被去除

按类型分为文字消息EMChatText,图片音讯EMChatImage,地方新闻EMChatLocation,语音音信EMChatVoice,录像新闻EMChatVideo,文件音讯EMChatFile,透传音信EMChatCommand(服务器发送指令给客商端做特殊操作);按传送范围,分为单聊eMessageTypeChat,群聊eConversationTypeGroupChat,闲谈室eConversationTypeChatRoom等品类。

  那几个顺序接收三个类叫做 ChatEngine 。
那一个类调控了上上下下之顾客和音讯。客商被积攒在叁个Hashtable 里,而新闻储存在
StringCollection 里:

ids:chatid
是贰个String型的多少,里面放的是最新的音信的编号(发音信时,自增这一个字段,就可以获取最新的值卡塔尔

在单聊景况下插入一条文本音信的代码示例:

Hashtable users;StringCollection chat;
一个 ChatEngine 之全局实例被停放在 Global.asax.cs :

ids:chat:
是贰个String型的数目,里面放的是新型的闲谈室的数码(创立闲谈室时,自增这些字段卡塔尔国

复制代码

public static UChat.ChatEngine.IChatEngine Engine =new
UChat.ChatEngine.ChatEngine();
二个JavaScript 函数用来异步之将全局变量内之数据显示在页面上:

代码

1–EMChatText *txt = [[EMChatText alloc] initWithText:@”test1″];

2–EMTextMessageBody *body = [[EMTextMessageBody alloc]
initWithChatObject:txt];

3–EMMessage *message = [[EMMessage alloc]
initWithReceiver:_conversation.chatter bodies:@[body]];

4–message.messageType = eMessageTypeChat;

5–message.deliveryState = eMessageDeliveryState_Delivered;

6–[[EaseMob sharedInstance].chatManager
insertMessageToDB:message];

function setTimers(){timeID = window.setTimeout( “updateAll()”,
refreshRate );}
接收种种顾客提供之称号和ID来标记客商:

OK 起首看代码

音讯的操作,除了成立,还也许有发送,选择在线恐怕离线消息,音信深入分析,音信已送达和音信已读回执,那也便是聊天chat的重大操作。

public void AddUser(string id, string user){//make sure user name does
not exist alreadyif( !UserExists( user ) ){//add user to users
listusers.Add( id, user );//display a notification message to all users
chat.Add( this.MakeServerMessage(string.Format(joinedfmt, user ) ));}}

public String createChat(Jedis conn, String sender, SetString recipients, String message) { //启动的时候redis里是没有ids:chat:这个键的 //自增之后返回1 String chatId = String.valueOf(conn.incr("ids:chat:")); return createChat(conn, sender, recipients, message, chatId); } /** * * @param conn * @param sender 发送消息的人 * @param recipients 接受消息的人 * @param message 待发送的消息 * @param chatId 聊天室的编号 * @return */ public String createChat( Jedis conn, String sender, SetString recipients, String message, String chatId){ //自己发的消息 自己也能接受到 recipients.add(sender); Transaction trans = conn.multi(); for (String recipient : recipients){ //聊天室的成员 最开始时 都阅读的是0号信息 trans.zadd("chat:" + chatId, 0, recipient); //记录每个人参加的聊天室 trans.zadd("seen:" + recipient, 0, chatId); } trans.exec(); return sendMessage(conn, chatId, sender, message); } public String sendMessage(Jedis conn, String chatId, String sender, String message) { //锁住聊天室 为啥 人员变动了咋办 //这个acquireLock见上一章 String identifier = acquireLock(conn, "chat:" + chatId); if (identifier == null){ throw new RuntimeException("Couldn't get the lock"); } try { //给要发布的消息设定一个最新的编号 第一次时 返回的是1 long messageId = conn.incr("ids:" + chatId); HashMapString,Object values = new HashMapString,Object(); values.put("id", messageId); values.put("ts", System.currentTimeMillis()); values.put("sender", sender); values.put("message", message); String packed = new Gson().toJson(values); //某个聊天室的消息列表 //最旧的消息----消息json //默认的zset是按照score的值从小到大排序 conn.zadd("msgs:" + chatId, messageId, packed); }finally{ releaseLock(conn, "chat:" + chatId, identifier); } return chatId; } 

只顾:登录成功现在本领实行谈心操作。发新闻时,单聊和群聊调用的是联合接口,分歧只是要安装下message.isGroup属性。

  截图和落到实处步骤

发音讯今后就OK了,剩下的尽管顾客去拉取未读的新闻了。那一个比较麻烦,恩,十一分的辛劳

异步发送音讯接口:

  主页突显了谈天室之主旨音信,举例有稍许人在聊天室、ChatLog之轻重。

 @SuppressWarnings("unchecked") public ListChatMessages fetchPendingMessages(Jedis conn, String recipient) { //获得用户在各个聊天室 已经看到的最新消息的id //有几个聊天室 seenSet的size就是几 SetTuple seenSet = conn.zrangeWithScores("seen:" + recipient, 0, -1); ListTuple seenList = new ArrayListTuple(seenSet); Transaction trans = conn.multi(); for (Tuple tuple : seenList){ String chatId = tuple.getElement(); int seenId = (int)tuple.getScore(); //获取每个聊天室里 未读的所有消息 //min 和 max 可以是 -inf 和 +inf trans.zrangeByScore("msgs:" + chatId, String.valueOf(seenId + 1), "inf"); } //我参加了几个聊天室 results的长度就是几 ListObject results = trans.exec(); //com.google.gson.Gson jar包自己下载吧 Gson gson = new Gson(); IteratorTuple seenIterator = seenList.iterator(); IteratorObject resultsIterator = results.iterator(); //用户最后成功拉取的未读消息 存放在chatMessages ListChatMessages chatMessages = new ArrayListChatMessages(); ListObject[] seenUpdates = new ArrayListObject[](); ListObject[] msgRemoves = new ArrayListObject[](); //这个大的while循环 用户参与了几个聊天室 就循环几次 while (seenIterator.hasNext()){ Tuple seen = seenIterator.next(); SetString messageStrings = (SetString)resultsIterator.next(); if (messageStrings.size() == 0){ //没有未读的消息 continue; } //代码运行到这里 //说明 我在某个聊天室 还有未读的消息 //seedid记录我已经拉取到的消息 初始为0 int seenId = 0; //当前处理的是哪个聊天室 String chatId = seen.getElement(); ListMapString,Object messages = new ArrayListMapString,Object(); //我在聊天室未读的消息列表 for (String messageJson : messageStrings){ MapString,Object message = (MapString,Object)gson.fromJson( messageJson, new TypeTokenMapString,Object(){}.getType()); int messageId = ((Double)message.get("id")).intValue(); if (messageId  seenId){ seenId = messageId; } message.put("id", messageId); //加入到成功拉取的列表里 messages.add(message); } //更新我在这个聊天室读到的最新消息 conn.zadd("chat:" + chatId, seenId, recipient); //记录我在某个聊天室读到的最新记录 seenUpdates.add(new Object[]{"seen:" + recipient, seenId, chatId}); //取出第0个member-score SetTuple minIdSet = conn.zrangeWithScores("chat:" + chatId, 0, 0); //为啥删除呢 每个聊天室是一个zset表 第一条记录代表的就是 所有用户至少都读了的消息 if (minIdSet.size()  0){ Tuple tuple=minIdSet.iterator().next(); System.out.println("要删除的 tuple:"+tuple.getElement()+"--"+tuple.getScore()); msgRemoves.add(new Object[]{"msgs:" + chatId, tuple.getScore()}); } chatMessages.add(new ChatMessages(chatId, messages)); } trans = conn.multi(); for (Object[] seenUpdate : seenUpdates){ trans.zadd( (String)seenUpdate[0], (Integer)seenUpdate[1], (String)seenUpdate[2]); } for (Object[] msgRemove : msgRemoves){ trans.zremrangeByScore( (String)msgRemove[0], 0, ((Double)msgRemove[1]).intValue()); } trans.exec(); //返回的是我这次拉取获得的 最新的消息 return chatMessages; } 

复制代码

  为了能够登陆闲聊室,必需提供贰个称谓。

OK,我们看看测验代码:

/*!

@method

@brief 异步方法, 发送一条音信

@discussion 待发送的音讯对象和出殡和安葬后的信息对象是同三个指标,
在发送进程中目的属性恐怕会被修正. 在殡葬进程中,
willSendMessage:error:和didSendMessage:error:那七个回调会被触发

@param message  音讯对象(包罗from, to, body列表等新闻卡塔尔(قطر‎

@param progress 发送多媒体消息时的progress回调对象

@result 发送的消息对象(因为是异步方法,
无法当作发送达成或发送成功失败与否的推断State of Qatar

*/

– (EMMessage *)asyncSendMessage:(EMMessage *)message

progress:(id)progress;

  当 Login 开关被单击。上面之代码就能被实行:

package redisinaction; import java.util.Arrays; import java.util.HashSet; import java.util.Iterator; import java.util.List; import java.util.Map; import java.util.Set; import org.junit.BeforeClass; import org.junit.Test; import jedis.redis_in_action.Chapter06; import jedis.redis_in_action.Chapter06.ChatMessages; import redis.clients.jedis.Jedis; import redis.clients.jedis.Tuple; /** * This class is used for ... * @author dlf(460795365@qq.com) * @version 1.0, 2016年10月17日 下午10:15:58 */ public class Chapter06Test { static Jedis conn = null; static Chapter06 c=null; @BeforeClass public static void initConn(){ System.out.println("test before"); conn = new Jedis("10.150.0.80"); conn.auth("dlf123123"); c=new Chapter06(); } @Test public void testMultiRecipientMessaging() { System.out.println("\n----- testMultiRecipientMessaging -----"); conn.del("ids:chat:", "msgs:1", "ids:1", "seen:joe", "seen:jeff", "seen:jenny"); System.out.println("Let's create a new chat session with some recipients..."); SetString recipients = new HashSetString(); recipients.add("jeff"); recipients.add("jenny"); String chatId = c.createChat(conn, "joe", recipients, "message 1"); System.out.println("Now let's send a few messages..."); for (int i = 2; i  5; i++){ c.sendMessage(conn, chatId, "joe", "message " + i); } System.out.println(); System.out.println("看看消息库"); //消息库里的所有消息 SetTuple messageFromBase=conn.zrangeWithScores("msgs:"+chatId, 0, -1); IteratorTuple iterator=messageFromBase.iterator(); while(iterator.hasNext()){ Tuple tuple=iterator.next(); System.out.println(tuple.getElement()+" -- "+tuple.getScore()); } System.out.println("And let's get the messages that are waiting for jeff and jenny..."); ListChatMessages r1 = c.fetchPendingMessages(conn, "jeff"); ListChatMessages r2 = c.fetchPendingMessages(conn, "jenny"); //当我拉取了joe的未读信息后 就会删除msgs:1里面的信息 //为什么想明白了么 ListChatMessages r3 = c.fetchPendingMessages(conn, "joe"); System.out.println("They are the same " + r1.equals(r2)); System.out.println("Those messages are:"); for(ChatMessages chat : r1){ System.out.println(" chatId: " + chat.chatId); System.out.println(" messages:"); for(MapString,Object message : chat.messages){ System.out.println(" " + message); } } System.out.println("看看还有没"); messageFromBase=conn.zrangeWithScores("msgs:"+chatId, 0, -1); iterator=messageFromBase.iterator(); while(iterator.hasNext()){ Tuple tuple=iterator.next(); System.out.println(tuple.getElement()+" -- "+tuple.getScore()); } conn.del("ids:chat:", "msgs:1", "ids:1", "seen:joe", "seen:jeff", "seen:jenny"); } } 

在线接受音讯接口:

  protected void Login( object sender, EventArgs e ){string user =
txtUsername.Text;if( !ValidateNick( user ) ) return;if(
Global.Engine.UserExists( user ) ){lblErrorMsg.Text = “A user with this
” +”name already exists, try again.”return;}Response.Redirect(
“Server.aspx?action=Login&u=” + user );}

化解了,大家不要紧把代码复制一份,自身看看 上边包车型大巴是测量试验的结果

复制代码

  举行部分认证现在,客户会被转变到另二个页面,这几个页面会利用 AddUser
函数将客商放入客户列表。当那全部都办好了。客户又会被转正到 Chat.aspx
页面,上边之 JavaScript 函数就要实践在此个页面上:
 
<script type=”text/javascript”>sniffBrowserType();//Shows
loading.. screenshowLoadScreen();//Set the javascript timer and //loads
user list and messages
setTimers();setFocus(‘mytext’);</script><input type=”text”
class=”mytext”id=”mytext” onkeydown=”captureReturn(event)”>

test before

/*!

@method

@brief 收到消息时的回调

@param message      音讯对象

@discussion 当EMConversation对象的enableReceiveMessage属性为YES时,
会触发此回调

本着有附属类小零器件的音讯, 那时候附属类小零器件尚未被下载.

附属类小零部件下载进程中的进程回调请参考didFetchingMessageAttachments:progress:,

下载完全数附属类小零部件后,
回调didMessageAttachmentsStatusChanged:error:会被触发

*/

– (void)didReceiveMessage:(EMMessage *)message;

  当客户输入了文字,况兼按了回车。上面之代码就能够被实践:

—– testMultiRecipientMessaging —–Let’s create a new chat session
with some recipients…Now let’s send a few messages…

2. 会话Conversation

  // Capture the enter key on the input box and post messagefunction
captureReturn( event ){if(event.which || event.keyCode){if ((event.which
== 13) || (event.keyCode == 13)){postText();return false;}else {return
true;}}}function postText(){rnd++;//Clear text box firstchatbox =
getElement( “mytext” );chat = chatbox.value;chatbox.value = “”//get user
GUID from urluserid = location.search.substring( 1,
location.search.length );//construct Ajax Server URLurl =
‘Server.aspx?action=PostMsg&u=’ + userid + ‘&t=’
+encodeURIComponent(chat) + ‘&session=’ + rnd;//Create and set the
instance //of appropriate XMLHTTP Request objectreq = getAjax();//Update
page with new messagereq.onreadystatechange = function(){if(
req.readyState == 4 && req.status == 200 ) {updateAll();}}req.open(
‘GET’, url, true );req.send( null );}

会见音讯库{“sender”:”joe”,”id”:1,”message”:”message
1″,”ts”:1477276890018} -- 1.0{“sender”:”joe”,”id”:2,”message”:”message
2″,”ts”:1477276890113} -- 2.0{“sender”:”joe”,”id”:3,”message”:”message
3″,”ts”:1477276890115} -- 3.0{“sender”:”joe”,”id”:4,”message”:”message
4″,”ts”:1477276890116} -- 4.0And let’s get the messages that are
waiting for jeff and jenny…要去除的 tuple:jenny–0.0要刨除的
tuple:joe–0.0要刨除的 tuple:jeff–4.0They are the same trueThose
messages are: chatId: 1 messages: {sender=joe, id=1, message=message 1,
ts=1.477276890018E12} {sender=joe, id=2, message=message 2,
ts=1.477276890113E12} {sender=joe, id=3, message=message 3,
ts=1.477276890115E12} {sender=joe, id=4, message=message 4,
ts=1.477276890116E12}看看还恐怕有没

对话是操作闲聊音信EMMessage的容器,也正是说大家得以经过会话赢得闲谈记录。对应的数据布局是EMConversation。

  告竣!正是这一个了。没什么特别之之方,下载示例程序,然后通晓那一个代码!

以上就是本文的全体内容,希望对大家的学习抱有助于,也愿意大家多多点拨脚本之家。

SDK中对应会话的操作,包含创建,删除,获取单个也许获得拥有会话等操作。下边是创制和8001的对话示例代码:

复制代码

EMConversation *conversation = [[EaseMob
sharedInstance].chatManager
conversationForChatter:@”8001″conversationType:eConversationTypeChat];

3. 群组Group

群组分为两大类(公开群组与个人群组),并依照诚邀形式更为细分为四小类。

群组唯有四个owner,为其创立者。有人口的界定。数据布局为E名爵roup。

操作包含:创制,查询,参与,退出,解散,黑名单管理等操作。

4. 聊天室Chatroom

相对于群组,谈天室比较容易些。环信官方网址关于“谈心室”模型的印证如下:

进入闲聊页面此前,实行踏入谈天室操作;

成功进去闲聊室之后,服务器会活动给推10条新闻;

离开谈天页面之后,进行抽离聊天室操作;

谈心室成立者owner能够开展分离闲聊室操作;

支撑最大成员5000;

环信的闲聊室内只有owner和游客;

不补助客商端营造谈心室;

不协理客商端特邀;

不支持REST邀请;

谈天室内成员离线后,服务器当监听到此成员不在线后不在会给此成员再发推送。

顾客端操作蕴涵:获取闲谈室,获取闲谈室详细情况,只怕成员,加入和间隔闲谈室等。

5. 基友BuddyList及黑名单BlockedList

据此一旦未在黑名单上,使用环信能够给任什么人发起闲聊,不管是或不是患难之交。