# coding: utf-8
from socket import *
import json, time, threading
import struct
from urllib import unquote
config = {
'HOST': 'localhost',
'PORT': 11011,
'LISTEN_CLIENT': 50,
'KEY': '391f10fadc339e9ec5fa15af60030ac1',
'SIZE': 2048,
'TIME_OUT': 1000,
'HEART_TIME': 5,
'MAGIC_STRING': '258EAFA5-E914-47DA-95CA-C5AB0DC85B11',
'HANDSHAKE_STRING': "HTTP/1.1 101 Switching Protocols\r\n"
"Upgrade:websocket\r\n"
"Connection: Upgrade\r\n"
"Sec-WebSocket-Accept: {1}\r\n"
"WebSocket-Location: ws://{2}/chat\r\n"
"WebSocket-Protocol:chat\r\n\r\n"
}
class Server():
"""
服务端基类
"""
def __init__(self):
self.sock = socket(AF_INET, SOCK_STREAM)
self.sock.bind((config['HOST'], config['PORT'])) # 监听端口
self.sock.listen(config['LISTEN_CLIENT']) # 监听客户端数量
# 所有监听的客户端
self.clients = {}
self.thrs = {}
self.users = {}
self.stops = []
print u"\n正在监听端口..."
# 监听客户端连接
def listen_client(self):
while 1:
# 循环监听
tcpClientSock,addr = self.sock.accept()
address = addr[0] + ':' + str(addr[1]) # ip:port
# 握手
topInfo = tcpClientSock.recv(1024)
headers = {}
#print u'\n服务端发送的信息:', address
if not topInfo:
tcpClientSock.close()
continue
header, data = topInfo.split('\r\n\r\n', 1)
try:
getInfo = header.split('\r\n')[0].split(' ')[1].split('/')[1:]
if getInfo[0] == 'name':
self.users[address] = str(getInfo[1])
else:
self.users[address] = '匿名用户'
except:
self.users[address] = '匿名用户'
for line in header.split('\r\n')[1:]:
key, val = line.split(': ', 1)
headers[key] = val
if 'Sec-WebSocket-Key' not in headers:
tcpClientSock.close()
continue
import hashlib, base64
sec_key = headers['Sec-WebSocket-Key']
res_key = base64.b64encode(hashlib.sha1(sec_key + config['MAGIC_STRING']).digest())
str_handshake = config['HANDSHAKE_STRING'].replace('{1}', res_key).replace('{2}', config['HOST'] + ':' + str(config['PORT']))
tcpClientSock.send(str_handshake)
try:
username = unquote(self.users[address])
except:
username = '匿名用户'
# 握手成功 分配线程进行监听
print u'\n用户: ',username+' 进入了房间',"IP地址为:",address
self.clients[address] = tcpClientSock
self.thrs[address] = threading.Thread(target=self.readMsg, args=[address])
self.thrs[address].start()
#print u"\n:",self.clients
def readMsg(self, address):
#print u"\n:",self.clients
if address not in self.clients:
return False
client = self.clients[address]
import select
time_out = 0
try:
username = unquote(self.users[address])
except:
username = '匿名用户'
while 1:
# print(len(self.clients))
if address in self.stops:
self.close_client(address)
print u"\n",username + "已经离开了系统!IP:",address
break
# 检测超时
if time_out >= config['TIME_OUT']:
self.close_client(address)
break
time_out += 5
infds, outfds, errfds = select.select([client, ], [], [], 5)
if len(infds) == 0:
continue
time_out = 0
try:
info = client.recv(1024)
except:
self.close_client(address)
break
if not info:
continue
if info == 'quit':
self.close_client(address)
break
code_len = ord(info[1]) & 127
if code_len == 126:
masks = info[4:8]
data = info[8:]
elif code_len == 127:
masks = info[10:14]
data = info[14:]
else:
masks = info[2:6]
data = info[6:]
i = 0
raw_str = ""
for d in data:
# print(masks, masks[i % 4])
raw_str += chr(ord(d) ^ ord(masks[i % 4]))
# print(raw_str)
i += 1
# 获取到输入的数据 向所有的客户端发送
# 开启线程记录
if raw_str:
t1 = threading.Thread(target=self.send_data, args=[raw_str, address])
t1.start()
def send_data(self, data, address):
#import struct
#from urllib import unquote
try:
username = unquote(self.users[address])
except:
username = '匿名用户'
if data:
data = str('【'+username+' 说】:'+data)
print u"\n服务端发送的信息为:",data
else:
return False
token = "\x81"
length = len(data)
if length < 126:
token += struct.pack("B", length)
elif length <= 0xFFFF:
token += struct.pack("!BH", 126, length)
else:
token += struct.pack("!BQ", 127, length)
# struct为Python中处理二进制数的模块,二进制流为C,或网络流的形式。
data = '%s%s' % (token, data)
try:
for key, val in self.clients.iteritems():
client = val
try:
client.send(data)
except:
self.close_client(key)
except:
pass
def close_client(self, address):
try:
username = unquote(self.users[address])
except:
username = '匿名用户'
try:
client = self.clients.pop(address)
self.stops.append(address)
client.close()
del self.users[address]
except:
pass
print u"\n用户:",username +"已经退出,IP为:",address
if __name__ == '__main__':
c = Server()
c.listen_client()
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>WebSocket</title>
<script src="js/jquery.js" type="text/javascript"></script>
<style>
* {
font-family: "微软雅黑";
margin: 0;
padding: 0;
}
html, body {
width: 100%;
height: 100%;
background: #000;
position: relative;
}
.msgInfo {
position: fixed;
right: 2%;
top: 2%;
height: 96%;
width: 20%;
background: #666;
}
.msgInfo .hd {
width: 100%;
text-align: center;
height: 40px;
line-height: 40px;
color: #fff;
background: #333;
}
.msgInfo .bd {
padding: 20px;
height: 100%;
overflow: auto;
color: #fff;
line-height: 30px;
}
.msgInfo .bd p {
margin: 5px 0;
}
.sendBox {
position: fixed;
left: 2%;
bottom: 2%;
width: 74%;
height: 10%;
background: #666;
overflow: hidden;
}
.sendBox textarea {
width: 90%;
height: 100%;
display: inline-block;
background: #666;
color: #fff;
border: none;
float: left;
}
.sendBox button {
width: 10%;
height: 100%;
display: inline-block;
background: #333;
cursor: pointer;
color: #fff;
border: none;
float: left;
}
.renBox {
margin-left: 2%;
margin-top: 2%;
width: 74%;
height: 84%;
position: relative;
overflow: hidden;
float: left;
}
.renBox .ren {
display: inline-block;
position: absolute;
left: 500px;
top: 500px;
}
.renBox .ren .renHead {
display: inline-block;
width: 20px;
height: 20px;
border-radius: 10px;
background: #fff;
}
.renBox .ren .sayInfo {
color: #fff;
position: absolute;
top: -30px;
width: 200px;
left: -90px;
text-align: center;
}
.renBox div {
color: #fff;
margin-left: 100%;
width: 100%;
overflow: hidden;
height: 30px;
line-height: 30px;
font-size: 20px;
}
.firstStep {
position: fixed;
width: 100%;
height: 100%;
z-index: 999;
background: #000;
text-align: center;
}
.firstStep .firstName {
display: inline-block;
padding-top: 300px;
}
.firstStep .firstName .username {
width: 300px;
height: 40px;
border: 0;
padding: 0 10px;
}
.firstStep .firstName .btn {
width: 100px;
height: 40px;
border: 0;
background: #333;
color: #fff;
cursor: pointer;
}
</style>
<script>
var socket;
function init(username) {
var host = "ws://localhost:11011/name/" + username;
try {
socket = new WebSocket(host);
socket.onopen = function(msg) {
log('您已经进入聊天室')
};
socket.onmessage = function(msg) {
log(msg.data);
};
socket.onclose = function(msg) {
log("与服务器连接断开");
};
} catch (ex) {
log(ex);
}
$(".sendInfo").focus();
}
function send() {
var txt, msg;
txt = $(".sendInfo");
msg = txt.val();
if (!msg) {
alert("发送消息不能为空!");
return;
}
txt.val('');
txt.focus();
try {
socket.send(msg);
$('.sayInfo').html(msg)
// log("我说:"+msg);
} catch (ex) {
log(ex);
}
}
window.onbeforeunload = function() {
try {
socket.send('quit');
socket.close();
socket = null;
} catch (ex) {
log(ex);
}
};
function nameok() {
var _name = $('input[name="username"]').val();
if (!_name) {
alert('请给自己取个名字吧')
} else {
$('.firstStep').remove()
init(_name)
}
}
function log(msg) {
$('.msgInfo .bd').append('<p>' + msg + '</p>');
//动画
var _html = $('<div>', {
'class' : 'showMsg'
});
_html.html(msg);
$('.renBox').append(_html);
_html.animate({
'marginLeft' : '-100%'
}, 10000, function() {
_html.remove()
})
}
function show(obj) {
obj.fadeIn()
}
function onkey(event) {
if (event.keyCode == 13) {
send();
}
}
</script>
<!--<link href="/static/css/css.css" type="text/css" rel="stylesheet" /> -->
</head>
<body>
<div class="renBox">
<!--<div class="ren">-->
<!--<span class="sayInfo"></span>-->
<!--<span class="renHead"></span>-->
<!--</div>-->
</div>
<div class="msgInfo">
<div class="hd">聊天记录</div>
<div class="bd"></div>
</div>
<div class="sendBox">
<textarea class="sendInfo"></textarea>
<button οnclick="send()">发送</button>
</div>
</body>
<div class="firstStep">
<div class="firstName">
<input type="text" class="username" name="username"
placeholder="给自己取一个响亮的名字!" /><input type="button" class="btn"
οnclick="nameok()" value="进入聊天" />
</div>
</div>
</html>