Python簡單的多客戶端聊天室程式

weixin_34162629發表於2012-01-06

本文所示程式碼將教你如何使用Python標準庫中的select.select模組實現多路複用的命令列下CS模式的聊天室程式。

伺服器端程式碼:

#!/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]

(完全完)


相關文章