abstrak:第一步, 實(shí)現(xiàn)通用的send()和receive()函數(shù):send函數(shù)定義通過cPicle.dumps()將需要發(fā)送的數(shù)據(jù)序列化,然后通過socket.htonl()方法將序列化后的數(shù)據(jù)長(zhǎng)度轉(zhuǎn)化為網(wǎng)絡(luò)字節(jié)序格式,以便于底層傳輸,再將網(wǎng)絡(luò)字節(jié)序格式的長(zhǎng)度打包為'L'類型的C struct, 最后發(fā)送打包后的長(zhǎng)度以及序列化后的數(shù)據(jù)receive函數(shù)即是send反向過程,先接收到打包后的
第一步, 實(shí)現(xiàn)通用的send()和receive()函數(shù):
send函數(shù)定義通過cPicle.dumps()將需要發(fā)送的數(shù)據(jù)序列化,然后通過socket.htonl()方法將序列化后的數(shù)據(jù)長(zhǎng)度轉(zhuǎn)化為網(wǎng)絡(luò)字節(jié)序格式,以便于底層傳輸,再將網(wǎng)絡(luò)字節(jié)序格式的長(zhǎng)度打包為'L'類型的C struct, 最后發(fā)送打包后的長(zhǎng)度以及序列化后的數(shù)據(jù)
receive函數(shù)即是send反向過程,先接收到打包后的長(zhǎng)度,將其解包,然后再主機(jī)序列化,所有數(shù)據(jù)接收完成以后,返回解除序列化后的原始數(shù)據(jù)。
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)
第二步,創(chuàng)建ChatServer類:
聊天室服務(wù)器需要能做到: 1,記錄連接數(shù) 2,記錄連接的客戶端地址以及名稱映射 ,需要時(shí)返回名稱地址 3,重用地址 4,檢測(cè)鍵盤中斷 5,處理輸入及請(qǐng)求
先實(shí)現(xiàn)1,2,3,4點(diǎn):
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點(diǎn)處理輸入及請(qǐng)求分幾種情況,1處理客戶端接入并通知其他客戶端,2處理客戶端輸入信息并轉(zhuǎn)發(fā)給其他客戶端,處理標(biāo)準(zhǔn)輸入, 這里丟棄,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()
第三步, 實(shí)現(xiàn)客戶端類。主要處理輸入與從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方法主要處理標(biāo)準(zhǔn)輸入,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