基于Html5 websocket和Python的在线聊天室

一、什么是WebSocket API
     WebSocket API是下一代客户端-服务器的异步通信方法。该通信取代了单个的TCP套接字,使用ws或wss协议,可用于任意的客户端和服务器程序。WebSocket目前由W3C进行标准化。WebSocket已经受到Firefox 4、Chrome 4、Opera 10.70以及Safari 5等浏览器的支持。
     WebSocket API最伟大之处在于服务器和客户端可以在给定的时间范围内的任意时刻,相互推送信息。WebSocket并不限于以Ajax(或XHR)方式通信,因为Ajax技术需要客户端发起请求,而WebSocket服务器和客户端可以彼此相互推送信息;XHR受到域的限制,而WebSocket允许跨域通信。

二、WebSocket协议
     websocket的协议是很简单的,这里我把它分成客户端和服务端来讲。在客户端,new WebSocket即可实例化一个新的websocket对象,但其参数略微有一点不一样,参数格式是这样的ws://yourdomain:port/path ,WebSocket对象会自动解析这段字符串,发送到指定服务器端口,首先执行的是双方握手(handshake),客户端发送数据格式类似这样:
GET /chat HTTP/1.1
Upgrade: WebSocket
Connection: Upgrade
Host: www.zendstudio.net:9108
Origin: http://www.zendstudio.net
Cookie: somenterCookie
这很是有些类似于http的头信息,同样每行都是以”\r\n”结尾的,上面这段格式无需我们去构造,WebSocket对象会自动发送,对客户端这是透明的。此时服务端应该返回的信息是:
HTTP/1.1 101 Web Socket Protocol Handshake
Upgrade: WebSocket
Connection: Upgrade
WebSocket-Origin: http://www.zendstudio.net
WebSocket-Location: ws://www.zendstudio.net:9108/chat
从这里我们太容易看出来,websocket协议的握手部分根本就是个类http的协议,所不同的是http每次都会有这样子的头信息交互,这在某些时候不得不显得很糟糕。而websocket只会执行一次这个过程,之后的传输信息就变得异常简洁了。

握手协议:request中有三个随机的key值,头部有两个,后面body里是长度为8字节的key3(括号里的文字是提示,还有字符间的冒号也是为了看上去清晰才加上的,真正传输是没有的),以此向server发送一个challenge,server需要根据这三个key计算出一个token,在响应中发回给client,以证明自己对request的正常解读。计算方法是这样的:对于key1,抽取其中的数字字符,形成一个整数num,然后除以他自身的空格数spaces,保留整数部分i1; key2如法炮制,得到i2,把i1和i2按照big-endian字符序连接起来,然后再与key3连接,得到一个初始的序列,对这个序列使用md5计算出一个16字节长的摘要,就是所需的token。另外值得注意的是Origin头部,意味着Websocket是支持cross origin的。

三、客户端client.html
<html>
<head>
<title>WebSocket</title>
 
<style>
 html,body{font:normal 0.9em arial,helvetica;}
 #log {width:440px; height:200px; border:1px solid #7F9DB9; overflow:auto;}
 #msg {width:330px;}
</style>
 
<script>
var socket;
 
function init(){
  var host = "ws://10.3.18.105:19887/";
  try{
    socket = new WebSocket(host);
    socket.onopen    = function(msg){ ; };
    socket.onmessage = function(msg){ log(msg.data); };
    socket.onclose   = function(msg){ log("Lose Connection!"); };
  }
  catch(ex){ log(ex); }
  $("msg").focus();
}
 
function send(){
  var txt,msg;
  txt = $("msg");
  msg = txt.value;
  if(!msg){ alert("Message can not be empty"); return; }
  txt.value="";
  txt.focus();
  try{ socket.send(msg); } catch(ex){ log(ex); }
}
 
window.οnbefοreunlοad=function(){
    try{
        socket.send('quit');
        socket.close();
        socket=null;
    }
    catch(ex){
        log(ex);
    }
};
 
 
function $(id){ return document.getElementById(id); }
function log(msg){ $("log").innerHTML+="<br>"+msg; }
function onkey(event){ if(event.keyCode==13){ send(); } }
</script>
 
