要去搞懂,XMPP群聊的话,是涉及到哪些知识
是如何发送和接收消息的。
据说是发送消息给:
即可使得改该组内所有的人都能收到消息了。
swiftXMPP room chat
swiftXMPP multiple chat
XMPP multiple chat
Why is group chat such an awkward thing in XMPP? | Ignite Realtime
7.4 Sending a Message to All OccupantsAn occupant sends a message to all other occupants in the room by sending a message of type "groupchat" to the <room@service> itself (a service MAY ignore or reject messages that do not have a type of "groupchat"). In a moderated room, this privilege is restricted to occupants with a role of participant or higher. Example 44. Occupant Sends a Message to All Occupants <message from=‘[email protected]/pda’ id=‘hysf1v37’ to=‘[email protected]’ type=‘groupchat’> <body>Harpier cries: ’tis time, ’tis time.</body> </message> |
然后发送的内容为:
<message type="groupchat" to="[email protected]"><body>To all</body></message> |
结果出错:
<message from="[email protected]" to="[email protected]/842f0050" type="error" xmlns="jabber:client"> <body>To all users</body> <error code="406" type="modify"><not-acceptable xmlns="urn:ietf:params:xml:ns:xmpp-stanzas"/></error> </message> |
搜:
xmpp message <error code="406" type="modify"><not-acceptable
xmpp message error code 406 type modify not-acceptable
xmpp聊天室发送群消息收不到 – CocoaChina移动版
[EJAB-595] Incorrect error message when user sends forbidden private message – ProcessOne – Support
how to send group (MUC) chat from a component | ejabberd
MUC not-acceptable message | Ignite Realtime
objective c – XMPPFramework – Sending Group Chat Message in iOS Failed – Stack Overflow
说是先要告诉聊天室,你在线:
发送XMPPPresence给对应的room
但是如何把XMPPPresence发送给一个聊天室,不知道。。。
www.psyced.org/dist/world/default/en/jabber.textdb
Urgent help needed to fix error code=’404′ in MUC
GauravDS’: XMPP Chat Connection ChatConnection
参考:
Entering a Room - XEP-0045: Multi-User Chat
“
Entering a Room
7.2.1 Groupchat 1.0 Protocol
In order to participate in the discussions held in a multi-user chat room, a user MUST first become an occupant by entering the room. In the old groupchat 1.0 protocol, this was done by sending presence with no ‘type’ attribute to <room@service/nick>, where "room" is the room ID, "service" is the hostname of the chat service, and "nick" is the user’s desired nickname within the room:
Example 18. User Seeks to Enter a Room (groupchat 1.0)
<presence<br /> from='[email protected]/pda'<br /> id='ng91xs69'<br /> to='[email protected]/thirdwitch'/><br />
In this example, a user with a full JID of "[email protected]/pda" has requested to enter the room "coven" on the "chat.shakespeare.lit" chat service with a room nickname of "thirdwitch".
If the user does not specify a room nickname (note the bare JID on the ‘from’ address in the following example), the service MUST return a <jid-malformed/> error:
Example 19. No Nickname Specified
<presence<br /> from='[email protected]'<br /> id='273hs51g'<br /> to='[email protected]/pda'<br /> type='error'><br /> <error by='[email protected]' type='modify'><br /> <jid-malformed xmlns='urn:ietf:params:xml:ns:xmpp-stanzas'/><br /> </error><br /></presence><br />
Note: The presence stanza used to join a room MUST NOT possess a ‘type’ attribute, i.e., it must be available presence. For further discussion, see the Presence business rules.
7.2.2 Basic MUC Protocol
Compliant multi-user chat services MUST accept the foregoing as a request to enter a room from any client that knows either the groupchat 1.0 protocol or the multi-user chat (MUC) protocol; however, MUC clients SHOULD signal their ability to speak the MUC protocol by including in the initial presence stanza an empty <x/> element qualified by the ‘http://jabber.org/protocol/muc‘ namespace (note the absence of the ‘#user’ fragment):
Example 20. User Seeks to Enter a Room (Multi-User Chat)
<presence<br /> from='[email protected]/pda'<br /> id='n13mt3l'<br /> to='[email protected]/thirdwitch'><br /> <x xmlns='http://jabber.org/protocol/muc'/><br /></presence><br />
Before attempting to enter the room, a MUC-compliant client SHOULD first discover its reserved room nickname (if any) by following the protocol defined in the Discovering Reserved Room Nickname section of this document.
"
去加入room
所以接着就是:
想办法找到XMPPFramework中如何发送Presence
SwiftXMPP MUC XMPPPresence
Stanzas: Presence · fritzy/SleekXMPP Wiki · GitHub
ios – MUC How-to with XMPPFramework – Stack Overflow
结果去发送Presence,对应的用户用:
[email protected]/测试用户2 print("enterRoomPresence=\(enterRoomPresence)") //<presence from="[email protected]/测试用户2" to="[email protected]"/> |
结果出错:
<presence from="[email protected]" to="[email protected]/de8f83ee" type="error" xmlns="jabber:client"> <error code="400" type="modify"><jid-malformed xmlns="urn:ietf:params:xml:ns:xmpp-stanzas"/></error> </presence> |
很明显是:
nickname有误。
去改为:
let userJidWithNickName = curUserFullJid + "/" + "fakeNickName_TestUser1_OnSwiftXMPP" |
结果:
错误依旧。
后来才知道:
原来是此处的服务器openfire端,在设计的时候,强制了nickname就是userId。。。
【已解决】XMPP出错:code 400 jid-malformed xmlns urn:ietf:params:xml:ns:xmpp-stanzas
但是也只是实现了:
发送presence给group,表示进入房间room了
接着还要去实现群发消息:
[总结]
最后是个人user发送给room==group:
<message from="[email protected]/user-1f4857d8-5ea8-434e-8e7c-3f8d20f1f2cf" to="[email protected]" type="groupchat"><body>Crifan user2 send to group a11838ca</body></message> |
然后即可收到:
发送成功时的didSendMessage,user发送给group的:
<message from="[email protected]/user-1f4857d8-5ea8-434e-8e7c-3f8d20f1f2cf" to="[email protected]" type="groupchat"><body>Crifan user2 send to group a11838ca</body></message> |
然后didReceiveMessage中,group也会发回给自己该消息的:
<message xmlns="jabber:client" from="[email protected]/user-1f4857d8-5ea8-434e-8e7c-3f8d20f1f2cf" to="[email protected]/b9378e86" type="groupchat"><body>Crifan user2 send to group a11838ca</body></message> |
即可成功实现消息发送给聊天室==room==group==组
然后同理,也可以实现,其他XMPP客户端(Mac下的Adium)发送消息到聊天室中时,当前代码(对应的iOS模拟器)中也可以收到
对应的消息是:
didReceiveMessage中收到的:
<message xmlns="jabber:client" type="groupchat" id="purple87418a29" to="[email protected]/b9378e86" from="[email protected]/user-09b740d4-45f9-499a-8c84-0ac707a115f5"><body>从Mac的XMPP客户端Adium中发送给聊天室a11838ca</body><html xmlns="http://jabber.org/protocol/xhtml-im"><body><p><span style="font-family: PingFang SC; font-size: medium;">从</span>Mac的XMPP客户端Adium中发送给聊天室a11838ca</p></body></html></message> |
如此,即可实现,双向的,发送和接收聊天室的消息,实现群组聊天了。
对应的为:
加入群组聊天之前,先要进入房间,表明自己在线:
if newConversationItem.msgTVC.contactItem.type == ContactType.Group { //if room, should enter into room first before chat //send presence to room let enterRoomPresence:XMPPPresence = XMPPPresence() let curUserFullJid = gCurrentLoginedUserId + "@" + StrServerDomain print("curUserFullJid=\(curUserFullJid)") //[email protected] enterRoomPresence.addAttributeWithName("from", stringValue: curUserFullJid) let roomFullJid = newConversationItem.msgTVC.contactItem.id + "@" + StrServerConferenceDomain print("roomFullJid=\(roomFullJid)") //group-7f1ad5cd-8f18-4c5e-9fe4-604c3e5b1ab8@conference.jiandao.im let roomJidWithName = roomFullJid + "/" + gCurrentLoginedUserId print("roomJidWithName=\(roomJidWithName)") enterRoomPresence.addAttributeWithName("to", stringValue: roomJidWithName) //DDXMLElement let xXmlnsElement:DDXMLElement = DDXMLElement(name: "x", xmlns: "http://jabber.org/protocol/muc") print("xXmlnsElement=\(xXmlnsElement)") //<x xmlns="http://jabber.org/protocol/muc"/> enterRoomPresence.addChild(xXmlnsElement) print("enterRoomPresence=\(enterRoomPresence)") /* Mac Adium enter the Room: <presence from="[email protected]/licrifandeMacBook-Pro" to="group-26e98e34-3f49-4914-8acd-34354bf23b03@conference.jiandao.im/user-59e68fc0-39c3-4fb3-afef-7a7244d0e87d"><c hash="sha-1" node="http://pidgin.im/" ver="DdnydQG7RGhP9E3k9Sf+b+bF0zo=" xmlns="http://jabber.org/protocol/caps"/> <x xmlns="http://jabber.org/protocol/muc"><history since="2015-12-04T03:14:47Z"/></x> </presence> */ //<presence from="[email protected]" to="group-a11838ca-a741-4d2d-ad5c-477c87400ad0@conference.jiandao.im/user-1f4857d8-5ea8-434e-8e7c-3f8d20f1f2cf"><x xmlns="http://jabber.org/protocol/muc"/></presence> xmppStream().sendElement(enterRoomPresence) } |
供参考。
之前再去发送消息:
func sendMessage(){ print("sendMessage") let messageStrToSend:String? = inputMessageTextField.text print("messageStrToSend=\(messageStrToSend)") if (messageStrToSend != nil) && (messageStrToSend?.lengthOfBytesUsingEncoding(NSUTF8StringEncoding) > 0) { let messageXmlElement:DDXMLElement = DDXMLElement.elementWithName("message") as! DDXMLElement let bodyXmlElement:DDXMLElement = DDXMLElement.elementWithName("body") as! DDXMLElement bodyXmlElement.setStringValue(messageStrToSend!) messageXmlElement.addChild(bodyXmlElement) let fromJid:String = gCurrentLoginedUserId + "@" + StrServerDomain print("fromJid=\(fromJid)") //[email protected] let fromJidWithNickname:String = fromJid + "/" + gCurrentLoginedUserId //[email protected]/user-1f4857d8-5ea8-434e-8e7c-3f8d20f1f2cf messageXmlElement.addAttributeWithName("from", stringValue: fromJidWithNickname) var chatTypeStr:String = "chat" //id is user/group/topic let sendToId:String = contactItem.id print("sendToId=\(sendToId)") //group-7f1ad5cd-8f18-4c5e-9fe4-604c3e5b1ab8 //TODO: maybe need append resource for full JID? var sentToFullJidStr:String = "" if contactItem.type == ContactType.Person { sentToFullJidStr = sendToId + "@" + StrServerDomain }else if contactItem.type == ContactType.Group { //group-c7b68ce4-bbe1-430d-b736-dd7eec6c4413@conference.jiandao.im sentToFullJidStr = sendToId + "@" + StrServerConferenceDomain chatTypeStr = "groupchat" }else if contactItem.type == ContactType.Topic{ print("TODO: add support topic") } print("sentToFullJidStr=\(sentToFullJidStr)") //group-7f1ad5cd-8f18-4c5e-9fe4-604c3e5b1ab8@conference.jiandao.im messageXmlElement.addAttributeWithName("to", stringValue: sentToFullJidStr) messageXmlElement.addAttributeWithName("type", stringValue: chatTypeStr) xmppStream().sendElement(messageXmlElement) print("messageXmlElement=\(messageXmlElement)") //<message type="chat" to="[email protected]"><body>From 2</body></message> //<message from="[email protected]" to="[email protected]" type="groupchat"><body>To all</body></message> inputMessageTextField.text = "" } } |
服务器会返回给你对应的Presence回应,表明你的身份是参与者:
//when receive friend become online func xmppStream(sender: XMPPStream?, didReceivePresence presence: XMPPPresence?) { print("didReceivePresence") print("presence=\(presence)") //<presence xmlns="jabber:client" from="[email protected]/444cbc62" to="[email protected]/444cbc62"/> //<presence xmlns="jabber:client" from="[email protected]/licrifandeMacBook-Pro" to="[email protected]/444cbc62"><c xmlns="http://jabber.org/protocol/caps" node="http://pidgin.im/" hash="sha-1" ver="DdnydQG7RGhP9E3k9Sf+b+bF0zo="/><x xmlns="vcard-temp:x:update"><photo>aa6e432351442c73d99ca484cd91abc96b1c7bed</photo></x></presence> /* <presence from="[email protected]" to="[email protected]/de8f83ee" type="error" xmlns="jabber:client"> <error code="400" type="modify"><jid-malformed xmlns="urn:ietf:params:xml:ns:xmpp-stanzas"/></error> </presence> */ //<presence xmlns="jabber:client" from="group-a11838ca-a741-4d2d-ad5c-477c87400ad0@conference.jiandao.im/user-1f4857d8-5ea8-434e-8e7c-3f8d20f1f2cf" to="[email protected]/96593ac5"><x xmlns="http://jabber.org/protocol/muc#user"><item jid="[email protected]/96593ac5" affiliation="member" role="participant"/></x></presence> let presenceType:String? = presence?.type() print("presenceType=\(presenceType)") //Optional("available") let prensenceSenderUsername:String? = sender?.myJID.user print("prensenceSenderUsername=\(prensenceSenderUsername)") //Optional("user-7aa7e3c3-bf8f-46e1-8cbe-3a5aad6e9dfb") let presenceFromUsername:String? = presence?.from().user print("presenceFromUsername=\(presenceFromUsername)") //Optional("user-7aa7e3c3-bf8f-46e1-8cbe-3a5aad6e9dfb") //TODO: later add process when use online or offline // if chatDelegate != nil { // if presenceFromUser != myUsername { // if presenceType == "available" { // chatDelegate?.newBuddyOnLine("\(presenceFromUser)" + "@" + "\(loginServer)") // } else if presenceType == "unavailable" { // chatDelegate?.buddyWentOffline("\(presenceFromUser)" + "@" + "\(loginServer)") // } // } // } } |
对应的代理delegate会去处理:
发送了消息时:
//when send new message to friend func xmppStream(sender: XMPPStream!, didSendMessage message: XMPPMessage!) { print("didSendMessage") print("message=\(message)") // if let msgBody: String = message.elementForName("body")?.stringValue() { if let msgTo: String = message.attributeForName("to")?.stringValue() { var sentMessage:Message sentMessage = Message() sentMessage.bubbleMessageType = BubbleMessageType.Sent sentMessage.bubbleMessageMediaType = BubbleMessageMediaType.Text sentMessage.text = msgBody //TODO: update to use real sender header image or optimize to fixed user header image after first init //sentMessage.avatar = UIImage(named:"hdImg_wutao_48x48.jpg")! sentMessage.displayTimestamp = true sentMessage.timestamp = NSDate() // sentMessage.timestampStr = sentMessage.timestamp!.toString("yyyy/MM/dd HH:mm:ss") sentMessage.timestampStr = sentMessage.timestamp.toString("HH:mm:ss") sentMessage.displayUsername = false //TODO: get real display name from here msgFrom==userId //[email protected] let toJidInfo:JidInfo = extarctJidInfoFromFullJid(msgTo) print("toJidInfo=\(toJidInfo)") sentMessage.toJidInfo = toJidInfo if let msgFrom = message.attributeForName("from")?.stringValue() { let fromJidInfo:JidInfo = extarctJidInfoFromFullJid(msgFrom) print("fromJidInfo=\(fromJidInfo)") sentMessage.fromJidInfo = fromJidInfo } if messageDelegate != nil { messageDelegate!.newMessageSent(sentMessage) } } } else { return } } |
接受到了消息时:
//when receive new message from friend // func xmppStream(sender: XMPPStream?, didReceiveMessage: XMPPMessage?) { func xmppStream(sender: XMPPStream, didReceiveMessage message: XMPPMessage) { print("didReceiveMessage") print("message: \(message)") //<message xmlns="jabber:client" type="chat" id="purple534f8ea6" to="[email protected]/f42314b" from="[email protected]/licrifandeMacBook-Pro"><body>from Adium to SwiftXMPP</body></message> /* <message from="[email protected]" id="[email protected][email protected]__R39L5" to="[email protected]" xmlns="jabber:client"> <event xmlns="http://jabber.org/protocol/pubsub#event"> <items node="urn:xmpp:avatar:data"> <item id="aa6e432351442c73d99ca484cd91abc96b1c7bed" node="urn:xmpp:avatar:data"> <data xmlns="urn:xmpp:avatar:data">iVBORw0……………………RU5ErkJggg==</data> </item> </items> </event> <delay from="192.168.1.110" stamp="2015-11-28T02:08:15.270Z" xmlns="urn:xmpp:delay"/> <x from="192.168.1.110" stamp="20151128T02:08:15" xmlns="jabber:x:delay"/> </message> */ //<message xmlns="jabber:client" type="chat" id="purplef9f56336" to="[email protected]" from="[email protected]/licrifandeMacBook-Pro"><active xmlns="http://jabber.org/protocol/chatstates"/><body>send to jiandao</body></message> /* <message from="[email protected]" to="[email protected]/76710702" type="error" xmlns="jabber:client"> <body>To all</body> <error code="406" type="modify"> <not-acceptable xmlns="urn:ietf:params:xml:ns:xmpp-stanzas"/> </error> </message> */ //<message xmlns="jabber:client" type="chat" id="purplecaaefd2b" to="[email protected]" from="[email protected]/licrifandeMacBook-Pro"><active xmlns="http://jabber.org/protocol/chatstates"/><body>222</body></message> //<message xmlns="jabber:client" from="group-c7b68ce4-bbe1-430d-b736-dd7eec6c4413@conference.jiandao.im/user-1f4857d8-5ea8-434e-8e7c-3f8d20f1f2cf" to="[email protected]/101f462f" type="groupchat"><body>To all</body></message> //received history groupchat message //<message xmlns="jabber:client" type="groupchat" id="purpleca4e1aca" to="[email protected]/3693760" from="group-a11838ca-a741-4d2d-ad5c-477c87400ad0@conference.jiandao.im/user-09b740d4-45f9-499a-8c84-0ac707a115f5"><body>Crifan Send to Server Via Adium</body><delay xmlns="urn:xmpp:delay" stamp="2015-12-05T03:49:56.298Z" from="group-a11838ca-a741-4d2d-ad5c-477c87400ad0@conference.jiandao.im/user-09b740d4-45f9-499a-8c84-0ac707a115f5"/><x xmlns="jabber:x:delay" stamp="20151205T03:49:56" from="group-a11838ca-a741-4d2d-ad5c-477c87400ad0@conference.jiandao.im/user-09b740d4-45f9-499a-8c84-0ac707a115f5"/></message> if let msgBody: String = message.elementForName("body")?.stringValue() { if let msgFrom: String = message.attributeForName("from")?.stringValue() { var receivedMessage:Message receivedMessage = Message()receivedMessage.bubbleMessageType = BubbleMessageType.Received receivedMessage.bubbleMessageMediaType = BubbleMessageMediaType.Text receivedMessage.text = msgBody //TODO: update to use real sender header image or optimize to fixed user header image after first init // newMessage.avatar = UIImage(named:"hdImg_wutao_48x48.jpg")! receivedMessage.displayTimestamp = true receivedMessage.timestamp = NSDate() // newMessage.timestampStr = newMessage.timestamp!.toString("yyyy/MM/dd HH:mm:ss") receivedMessage.timestampStr = receivedMessage.timestamp.toString("HH:mm:ss") receivedMessage.displayUsername = true //TODO: get real display name from here msgFrom==userId //[email protected]/licrifandeMacBook-Pro //group-7f1ad5cd-8f18-4c5e-9fe4-604c3e5b1ab8@conference.jiandao.im let fromJidInfo:JidInfo = extarctJidInfoFromFullJid(msgFrom) print("fromJidInfo=\(fromJidInfo)") receivedMessage.fromJidInfo = fromJidInfo if let msgTo = message.attributeForName("to")?.stringValue() { let toJidInfo:JidInfo = extarctJidInfoFromFullJid(msgTo) print("toJidInfo=\(toJidInfo)") receivedMessage.toJidInfo = toJidInfo } if messageDelegate != nil { messageDelegate!.newMessageReceived(receivedMessage) } } } else { return } } |
转载请注明:在路上 » [已解决]Swift中实现XMPP的讨论组群聊功能