基於select的python聊天室程式,selectpython聊天室
python網路編程具體參考《python select網路編程詳細介紹》。
在python中,select函數是一個對底層作業系統的直接存取的介面。它用來監控sockets、files和pipes,等待IO完成(Waiting for I/O completion)。當有可讀、可寫或是例外狀況事件產生時,select可以很容易的監控到。select.select(rlist, wlist, xlist[, timeout]) 傳遞三個參數,一個為輸入而觀察的檔案對象列表,一個為輸出而觀察的檔案對象列表和一個觀察錯誤異常的檔案清單。第四個是一個選擇性參數,表示逾時秒數。其返回3個tuple,每個tuple都是一個準備好的對象列表,它和前邊的參數是一樣的順序。
聊天室程式如下。運行多個client,則可互相聊天,輸入"exit"即可退出
伺服器代碼
#!/bin/env python#-*- coding:utf8 -*-"""server select"""import sysimport timeimport socketimport selectimport loggingimport Queueg_select_timeout = 10class Server(object): def __init__(self, host='10.1.32.80', port=33333, timeout=2, client_nums=10): self.__host = host self.__port = port self.__timeout = timeout self.__client_nums = client_nums self.__buffer_size = 1024 self.server = socket.socket(socket.AF_INET, socket.SOCK_STREAM) self.server.setblocking(False) self.server.settimeout(self.__timeout) self.server.setsockopt(socket.SOL_SOCKET, socket.SO_KEEPALIVE, 1) #keepalive self.server.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) #連接埠複用 server_host = (self.__host, self.__port) try: self.server.bind(server_host) self.server.listen(self.__client_nums) except: raise self.inputs = [self.server] #select 接收檔案描述符列表 self.outputs = [] #輸出檔案描述符列表 self.message_queues = {}#訊息佇列 self.client_info = {} def run(self): while True: readable , writable , exceptional = select.select(self.inputs, self.outputs, self.inputs, g_select_timeout) if not (readable or writable or exceptional) : continue for s in readable : if s is self.server:#是用戶端串連 connection, client_address = s.accept() #print "connection", connection print "%s connect." % str(client_address) connection.setblocking(0) #非阻塞 self.inputs.append(connection) #用戶端添加到inputs self.client_info[connection] = str(client_address) self.message_queues[connection] = Queue.Queue() #每個用戶端一個訊息佇列 else:#是client, 資料發送過來 try: data = s.recv(self.__buffer_size) except: err_msg = "Client Error!" logging.error(err_msg) if data : #print data data = "%s %s say: %s" % (time.strftime("%Y-%m-%d %H:%M:%S"), self.client_info[s], data) self.message_queues[s].put(data) #隊列添加訊息 if s not in self.outputs: #要回複訊息 self.outputs.append(s) else: #用戶端斷開 #Interpret empty result as closed connection print "Client:%s Close." % str(self.client_info[s]) if s in self.outputs : self.outputs.remove(s) self.inputs.remove(s) s.close() del self.message_queues[s] del self.client_info[s] for s in writable: #outputs 有訊息就要發出去了 try: next_msg = self.message_queues[s].get_nowait() #非阻塞擷取 except Queue.Empty: err_msg = "Output Queue is Empty!" #g_logFd.writeFormatMsg(g_logFd.LEVEL_INFO, err_msg) self.outputs.remove(s) except Exception, e: #發送的時候用戶端關閉了則會出現writable和readable同時有資料,會出現message_queues的keyerror err_msg = "Send Data Error! ErrMsg:%s" % str(e) logging.error(err_msg) if s in self.outputs: self.outputs.remove(s) else: for cli in self.client_info: #發送給其他用戶端 if cli is not s: try: cli.sendall(next_msg) except Exception, e: #發送失敗就關掉 err_msg = "Send Data to %s Error! ErrMsg:%s" % (str(self.client_info[cli]), str(e)) logging.error(err_msg) print "Client: %s Close Error." % str(self.client_info[cli]) if cli in self.inputs: self.inputs.remove(cli) cli.close() if cli in self.outputs: self.outputs.remove(s) if cli in self.message_queues: del self.message_queues[s] del self.client_info[cli] for s in exceptional: logging.error("Client:%s Close Error." % str(self.client_info[cli])) if s in self.inputs: self.inputs.remove(s) s.close() if s in self.outputs: self.outputs.remove(s) if s in self.message_queues: del self.message_queues[s] del self.client_info[s] if "__main__" == __name__: Server().run()
用戶端代碼
#!/usr/local/bin/python# *-* coding:utf-8 -*-"""client.py"""import sysimport timeimport socketimport threadingclass Client(object): def __init__(self, host, port=33333, timeout=1, reconnect=2): self.__host = host self.__port = port self.__timeout = timeout self.__buffer_size = 1024 self.__flag = 1 self.client = None self.__lock = threading.Lock() @property def flag(self): return self.__flag @flag.setter def flag(self, new_num): self.__flag = new_num def __connect(self): client = socket.socket(socket.AF_INET, socket.SOCK_STREAM) #client.bind(('0.0.0.0', 12345,)) client.setblocking(True) client.settimeout(self.__timeout) client.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) #連接埠複用 server_host = (self.__host, self.__port) try: client.connect(server_host) except: raise return client def send_msg(self): if not self.client: return while True: time.sleep(0.1) #data = raw_input() data = sys.stdin.readline().strip() if "exit" == data.lower(): with self.__lock: self.flag = 0 break self.client.sendall(data) return def recv_msg(self): if not self.client: return while True: data = None with self.__lock: if not self.flag: print 'ByeBye~~' break try: data = self.client.recv(self.__buffer_size) except socket.timeout: continue except: raise if data: print "%s\n" % data time.sleep(0.1) return def run(self): self.client = self.__connect() send_proc = threading.Thread(target=self.send_msg) recv_proc = threading.Thread(target=self.recv_msg) recv_proc.start() send_proc.start() recv_proc.join() send_proc.join() self.client.close()if "__main__" == __name__: Client('10.1.32.80').run()