Created
November 4, 2014 12:01
-
-
Save corehello/6961ef62a6facad8aefd to your computer and use it in GitHub Desktop.
python select implement chat server and client: come from http://code.activestate.com/recipes/531824-chat-server-client-using-selectselect/
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
# First the server | |
#!/usr/bin/env python | |
#!/usr/bin/env python | |
""" | |
A basic, multiclient 'chat server' using Python's select module | |
with interrupt handling. | |
Entering any line of input at the terminal will exit the server. | |
""" | |
import select | |
import socket | |
import sys | |
import signal | |
from communication import send, receive | |
BUFSIZ = 1024 | |
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() | |
############################################################################# | |
# The chat client | |
############################################################################# | |
#! /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() | |
############################################################################### | |
# The communication module (communication.py) | |
############################################################################### | |
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] |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment