socket.makefile(mode ='r',buffering = None,*,encoding = None,errors = None,newline = None )
返回一个与套接字相关联的文件对象。返回的确切类型取决于给makefile()提供的参数。
这些参数的解释方式与内置open()函数的解释方式相同,除了makefile方法唯一支持的mode值是'r'(默认)'w'和'b'。
套接字必须处于阻塞模式; 它可能有超时,但是如果超时发生,文件对象的内部缓冲区可能会以不一致的状态结束。
关闭返回的文件对象makefile()将不会关闭原始套接字,除非所有其他文件对象已关闭并且 socket.close()已在套接字对象上调用。
makefie的简单用法:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
|
#makefile import threading,logging,socket DATEFMT = "%H:%M:%S" FORMAT = "[%(asctime)s] [%(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退出,和异常退出:强制退出。然后观察服务端是否运行正常。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
|
#TCP Server 改装成makefile import threading,logging,time,random,datetime,socket DATEFMT = "%H:%M:%S" FORMAT = "[%(asctime)s] [%(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} {}:{}
{}
" . 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使用。