摘要:第一步, 实现通用的send()和receive()函数:send函数定义通过cPicle.dumps()将需要发送的数据序列化,然后通过socket.htonl()方法将序列化后的数据长度转化为网络字节序格式,以便于底层传输,再将网络字节序格式的长度打包为'L'类型的C struct, 最后发送打包后的长度以及序列化后的数据receive函数即是send反向过程,先接收到打包后的
第一步, 实现通用的send()和receive()函数:
send函数定义通过cPicle.dumps()将需要发送的数据序列化,然后通过socket.htonl()方法将序列化后的数据长度转化为网络字节序格式,以便于底层传输,再将网络字节序格式的长度打包为'L'类型的C struct, 最后发送打包后的长度以及序列化后的数据
receive函数即是send反向过程,先接收到打包后的长度,将其解包,然后再主机序列化,所有数据接收完成以后,返回解除序列化后的原始数据。
def send(channel, *args):
data = cPickle.dumps(*args)
htonl_size = socket.htonl(len(data))
size = struct.pack("L", htonl_size)
channel.send(size)
channel.send(data)
def receive(channel):
recv_size = struct.calsize("L")
data_size = channel.recv(recv_size) # receive size's value
try:
size = socket.ntohl(struct.unpack("L", data_size)[0])
except struct.error, e:
return ''
data = ''
while len(data) < data_size:
data = channel.recv(data_size - len(data))
return cPickle.loads(data)第二步,创建ChatServer类:
聊天室服务器需要能做到: 1,记录连接数 2,记录连接的客户端地址以及名称映射 ,需要时返回名称地址 3,重用地址 4,检测键盘中断 5,处理输入及请求
先实现1,2,3,4点:
class ChatServer(object):
def __init__(self, port, backlog=5):
self.clients = " # record client number
self.clientmap = {} # client address and name's mapping
self.outputs = [] # socket output objects' list
self.server = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
self.server.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
self.server.bind((SERVER_HOST, port)
self.server.listen(backlog)
signal.signal(signal.SIGINT, self.sighandler)
def sighandler(self, signum, frame):
print "Shutting down server..."
for output in outputs:
output.close(0)
self.server.close()
def get_client_name(self, client):
info = self.clientmap[client]
host, name = info[0][0], info[1]
return '@'.join((name, host))第5点处理输入及请求分几种情况,1处理客户端接入并通知其他客户端,2处理客户端输入信息并转发给其他客户端,处理标准输入, 这里丢弃,3处理异常
def run(self):
inputs = [self.server, sys.stdin] #inputs for select.select() first arg
self.outputs = [] # define outputs for select.select(), second arg
running = True
while running:
try:
readable, writeable, exceptional = \
select.selcet(inputs, self.outputs, [])
except select.error, e:
break
for sock in readable:
if sock == self.server:
client, address = self.server.accept()
print "Chat server: got connection %d from %s" % \
(client.fileno, address)
cname = receive(client).spilt('NAME: ')[1]
self.clients += 1
send(client, "CLIENT: " + str(address[0])
inputs.append(client)
self.clientmap[client] = (address, cname)
# send joining information to other clients
msg = '\n (Connected: New client (%d) from %s" % \
(self.clients, self.get_client_name(client))'
for output in self.outputs:
send(output, msg)
self.outputs.append(client)
elif sock == sys.stdin:
junk = sys.stdin.readable()
running = False
else:
try:
data = receive(sock)
if data:
msg = '\n#[' + self.get_client_name(sock) + \
']>>' + data
for output in self.outputs:
if output != sock:
send(output.msg)
else:
print "Chat server: %d hung up" % sock.fileno()
self.clients -= 1
sock.close()
inputs.remove(sock)
self.outputs.remove(sock)
msg = '\n(now hung up: client from %s)' % \
sef.get_client_name(sock)
for output in self.outputs:
send(output, msg)
except socket.error, e:
inputs.remove(sock)
self.outputs.remove(sock)
self.server.close() 第三步, 实现客户端类。主要处理输入与从server端返回消息的读取
class ChatClient(object):
def __init__(self, name, port, host=SERVER_HOST):
self.name = name
self.connected = False
self.host = host
self.port = port
self.prompt = "[" + \
'@'.join((name, socket.gethostname().spilt('.')[0])) + ']>'
try:
self.sock = socket.socket(socket.AF_INET, \ socket.SOCK_STREAM)
self.sock.connect((host, port))
print "Now connceted to chat server at port %d" % port
self.connected = True
send(self.sock, 'NAME: ' + self.name)
data = receive(self.sock)
addr = data.split('CLIENT: ')[1]
self.prompt = '[' + '@'.join((self.name, addr)) + ']>'
except socket.error, e:
print "Failed to connect to chat server @ port %d" % self.port
sys.exit(1)客户端类的run方法主要处理标准输入,server返回信息,以及异常:
def run(self): """client main loop""" while self.connected: try: sys.stdout.write(self.prompt) sys.stdout.flush() #wait for input from stdin or socket readable, writeable, exceptional = select.select([0, self.sock], [], []) for sock in readable: if sock == 0: data = sys.stdin.readline().strip() if data: send(self.sock, data) else sock == self.sock: data = receive(self.sock) if not data: print "Client shutting down" self.connected = False break else: sys.stdout.write(data + '\n') sys.stdout.flush() except KeyboardInterrupt, e: print " Client interrupted, " self.sock.close() break