在iOS上构建一个使用node.js服务器的聊天应用程序

5

我正在尝试构建一个使用node.js和socket.io的iPhone(本地)聊天应用程序。

在IOS上创建聊天应用程序的最佳方法是什么?

是否有任何方法可以在IOS中使用node.js服务器创建聊天应用程序?

有人能给我建议吗?

感谢您的建议。


纯粹是为了参考。考虑只使用PubNub(或者可能是Parse.com + PubNub)。你可以用几行代码完成所有操作。 - Fattie
@ Joe Blow:有没有关于使用Parse.com和PubNub的示例? - btmanikandan
嗨@btmanikandan,你在吗? - Fattie
3个回答

6

当然,您可以使用Socket.io在iOS/Android和HTML中创建聊天应用程序!

有两种方法可以实现:

i)使用Socket.io实现自己的套接字通信(这很困难,因为您需要自己编写大部分网络实现!)

Socket.io将作为流接口,您需要从iOS连接它!

您可以参考iOS开发指南,了解如何实现流!

https://developer.apple.com/library/ios/documentation/NetworkingInternet/Conceptual/NetworkingTopics/Articles/UsingSocketsandSocketStreams.html

这里有一个正在讨论相关内容的活跃线程!

iPhone Objective-C使用Socket.IO进行套接字通信

ii) 使用已经存在的iOS库或包装器,你可以在下面找到链接!

这个库主要完成网络部分,你只需要实现你的应用逻辑即可!

iOS

https://github.com/pkyeck/socket.IO-objc

安卓

https://github.com/nkzawa/socket.io-client.java

最好先从库开始,然后尝试制作自己的实现!:))


https://github.com/shuoli84/SocketIOCocoa 是一个用于 socketio 1.0 的 Swift 客户端。 - Shuo

3
我建议以下步骤:
  1. 使用node.js和NOSQL数据库(例如我的例子中的CouchDb)开发HTML5应用程序。
  2. 在您第一次测试时,不要使用socket.io,因为它比较复杂。您必须先非常了解websockets。

不要负担过多预先准备的框架和大量代码。您需要清晰、简洁的代码以便于您日后进行所需的修改。您需要完全掌握和了解您的代码。

我已经附上了一个工作样例(感谢Ales Smodis)。注意:为了使下面的示例正常工作,您需要安装2个node.js包:

npm install websocket
npm install nano 

您需要创建数据库并将至少1个用户插入CouchDb数据库:

curl -X PUT http://localhost:5984/chatroom
curl -X PUT -H 'Content-Type: application/json' --data '{"username":"john","password":"john"}' http://localhost:5984/chatroom/john

