socket.makefile(mode ='r',buffering = None,*,encoding = None,errors = None,newline = None )
返回一個與套接字相關聯的文件對象。返回的確切類型取決於給makefile()提供的參數。
這些參數的解釋方式與內置open()函數的解釋方式相同,除了makefile方法唯一支持的mode值是'r'(默認)'w'和'b'。
套接字必須處於阻塞模式; 它可能有超時,但是如果超時發生,文件對象的內部緩沖區可能會以不一致的狀態結束。
關閉返回的文件對象makefile()將不會關閉原始套接字,除非所有其他文件對象已關閉並且 socket.close()已在套接字對象上調用。
makefie的簡單用法:
#makefile import threading,logging,socket DATEFMT="%H:%M:%S" FORMAT = "[%(asctime)s]\t [%(threadName)s,%(thread)d] %(message)s" logging.basicConfig(level=logging.INFO,format=FORMAT,datefmt=DATEFMT) sock = socket.socket() addr = ('127.0.0.1',9999) event = threading.Event() sock.bind(addr) sock.listen() def _accept(sock:socket.socket): s,addrinfo = sock.accept() f = s.makefile(mode='rw') while True: line = f.readline() # read(10) 文本使用readline logging.info(line) if line.strip() == 'quit': break msg = "Your msg = {}. ack".format(line) f.write(msg) f.flush() f.close() sock.close() threading.Thread(target=_accept,args=(sock,)).start() while not event.wait(2): logging.info(sock) #運行結果: [19:09:47] [MainThread,3544] <socket.socket fd=288, family=AddressFamily.AF_INET, type=SocketKind.SOCK_STREAM, proto=0, laddr=('127.0.0.1', 9999)> [19:09:49] [MainThread,3544] <socket.socket fd=288, family=AddressFamily.AF_INET, type=SocketKind.SOCK_STREAM, proto=0, laddr=('127.0.0.1', 9999)> [19:09:49] [Thread-1,6044] hi? [19:09:51] [MainThread,3544] <socket.socket fd=288, family=AddressFamily.AF_INET, type=SocketKind.SOCK_STREAM, proto=0, laddr=('127.0.0.1', 9999)> [19:09:53] [MainThread,3544] <socket.socket fd=288, family=AddressFamily.AF_INET, type=SocketKind.SOCK_STREAM, proto=0, laddr=('127.0.0.1', 9999)> [19:09:55] [MainThread,3544] <socket.socket fd=288, family=AddressFamily.AF_INET, type=SocketKind.SOCK_STREAM, proto=0, laddr=('127.0.0.1', 9999)> [19:09:57] [MainThread,3544] <socket.socket fd=288, family=AddressFamily.AF_INET, type=SocketKind.SOCK_STREAM, proto=0, laddr=('127.0.0.1', 9999)> [19:09:59] [MainThread,3544] <socket.socket fd=288, family=AddressFamily.AF_INET, type=SocketKind.SOCK_STREAM, proto=0, laddr=('127.0.0.1', 9999)> [19:10:01] [MainThread,3544] <socket.socket fd=288, family=AddressFamily.AF_INET, type=SocketKind.SOCK_STREAM, proto=0, laddr=('127.0.0.1', 9999)> [19:10:03] [MainThread,3544] <socket.socket fd=288, family=AddressFamily.AF_INET, type=SocketKind.SOCK_STREAM, proto=0, laddr=('127.0.0.1', 9999)> [19:10:05] [MainThread,3544] <socket.socket fd=288, family=AddressFamily.AF_INET, type=SocketKind.SOCK_STREAM, proto=0, laddr=('127.0.0.1', 9999)> [19:10:07] [Thread-1,6044] Are you ok? [19:10:07] [MainThread,3544] <socket.socket fd=288, family=AddressFamily.AF_INET, type=SocketKind.SOCK_STREAM, proto=0, laddr=('127.0.0.1', 9999)> [19:10:09] [MainThread,3544] <socket.socket fd=288, family=AddressFamily.AF_INET, type=SocketKind.SOCK_STREAM, proto=0, laddr=('127.0.0.1', 9999)> [19:10:11] [MainThread,3544] <socket.socket fd=288, family=AddressFamily.AF_INET, type=SocketKind.SOCK_STREAM, proto=0, laddr=('127.0.0.1', 9999)> [19:10:13] [MainThread,3544] <socket.socket fd=288, family=AddressFamily.AF_INET, type=SocketKind.SOCK_STREAM, proto=0, laddr=('127.0.0.1', 9999)> [19:10:13] [Thread-1,6044] quit [19:10:15] [MainThread,3544] <socket.socket [closed] fd=-1, family=AddressFamily.AF_INET, type=SocketKind.SOCK_STREAM, proto=0>
TCP Server 改裝成makefile:
連接兩個客戶端分別測試消息是否分發正常,客戶端quit指令是否可以正常關閉socket,self.clients字典是否已經移除失聯的socket。
客戶端分別測試正常退出:quit退出,和異常退出:強制退出。然后觀察服務端是否運行正常。
#TCP Server 改裝成makefile import threading,logging,time,random,datetime,socket DATEFMT="%H:%M:%S" FORMAT = "[%(asctime)s]\t [%(threadName)s,%(thread)d] %(message)s" logging.basicConfig(level=logging.INFO,format=FORMAT,datefmt=DATEFMT) class ChatServer: def __init__(self,ip='127.0.0.1',port=9999): #啟動服務 self.addr = (ip,port) self.sock = socket.socket() self.event = threading.Event() self.clients = {} #客戶端 def show_client(self): while not self.event.is_set(): if len(self.clients) > 0: logging.info(self.clients) self.event.wait(3) def start(self): self.sock.bind(self.addr) self.sock.listen() # accept會阻塞主線程,所以開一個新線程 threading.Thread(target=self._accept,name='accept',daemon=True).start() threading.Thread(target=self.show_client,name='show_client',daemon=True).start() def stop(self): for c in self.clients.values(): c.close() self.sock.close() self.event.wait(3) self.event.set() def _accept(self): while not self.event.is_set(): #多人連接 conn,client = self.sock.accept() #阻塞 f = conn.makefile(mode='rw',encoding='utf8') self.clients[client] = f logging.info("{}-{}".format(conn,client)) # recv 默認阻塞,每一個連接單獨起一個recv線程准備接收數據 threading.Thread(target=self._recv, args=(f, client), name='recv',daemon=True).start() def _recv(self, f, client): #接收客戶端數據 while not self.event.is_set(): try: data = f.readline() except Exception: data = 'quit' finally: msg = data.strip() # Client通知退出機制 if msg == 'quit': f.close() self.clients.pop(client) logging.info('{} quit'.format(client)) break msg = "{:%Y/%m/%d %H:%M:%S} {}:{}\n{}\n".format(datetime.datetime.now(),*client,data) print(msg) logging.info(msg) for c in self.clients.values(): c.writelines(msg) c.flush() cs = ChatServer() print('!!!!!!!!!!!') cs.start() print('~~~~~~~~~~~~~~~~~~~~') e = threading.Event() def showthreads(e:threading.Event): while not e.wait(3): logging.info(threading.enumerate()) threading.Thread(target=showthreads,name='showthreads',args=(e,)).start() while not e.wait(1): # Sever控制台退出方式 cmd = input('>>> ').strip() if cmd == 'quit': cs.stop() e.wait(3) break #運行結果: !!!!!!!!!!! ~~~~~~~~~~~~~~~~~~~~ >>> [15:18:49] [show_client,4284] {('127.0.0.1', 3507): <_io.TextIOWrapper mode='rw' encoding='utf8'>} [15:18:49] [accept,2820] <socket.socket fd=388, family=AddressFamily.AF_INET, type=SocketKind.SOCK_STREAM, proto=0, laddr=('127.0.0.1', 9999), raddr=('127.0.0.1', 3507)>-('127.0.0.1', 3507) [15:18:49] [showthreads,8384] [<Thread(showthreads, started 8384)>, <Thread(accept, started daemon 2820)>, <Thread(show_client, started daemon 4284)>, <_MainThread(MainThread, started 932)>, <Thread(recv, started daemon 10156)>] [15:18:52] [show_client,4284] {('127.0.0.1', 3507): <_io.TextIOWrapper mode='rw' encoding='utf8'>} [15:18:52] [showthreads,8384] [<Thread(showthreads, started 8384)>, <Thread(accept, started daemon 2820)>, <Thread(show_client, started daemon 4284)>, <_MainThread(MainThread, started 932)>, <Thread(recv, started daemon 10156)>] [15:18:54] [recv,10156] 2017/12/24 15:18:54 127.0.0.1:3507 2017/12/24 15:18:54 127.0.0.1:3507 123 123 [15:18:55] [show_client,4284] {('127.0.0.1', 3507): <_io.TextIOWrapper mode='rw' encoding='utf8'>} [15:18:55] [showthreads,8384] [<Thread(showthreads, started 8384)>, <Thread(accept, started daemon 2820)>, <Thread(show_client, started daemon 4284)>, <_MainThread(MainThread, started 932)>, <Thread(recv, started daemon 10156)>] [15:18:58] [show_client,4284] {('127.0.0.1', 3507): <_io.TextIOWrapper mode='rw' encoding='utf8'>} [15:18:58] [showthreads,8384] [<Thread(showthreads, started 8384)>, <Thread(accept, started daemon 2820)>, <Thread(show_client, started daemon 4284)>, <_MainThread(MainThread, started 932)>, <Thread(recv, started daemon 10156)>] [15:19:01] [show_client,4284] {('127.0.0.1', 3507): <_io.TextIOWrapper mode='rw' encoding='utf8'>} [15:19:01] [showthreads,8384] [<Thread(showthreads, started 8384)>, <Thread(accept, started daemon 2820)>, <Thread(show_client, started daemon 4284)>, <_MainThread(MainThread, started 932)>, <Thread(recv, started daemon 10156)>] [15:19:04] [show_client,4284] {('127.0.0.1', 3507): <_io.TextIOWrapper mode='rw' encoding='utf8'>} [15:19:04] [showthreads,8384] [<Thread(showthreads, started 8384)>, <Thread(accept, started daemon 2820)>, <Thread(show_client, started daemon 4284)>, <_MainThread(MainThread, started 932)>, <Thread(recv, started daemon 10156)>] [15:19:07] [show_client,4284] {('127.0.0.1', 3507): <_io.TextIOWrapper mode='rw' encoding='utf8'>} [15:19:07] [showthreads,8384] [<Thread(showthreads, started 8384)>, <Thread(accept, started daemon 2820)>, <Thread(show_client, started daemon 4284)>, <_MainThread(MainThread, started 932)>, <Thread(recv, started daemon 10156)>] [15:19:10] [show_client,4284] {('127.0.0.1', 3507): <_io.TextIOWrapper mode='rw' encoding='utf8'>} [15:19:10] [showthreads,8384] [<Thread(showthreads, started 8384)>, <Thread(accept, started daemon 2820)>, <Thread(show_client, started daemon 4284)>, <_MainThread(MainThread, started 932)>, <Thread(recv, started daemon 10156)>] [15:19:12] [recv,10156] 2017/12/24 15:19:12 127.0.0.1:3507 [15:19:12] [recv,10156] ('127.0.0.1', 3507) quit 2017/12/24 15:19:12 127.0.0.1:3507 [15:19:13] [showthreads,8384] [<Thread(showthreads, started 8384)>, <Thread(accept, started daemon 2820)>, <Thread(show_client, started daemon 4284)>, <_MainThread(MainThread, started 932)>]
總結:
使用makefile返回一個套接字相關聯的文件對象,對該文件對象的操作方法,與普通文件操作方法一致,read,readline,write,writeline
makefile不僅僅可以對accept建立連接后的socketObject使用,也可對主線程的sock和任何socketObject使用。