python websockets 網絡聊天室V0

  • 實現效果

undefined

  • 使用方式
  1. 將前後端分別保存爲html和py文件。

  2. pip install asyncio、json、websockets

  3. 雙擊打開html

*在一臺電腦開兩個網頁的確有點傻,您可以修改代碼ip,再將py文件在電腦運行。可達到局域網通信

  • 後端
#!/usr/bin/env python
#後端
# WS server example that synchronizes state across clients

import asyncio
import json
import websockets

USERS = {}

async def notify_state(content, name):
    if len(USERS) != 0:  # asyncio.wait doesn't accept an empty list
        message = json.dumps(
            {"type": "user", "content": content, "from": name})
        await asyncio.wait([user.send(message) for user in USERS.values()])


async def plus_users(name, websocket):
    USERS[name] = websocket
    if len(USERS) != 0:  # asyncio.wait doesn't accept an empty list
        message = json.dumps(
            {"type": "login", "content": name, "user_list": list(USERS.keys())})
        await asyncio.wait([user.send(message) for user in USERS.values()])


async def minus_users(name):
    del USERS[name]
    if len(USERS) != 0:  # asyncio.wait doesn't accept an empty list
        message = json.dumps(
            {"type": "logout", "content": name, "user_list": list(USERS.keys())})
        await asyncio.wait([user.send(message) for user in USERS.values()])


async def register(websocket):
    # 握手
    await websocket.send(json.dumps({"type": "handshake"}))


async def unregister(websocket):
    for k, v in USERS.items():
        if v == websocket:
            del USERS[k]
            await minus_users(k)


async def chat(websocket, path):
    # register(websocket) sends user_event() to websocket
    await register(websocket)
    try:
        async for message in websocket:
            data = json.loads(message)
            print(data)
            if data["type"] == "login":
                await plus_users(data["content"], websocket)
            elif data["type"] == "send":
                await notify_state(data["content"], data["uname"])
            else:
                pass
    finally:
        await unregister(websocket)


start_server = websockets.serve(chat, "localhost", 5678)

asyncio.get_event_loop().run_until_complete(start_server)
asyncio.get_event_loop().run_forever()

  • 前端
<!DOCTYPE html>
<html>

<head>
    <title></title>
    <meta http-equiv="content-type" content="text/html;charset=utf-8">
    <style>
        p {
            text-align: left;
            padding-left: 20px;
        }
    </style>
</head>

<body>
    <div style="width: 800px;height: 600px;margin: 30px auto;text-align: center">
        <h1>websocket聊天室</h1>
        <div style="width: 800px;border: 1px solid gray;height: 300px;">
            <div style="width: 200px;height: 300px;float: left;text-align: left;">
                <p><span>當前在線:</span><span id="user_num">0</span></p>
                <div id="user_list" style="overflow: auto;">

                </div>
            </div>
            <div id="msg_list" style="width: 598px;border:  1px solid gray; height: 300px;overflow: scroll;float: left;">
            </div>
        </div>
        <br>
        <textarea id="msg_box" rows="6" cols="50" onkeydown="confirm(event)"></textarea><br>
        <input type="button" value="發送" onclick="send()">
    </div>
</body>

</html>

<script type="text/javascript">
    // 存儲用戶名到全局變量,握手成功後發送給服務器
    var uname = prompt('請輸入用戶名', 'user' + uuid(8, 16));
    var ws = new WebSocket("ws://127.0.0.1:5678");
    ws.onopen = function() {
        var data = "系統消息:建立連接成功";
        listMsg(data);
    };

    /**
     * 分析服務器返回信息
     *
     * msg.type : user 普通信息;system 系統信息;handshake 握手信息;login 登陸信息; logout 退出信息;
     * msg.from : 消息來源
     * msg.content: 消息內容
     */
    ws.onmessage = function(e) {
        console.log(e)
        var msg = JSON.parse(e.data);
        var sender, user_name, name_list, change_type;

        switch (msg.type) {
            case 'system':
                sender = '系統消息: ';
                break;
            case 'user':
                sender = msg.from + ': ';
                break;
            case 'handshake':
                var user_info = {
                    'type': 'login',
                    'content': uname
                };
                sendMsg(user_info);
                return;
            case 'login':
            case 'logout':
                user_name = msg.content;
                name_list = msg.user_list;
                change_type = msg.type;
                dealUser(user_name, change_type, name_list);
                return;
        }

        var data = sender + msg.content;
        listMsg(data);
    };

    ws.onerror = function() {
        var data = "系統消息 : 出錯了,請退出重試.";
        listMsg(data);
    };

    /**
     * 在輸入框內按下回車鍵時發送消息
     *
     * @param event
     *
     * @returns {boolean}
     */
    function confirm(event) {
        var key_num = event.keyCode;
        if (13 == key_num) {
            send();
        } else {
            return false;
        }
    }

    /**
     * 發送並清空消息輸入框內的消息
     */
    function send() {
        var msg_box = document.getElementById("msg_box");
        var content = msg_box.value;
        var reg = new RegExp("\r\n", "g");
        content = content.replace(reg, "");
        var msg = {
            'content': content.trim(),
            'type': 'send',
            'uname': uname
        };
        sendMsg(msg);
        msg_box.value = '';
        // todo 清除換行符
    }

    /**
     * 將消息內容添加到輸出框中,並將滾動條滾動到最下方
     */
    function listMsg(data) {
        var msg_list = document.getElementById("msg_list");
        var msg = document.createElement("p");

        msg.innerHTML = data;
        msg_list.appendChild(msg);
        msg_list.scrollTop = msg_list.scrollHeight;
    }

    /**
     * 處理用戶登陸消息
     *
     * @param user_name 用戶名
     * @param type  login/logout
     * @param name_list 用戶列表
     */
    function dealUser(user_name, type, name_list) {
        var user_list = document.getElementById("user_list");
        var user_num = document.getElementById("user_num");
        while (user_list.hasChildNodes()) {
            user_list.removeChild(user_list.firstChild);
        }

        for (var index in name_list) {
            var user = document.createElement("p");
            user.innerHTML = name_list[index];
            user_list.appendChild(user);
        }
        user_num.innerHTML = name_list.length;
        user_list.scrollTop = user_list.scrollHeight;

        var change = type == 'login' ? '上線' : '下線';

        var data = '系統消息: ' + user_name + ' 已' + change;
        listMsg(data);
    }

    /**
     * 將數據轉爲json併發送
     * @param msg
     */
    function sendMsg(msg) {
        var data = JSON.stringify(msg);
        ws.send(data);
    }

    /**
     * 生產一個全局唯一ID作爲用戶名的默認值;
     *
     * @param len
     * @param radix
     * @returns {string}
     */
    function uuid(len, radix) {
        var chars = '0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz'.split('');
        var uuid = [],
            i;
        radix = radix || chars.length;

        if (len) {
            for (i = 0; i < len; i++) uuid[i] = chars[0 | Math.random() * radix];
        } else {
            var r;

            uuid[8] = uuid[13] = uuid[18] = uuid[23] = '-';
            uuid[14] = '4';

            for (i = 0; i < 36; i++) {
                if (!uuid[i]) {
                    r = 0 | Math.random() * 16;
                    uuid[i] = chars[(i == 19) ? (r & 0x3) | 0x8 : r];
                }
            }
        }

        return uuid.join('');
    }
</script>
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章