Python简单的多客户端聊天室程序

本文所示代码将教你如何使用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]

(完全完)


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值