</head>


<body οnlοad="init()">
 <h3>WebSocket</h3>
 <br><br>
 <div id="log"></div>
 <input id="msg" type="textbox" οnkeypress="onkey(event)"/>
 <button οnclick="send()">发送</button>
</body>

</html>

四、服务器端server.py
import socket
import struct
import hashlib
import threading,random

connectionlist = {}

def sendMessage(message):
    global connectionlist
    for connection in connectionlist.values():
        connection.send("\x00%s\xFF" %message)

def deleteconnection(item):
    global connectionlist
    del connectionlist['connection'+item]

class WebSocket(threading.Thread):
    def __init__(self, conn, index, name, remote, path="/"):
        threading.Thread.__init__(self)
        self.conn = conn
        self.index = index
        self.name = name
        self.remote = remote
        self.path = path
        self.buffer = ""

    def run(self):
        print 'Socket %s Start!' %self.index
        headers = {}
        self.handshaken = False

        while True:
            if self.handshaken == False:
                print 'Socket %s Start Handshaken with %s!' %(self.index, self.remote)
                self.buffer += self.conn.recv(1024)
                if self.buffer.find('\r\n\r\n') != -1: 
                    header, data = self.buffer.split('\r\n\r\n', 1)
                    for line in header.split("\r\n")[1:]:
                        key, value = line.split(": ", 1)
                        headers[key] = value

                    headers["Location"] = "ws://%s%s" %(headers["Host"], self.path)
                    print headers    
    
                    key1 = headers["Sec-WebSocket-Key1"]
                    key2 = headers["Sec-WebSocket-Key2"]
                    if len(data) < 8:
                        data += self.conn.recv(8-len(data))
                    key3 = data[:8]
                    self.buffer = data[8:]
                    token = self.generate_token(key1, key2, key3)

                    handshake = '\
HTTP/1.1 101 Web Socket Protocol Handshake\r\n\
Upgrade: WebSocket\r\n\
Connection: Upgrade\r\n\
Sec-WebSocket-Origin: %s\r\n\
Sec-WebSocket-Location: %s\r\n\r\n\
' %(headers['Origin'], headers['Location'])

                    self.conn.send(handshake + token)
                    self.handshaken = True
                    print 'Socket %s Handshaken with %s success!' %(self.index, self.remote)
                    sendMessage('Welcome, ' + self.name + ' !')

                else:
                    self.buffer += self.conn.recv(64)
                    if self.buffer.find("\xFF") != -1:
                        s = self.buffer.split("\xFF")[0][1:]
                        if s == 'quit':
                            print 'Socket %s Logout !' %(self.index)
                            sendMessage(self.name + ' Logout')
                            deleteconnection(str(self.index))
                            self.conn.close()
                            break
                        else:
                            print 'Socket %s Got msg: %s from %s!' %(self.index,s,self.remote)
                            sendMessage(self.name + ':' + s)
                        self.buffer = ""

    def generate_token(self, key1, key2, key3):
        num1=int("".join([digit for digit in list(key1) if digit.isdigit()]))
        spaces1 = len([char for char in list(key1) if char == " "])
        num2 = int("".join([digit for digit in list(key2) if digit.isdigit()]))
        spaces2 = len([char for char in list(key2) if char == " "])

        combined = struct.pack(">II", num1/spaces1, num2/spaces2) + key3
        return hashlib.md5(combined).digest()



class WebSocketServer(object):
    def __init__(self):
        self.socket = None
    def begin(self):
        print "WebSocketSerber Start!"
        self.socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
        self.socket.bind(("10.3.18.105", 19887))
        self.socket.listen(50)

        global connectionlist

        i = 0
        while True:
            connection, address = self.socket.accept()
            username = address[0]

            newSocket = WebSocket(connection, i, username, address)
            newSocket.start()
            conectionlist['connection'+str(i)] = connection
            i = i + 1


if __name__ == "__main__":
    server = WebSocketServer()
    server.begin()
  • 2
    点赞
  • 12
    收藏
    觉得还不错? 一键收藏
  • 8
    评论