$(document).ready(function () {

    var connection,
        username = 'Tester',
        password = 'Tester',
        historyCounter = 0,
        members = {},
        // displays
        jqLogin = $('#login'),
        jqChatroom = $('#chatroom'),
        // login display components
        jqWaiting = $('#waiting'),
        jqNickname = $('#nickname'),
        jqPassword = $('#password'),
        // chat display components
        jqHistory = $('#history'),
        jqMembers = $('#members'),
        jqLine = $('#line');

    function addLine(nick, line) {
        var jq = $('<p><span class="nick"></span><span class="line"></span></p>'),
            jqNick = jq.find('.nick'),
            jqLine = jq.find('.line'),
            i, lines;
        jqNick.text(nick ? nick + ': ' : '*** ');
        jqLine.text(line);
        jqHistory.append(jq);
        historyCounter++;
        for (lines = jqHistory.children(), i = 0; historyCounter > 100; i++, historyCounter--) {
            $(lines[i]).remove();
        }
    }

    function addMsg(msgObj) {
        var msgHandler = states[activeState].messageHandlers[msgObj.type] || function () { addLine(null, 'Neveljaven paket tipa ' + msgObj.type); };
        msgHandler(msgObj);
    }

    function clearMembers() {
        var nickname;
        for (nickname in members) {
            members[nickname].remove();
            delete members[nickname];
        }
        jqMembers.empty(); // just in case
    }

    function addMember(nickname) {
        var jq = $('<li></li>');
        jq.text(nickname);
        jqMembers.append(jq);
        members[nickname] = jq;
    }

    function removeMember(nickname) {
        if (nickname in members) {
            members[nickname].remove();
            delete members[nickname];
        }
    }

    function connect () {
        connection = new WebSocket('ws://127.0.0.1:8080');

        connection.onopen = function () {
            states[activeState].onopen();
        };

        connection.onmessage = function (message) {
            try {
                addMsg(JSON.parse(message.data));
            }
            catch (e) {
                addLine(null, 'Exception while handling a server message: ' + e.toString());
            }
        };

        connection.onclose = function () {
            states[activeState].onclose();
        };
    }

    function loginKeypress(event) {
        if (13 !== event.keyCode) return;
        username = jqNickname.val();
        password = jqPassword.val();
        if (!username) jqNickname[0].focus();
        else if (!password) jqPassword[0].focus();
        else {
            jqWaiting.css('display', '');
            jqNickname.unbind('keydown', loginKeypress);
            jqPassword.unbind('keydown', loginKeypress);
            connect();
        }
    }

    function inputKeypress(event) {
        var line;
        if (13 === event.keyCode) {
            line = jqLine.val();
            if (line.length === 0) return;
            jqLine.val('');
            connection.send(JSON.stringify({ 'type': 'line', 'line': line }));
        }
    }

    var states = {
            'login': {
                'start': function () {
                    jqChatroom.css('display', 'none');
                    jqWaiting.css('display', 'none');
                    jqLogin.css('display', '');
                    jqNickname.val('');
                    jqPassword.val('');
                    jqNickname[0].focus();
                    activeState = 'login';
                    jqNickname.bind('keydown', loginKeypress);
                    jqPassword.bind('keydown', loginKeypress);
                },
                'onopen': function () {
                    connection.send(JSON.stringify({ 'type': 'login', 'username': username, 'password': password }));
                },
                'messageHandlers': {
                    'state': function (msgObj) {
                        var i, history, users;
                        states.chat.start();
                        history = msgObj.history;
                        jqHistory.empty();
                        historyCounter = 0;
                        for (i = 0; i < history.length; i++) addMsg(history[i]);
                        users = msgObj.users;
                        clearMembers();
                        for (i = 0; i < users.length; i++) addMember(users[i]);
                    }
                },
                'unhandledMessage': function (msgObj) {
                    connection.close(4000, 'Unhandled message type');
                },
                'onclose': function () {
                    states.login.start();
                }
            },

            'chat': {
                'start': function () {
                    jqLogin.css('display', 'none');
                    jqWaiting.css('display', 'none');
                    jqChatroom.css('display', '');
                    jqHistory.empty();
                    historyCounter = 0;
                    activeState = 'chat';
                    jqLine.bind('keydown', inputKeypress);
                },
                'onopen': function () {
                    connection.close(4001, 'Connection opened while chatting');
                },
                'messageHandlers': {
                    'line': function (msgObj) {
                        addLine(msgObj.nick, msgObj.line);
                    },
                    'join': function (msgObj) {
                        addLine(null, 'Priklopil: ' + msgObj.nick);
                        addMember(msgObj.nick);
                    },
                    'leave': function (msgObj) {
                        addLine(null, 'Odklopil: ' + msgObj.nick);
                        removeMember(msgObj.nick);
                    }
                },
                'unhandledMessage': function (msgObj) {
                    connection.close(4000, 'Unhandled message type');
                },
                'onclose': function () {
                    addLine(null, 'Connection closed');
                    jqLine.unbind('keydown', inputKeypress);
                }
            }
        },
        activeState = 'login';

    states.login.start();
});
// node.js code
var http = require('http'),
    url = require('url'),
    path = require('path'),
    fs = require('fs'),
    nano = require('nano')('http://localhost:5984'),
    chatroomDb = nano.use('chatroom'),
    websocket = require('websocket'),
    chatHistory = [],
    activeUsers = {};

var filesDir = path.join(process.cwd(), 'web');

var mimeTypes = {
    '.html': 'text/html',
    '.css': 'text/css',
    '.js': 'text/javascript'
};

var getContentType = function (extension) {
    var mimeType = mimeTypes[extension];
    return mimeType ? mimeType : 'application/octet-stream';
};

