本文所示代碼將教你如何使用Python標(biāo)準(zhǔn)庫中的select.select模塊實現(xiàn)多路復(fù)用的命令行下CS模式的聊天室程序。
服務(wù)器端代碼:
#!/usr/bin/env python
""" A basic, multiclient 'chat server' using Python's select module with interrupt handling.
Entering any input at the terminal will exit the server. """
import select import socket import sys import signal from communication import send, receive
BUFSIZ = 4096
class ChatServer(object): """ Simple chat server using select """ def __init__(self, port=3490, backlog=5): self.clients = 0 # Client map self.clientmap = {} # Output socket list self.outputs = [] self.server = socket.socket(socket.AF_INET, socket.SOCK_STREAM) self.server.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) self.server.bind(('',port)) print 'Listening to port',port,'...' self.server.listen(backlog) # Trap keyboard interrupts signal.signal(signal.SIGINT, self.sighandler) def sighandler(self, signum, frame): # Close the server print 'Shutting down server...' # Close existing client sockets for o in self.outputs: o.close() self.server.close()
def getname(self, client):
# Return the printable name of the # client, given its socket... info = self.clientmap[client] host, name = info[0][0], info[1] return '@'.join((name, host)) def serve(self): inputs = [self.server,sys.stdin] self.outputs = []
running = 1
while running:
try: inputready,outputready,exceptready = select.select(inputs, self.outputs, []) except select.error, e: break except socket.error, e: break
for s in inputready:
if s == self.server: # handle the server socket client, address = self.server.accept() print 'chatserver: got connection %d from %s' % (client.fileno(), address) # Read the login name cname = receive(client).split('NAME: ')[1] # Compute client name and send back 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.getname(client)) for o in self.outputs: # o.send(msg) send(o, msg) self.outputs.append(client)
elif s == sys.stdin: # handle standard input junk = sys.stdin.readline() running = 0 else: # handle all other sockets try: # data = s.recv(BUFSIZ) data = receive(s) if data: # Send as new client's message... msg = '\n#[' + self.getname(s) + ']>> ' + data # Send data to all except ourselves for o in self.outputs: if o != s: # o.send(msg) send(o, msg) else: print 'chatserver: %d hung up' % s.fileno() self.clients -= 1 s.close() inputs.remove(s) self.outputs.remove(s)
# Send client leaving information to others msg = '\n(Hung up: Client from %s)' % self.getname(s) for o in self.outputs: # o.send(msg) send(o, msg) except socket.error, e: # Remove inputs.remove(s) self.outputs.remove(s)
self.server.close()
if __name__ == "__main__": ChatServer().serve()
客戶端代碼:
#! /usr/bin/env python """ Simple chat client for the chat server. Defines a simple protocol to be used with chatserver.
"""
import socket import sys import select from communication import send, receive
BUFSIZ = 1024
class ChatClient(object): """ A simple command line chat client using select """
def __init__(self, name, host='127.0.0.1', port=3490): self.name = name # Quit flag self.flag = False self.port = int(port) self.host = host # Initial prompt self.prompt='[' + '@'.join((name, socket.gethostname().split('.')[0])) + ']> ' # Connect to server at port try: self.sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM) self.sock.connect((host, self.port)) print 'Connected to chat server@%d' % self.port # Send my name... send(self.sock,'NAME: ' + self.name) data = receive(self.sock) # Contains client address, set it addr = data.split('CLIENT: ')[1] self.prompt = '[' + '@'.join((self.name, addr)) + ']> ' except socket.error, e: print 'Could not connect to chat server @%d' % self.port sys.exit(1)
def cmdloop(self):
while not self.flag: try: sys.stdout.write(self.prompt) sys.stdout.flush()
# Wait for input from stdin & socket inputready, outputready,exceptrdy = select.select([0, self.sock], [],[]) for i in inputready: if i == 0: data = sys.stdin.readline().strip() if data: send(self.sock, data) elif i == self.sock: data = receive(self.sock) if not data: print 'Shutting down.' self.flag = True break else: sys.stdout.write(data + '\n') sys.stdout.flush() except KeyboardInterrupt: print 'Interrupted.' self.sock.close() break if __name__ == "__main__": import sys
if len(sys.argv)<3: sys.exit('Usage: %s chatid host portno' % sys.argv[0]) client = ChatClient(sys.argv[1],sys.argv[2], int(sys.argv[3])) client.cmdloop()
communication模塊代碼:
import cPickle import socket import struct
marshall = cPickle.dumps unmarshall = cPickle.loads
def send(channel, *args): buf = marshall(args) value = socket.htonl(len(buf)) size = struct.pack("L",value) channel.send(size) channel.send(buf)
def receive(channel):
size = struct.calcsize("L") size = channel.recv(size) try: size = socket.ntohl(struct.unpack("L", size)[0]) except struct.error, e: return '' buf = ""
while len(buf) < size: buf = channel.recv(size - len(buf))
return unmarshall(buf)[0]
(完全完)
|