### 回答1: 好的,这是一段基于PythonWebSocket聊天室代码。 ``` import asyncio import websockets connected = set() async def chat_handler(websocket, path): # 将连接的客户端加入connected set中 connected.add(websocket) try: async for message in websocket: # 遍历所有已连接的客户端 for client in connected: # 如果不是当前客户端,将消息发送给其他客户端 if client != websocket: await client.send(message) finally: # 断开连接后,从connected set中移除该客户端 connected.remove(websocket) # 启动服务器 start_server = websockets.serve(chat_handler, 'localhost', 8765) asyncio.get_event_loop().run_until_complete(start_server) asyncio.get_event_loop().run_forever() ``` 这段代码实现了一个简单的WebSocket聊天室,允许多个客户端连接并在它们之间进行消息传递。 ### 回答2: 基于PythonWebSocket聊天室可以使用第三方库`websockets`来实现。首先,需要安装该库,可以使用`pip`进行安装。 ``` pip install websockets ``` 接下来,我们可以创建一个服务器端和一个客户端,服务器端用于接收和发送消息,客户端用于与服务器进行通信。以下是一个简单的示例代码: 服务器端(`server.py`): ```python import asyncio import websockets # 存储所有连接的客户端 clients = set() # 处理客户端的消息 async def handle_message(message, sender): # 将消息发送给所有客户端 for client in clients: if client != sender: await client.send(message) # 处理客户端连接 async def handle_client(websocket, path): # 添加客户端到列表 clients.add(websocket) try: # 接收客户端消息并处理 async for message in websocket: await handle_message(message, websocket) finally: # 客户端断开连接时移除 clients.remove(websocket) # 启动服务器 start_server = websockets.serve(handle_client, "localhost", 8765) asyncio.get_event_loop().run_until_complete(start_server) asyncio.get_event_loop().run_forever() ``` 客户端(`client.py`): ```python import asyncio import websockets async def handle_client(): # 与服务器建立连接 async with websockets.connect('ws://localhost:8765') as websocket: # 接收和发送消息 while True: message = await websocket.recv() print(f"收到消息:{message}") user_input = input("请输入消息:") await websocket.send(user_input) # 启动客户端 asyncio.get_event_loop().run_until_complete(handle_client()) ``` 你可以在终端分别运行服务器端和多个客户端实例(同时运行多个终端窗口),各客户端之间就可以通过服务器进行消息的发送和接收了。每当一个客户端发送消息时,服务器会将此消息发送给其他所有客户端。 请注意,此示例代码仅为基础的实现方式,还可以进一步扩展和改进,例如添加身份验证、房间管理等功能。 ### 回答3: 当然可以帮您写一段基于PythonWebSocket聊天室。首先,您需要安装`websockets`库,它是一个用于编写WebSocket服务器和客户端的Python库。 以下是一个简单的WebSocket聊天室示例: ```python import asyncio import websockets # 保存连接的客户端 clients = set() # 接收来自客户端的消息并广播给所有连接的客户端 async def receive_message(websocket, path): try: async for message in websocket: await broadcast(message) finally: await unregister(websocket) # 广播消息给所有连接的客户端 async def broadcast(message): if clients: await asyncio.wait([client.send(message) for client in clients]) # 注册新的客户端 async def register(websocket): clients.add(websocket) # 取消注册客户端 async def unregister(websocket): clients.remove(websocket) # 运行WebSocket服务器 start_server = websockets.serve(receive_message, 'localhost', 5000) asyncio.get_event_loop().run_until_complete(start_server) asyncio.get_event_loop().run_forever() ``` 这段代码首先导入了所需的模块,然后定义了一个`clients`集合,用于保存连接的客户端。 `receive_message`函数用于接收来自客户端的消息,并调用`broadcast`函数广播消息给所有连接的客户端。 `broadcast`函数会遍历`clients`集合,向每个客户端发送消息。 `register`和`unregister`函数用于注册和取消注册客户端。 最后,通过`websockets.serve`函数创建WebSocket服务器,并通过`asyncio`进行事件循环。 这只是一个简单的聊天室示例,您可以根据需要进行修改和扩展。注意,此示例仅支持本地主机上的连接,如果要在网络上进行测试,需要修改`start_server`中的主机IP地址和端口。
评论 8
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值