var server = http.createServer(function (request, response) {
    var relativePath = url.parse(request.url).pathname,
        absolutePath = path.join(filesDir, relativePath);
    var handler = function (err, stats) {
        if (stats) {
            if (stats.isDirectory()) {
                absolutePath = path.join(absolutePath, 'index.html');
                fs.stat(absolutePath, handler);
                return;
            }
            if (stats.isFile()) {
                response.writeHead(200, getContentType(path.extname(absolutePath)));
                var stream = fs.createReadStream(absolutePath);
                stream.pipe(response);
                return;
            }
        }
        response.writeHead(404, {'Content-Type': 'text/plain'});
        response.write('Not found\r\n');
        response.end();
    };
    console.log('HTTP request for ' + relativePath);
    fs.stat(absolutePath, handler);
});
server.listen(8080, function () {});

wsServer = new websocket.server({
    'httpServer': server
});

function addLine (type, nick, line) {
    var msg = { 'type': type, 'nick': nick, 'line': line },
        jsonMsg = JSON.stringify(msg),
        username;
    chatHistory.push(msg);
    while (chatHistory.length > 100) chatHistory.shift();
    for (username in activeUsers) {
        activeUsers[username].sendMessage(jsonMsg);
    }
}

wsServer.on('request', function (request) {
    console.log('New websocket connection from ' + request.origin);
    // TODO: verify that request.origin is our web site

    var connection = request.accept(null, request.origin);

    var username = null;

    connection.on('message', function (message) {
        if (message.type !== 'utf8') {
            console.log('Refusing a non-utf8 message');
            return;
        }
        console.log('Processing message: ' + message.utf8Data);
        try {
            var m = JSON.parse(message.utf8Data);
            switch (m.type) {

                case 'login':
                    chatroomDb.get(m.username, function (err, body) {
                        if (err || (body.password !== m.password)) {
                            connection.close();
                            return;
                        }
                        username = m.username;
                        addLine('join', username, null);
                        activeUsers[username] = {
                            'sendMessage': function (jsonMsg) {
                                connection.sendUTF(jsonMsg);
                            }
                        };
                        var users = [], u;
                        for (u in activeUsers) users.push(u);
                        connection.sendUTF(JSON.stringify({ 'type': 'state', 'history': chatHistory, 'users': users }));
                    });
                    break;

                case 'line':
                    if (!username) {
                        connection.close();
                        break;
                    }
                    addLine('line', username, m.line);
                    break;
            }
        }
        catch (e) {
            console.log(e);
        }
    });

    connection.on('close', function (connection) {
        console.log('Connection closed');
        if (username) {
            delete activeUsers[username];
            addLine('leave', username, null);
        }
    });
});

console.log('Server running');
<!DOCTYPE html>
<html>
    <head>
        <meta charset="utf-8">
        <title>Chatroom</title>
      <style>
        html, body {
    width: 100%;
    height: 100%;
    padding: 0;
    border: none;
    margin: 0;
}

#heading {
    position: absolute;
    top: 0;
    left: 0;
    right: 0;
    height: 30px;
    margin: 0;
    padding: 0;
    line-height: 30px;
    text-align: center;
    font-size: 20px;
    background-color: green;
}

#outer {
    position: absolute;
    top: 30px;
    bottom: 0;
    left: 0;
    right: 0;
    margin: 20px;
    min-height: 400px;
    min-width: 400px;
    background-color: lime;
}

#inner {
    height: 100%;
    background-color: #ffc0cb;
}

#chat {
    position: absolute;
    top: 0;
    left: 0;
    right: 200px;
    bottom: 0;
    background-color: #ffd700;
}

#members {
    position: absolute;
    top: 0;
    right: 10px;
    width: 180px;
    bottom: 0;
    background-color: #ff00ff;
    list-style-type: none;
    padding: 10px;
    padding: 0;
    border: none;
}

#history {
    position: absolute;
    top: 0;
    left: 0;
    bottom: 2em;
    right: 0;
    background-color: #00ffff;
    padding: 10px;
}

#input {
    position: absolute;
    height: 2em;
    left: 0;
    right: 0;
    bottom: 0;
    background-color: #90ee90;
    line-height: 2em;
}

