socket.makefile(mode ='r',buffering = None,*,encoding = None,errors = None,newline = None )
返回一個與套接字相關聯的文件對象。返回的確切類型取決於給makefile()提供的參數。python
這些參數的解釋方式與內置open()函數的解釋方式相同,除了makefile方法惟一支持的mode值是'r'(默認)'w'和'b'。app
套接字必須處於阻塞模式; 它可能有超時,可是若是超時發生,文件對象的內部緩衝區可能會以不一致的狀態結束。dom
關閉返回的文件對象makefile()將不會關閉原始套接字,除非全部其餘文件對象已關閉而且 socket.close()已在套接字對象上調用。socket
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]\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。ui
客戶端分別測試正常退出:quit退出,和異常退出:強制退出。而後觀察服務端是否運行正常。spa
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]\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,writelinecode
makefile不單單能夠對accept創建鏈接後的socketObject使用,也可對主線程的sock和任何socketObject使用。