最新消息:20210816 当前crifan.com域名已被污染,为防止失联,请关注(页面右下角的)公众号

[已解决]Swift中实现XMPP的讨论组群聊功能

Swift crifan 3679浏览 0评论

要去搞懂,XMPP群聊的话,是涉及到哪些知识

是如何发送和接收消息的。

据说是发送消息给:

[email protected]

即可使得改该组内所有的人都能收到消息了。

swiftXMPP room chat

swiftXMPP multiple chat

XMPP multiple chat

Multi-User Chat

Why is group chat such an awkward thing in XMPP? | Ignite Realtime

Multi User Chat

XEP-0045: Multi-User Chat

7.4 Sending a Message to All Occupants

An 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

Error "406 modify not-acceptable" when trying to send file via TurnSocket · Issue #159 · robbiehanson/XMPPFramework · GitHub

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)

&lt;presence<br />    from='[email protected]/pda'<br />    id='ng91xs69'<br />    to='[email protected]/thirdwitch'/&gt;<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

&lt;presence<br />    from='[email protected]'<br />    id='273hs51g'<br />    to='[email protected]/pda'<br />    type='error'&gt;<br />  &lt;error by='[email protected]' type='modify'&gt;<br />    &lt;jid-malformed xmlns='urn:ietf:params:xml:ns:xmpp-stanzas'/&gt;<br />  &lt;/error&gt;<br />&lt;/presence&gt;<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)

&lt;presence<br />    from='[email protected]/pda'<br />    id='n13mt3l'<br />    to='[email protected]/thirdwitch'&gt;<br />  &lt;x xmlns='http://jabber.org/protocol/muc'/&gt;<br />&lt;/presence&gt;<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了

接着还要去实现群发消息:

【已解决】尝试用XMPP去实现消息群发

[总结]

最后是个人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的讨论组群聊功能

发表我的评论
取消评论

表情

Hi,您需要填写昵称和邮箱!

  • 昵称 (必填)
  • 邮箱 (必填)
  • 网址
86 queries in 0.169 seconds, using 22.31MB memory