#line {
    width: 100%;
    margin: 0;
    border: none;
    padding: 0;
}

#history > p {
    margin: 2px;
}

.nick {
    white-space: pre;
    display: table-cell;
}

.line {
    display: table-cell;
}

#login {
    height: 100%;
    display: table;
    margin: 0 auto;
}
#login > .svg {
    vertical-align: middle;
    display: table-cell;
}
#login-wrapper1 {
    display: table;
    height: 100%;
    width: 100%;
}
#login-wrapper2 {
    display: table-cell;
    vertical-align: middle;
}
#login-table {
    margin: 0 auto;
}
#login-table .label {
    text-align: right;
}

#waiting {
    position: fixed;
    top: 0;
    bottom: 0;
    left: 0;
    right: 0;
    opacity: 0.3;
}

       </style> 
        <script src="https://ajax.googleapis.com/ajax/libs/jquery/1.8.3/jquery.min.js"></script>
        <script src="client.js"></script>
    </head>
    <body>
        <div id="login">
            <div class="svg">

                <svg
                    xmlns="http://www.w3.org/2000/svg"
                    xmlns:xlink="http://www.w3.org/1999/xlink"
                    viewBox="0 0 400 400"
                    width="400"
                    height="400">
                    <defs>
                        <path id="curved-text" d="M0,0 c 50 -50 150 -50 200 0" />
                    </defs>
                    <g>
                        <text
                            transform="translate(-100,40)"
                            font-weight="bold"
                            font-variant="small-caps"
                            font-family="Arial sans-serif"
                            font-size="30"
                            fill="none"
                            stroke="orange"
                            text-anchor="middle">
                            <textPath xlink:href="#curved-text" startOffset="50%">Chatroom</textPath>
                        </text>
                        <animateTransform attributeName="transform" attributeType="XML" type="rotate" from="0" to="360" dur="5s" fill="remove" additive="sum" repeatCount="indefinite" />
                        <animateMotion dur="10s" repeatCount="indefinite" path="M100,200 a100,100 0 1 1 200,0 a100,100 0 1 1 -200,0" />
                    </g>
                    <foreignObject
                        x="0"
                        y="0"
                        width="400"
                        height="400"
                        style="height:400px;">

                        <div xmlns="http://www.w3.org/1999/xhtml" id="login-wrapper1">
                            <div id="login-wrapper2">
                                <table id="login-table">
                                    <tbody>
                                        <tr>
                                            <td class="label">Vzdevek:</td><td><input type="text" id="nickname" /></td>
                                        </tr>
                                        <tr>
                                            <td class="label">Geslo:</td><td><input type="password" id="password" /></td>
                                        </tr>
                                    </tbody>
                                </table>
                            </div>
                        </div>

                    </foreignObject>
                </svg>

            </div>
        </div>

        <div id="chatroom" style="display:none;">
            <h1 id="heading">Chatroom</h1>

            <div id="outer">
                <div id="inner">
                    <div id="chat">
                        <div id="history">
                            <p><span class="nick">Matej: </span><span class="line">Hi</span></p>
                            <p><span class="nick">Borut: </span><span class="line">How are you?</span></p>
                            <p><span class="nick">Martina: </span><span class="line">Ok, thanks!</span></p>
                        </div>
                        <div id="input"><input id="line" type="text"></div>
                    </div>
                    <ul id="members">
                        <li>Matej</li>
                        <li>Borut</li>
                        <li>Martina</li>
                    </ul>
                </div>
            </div>
        </div>

        <div id="waiting" style="display:none;"></div>

    </body>
</html>


0
如果您正在评估选项,请查看Twilio的IP消息传递:

https://www.twilio.com/docs/tutorials/walkthrough/ip-chat/ios/swift#0

上面的教程是关于Swift for iOS的(也适用于Objective-C),它允许您使用本地SDK,而服务器端应用程序(yours in Node.js)生成用户访问令牌以连接到API。

代码将引导您加入频道、创建频道和发送消息。

注意:我为Twilio工作。这个快速示例应用程序至少可以帮助您确定哪些功能最适合您。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接