Python的網絡編程主要支持兩種網絡協議:TCP和UDP。這兩種協議都經過叫Socket的編程抽象進行處理。Socket起源於Unix,是相似於文件的存在,能夠像文件同樣進行I/O、打開、關閉等操做,最主要的是它能夠實現網絡上不一樣主機的進程間通訊,因此基本上Socket是任何一種網絡通信中最基礎的內容。html
Python中創建一個套接字很簡單:python
1
2
|
import
socket
s
=
socket.socket(family,
type
)
|
family爲地址族,該族指定要使用的網絡協議,主要使用的有:chrome
type爲套接字類型,指定給定的協議組中使用的通訊類型:編程
TCP和UDP都是基於Client/Server的編程模型,因此Socket編程也分爲客戶端和服務器端,以TCP爲例:服務器
要獲取遠程主機的ip地址,可使用socket標準庫提供的gethostbyname()方法:網絡
1
2
3
|
>>>
import
socket
>>> socket.gethostbyname(
'www.baidu.com'
)
'115.239.211.112'
|
socket套接字實例s可用於客戶端的方法有如下幾個:多線程
OK,如今能夠用socket向遠程主機發送一個HTTP GET請求了:socket
1
2
3
4
5
6
7
8
9
10
11
12
13
|
# -*- coding: utf-8 -*-
import
socket
s
=
socket.socket(socket.AF_INET, socket.SOCK_STREAM)
#創建套接字
host
=
'www.baidu.com'
port
=
80
ip
=
socket.gethostbyname(host)
#獲取ip
s.connect((ip, port))
#創建鏈接
message
=
'GET / HTTP/1.1\r\n\r\n'
s.sendall(message)
#發送GET請求
r
=
s.recv(
4096
)
#接收數據
print
r
s.close()
#關閉套接字
|
返回:大數據
1
2
3
4
5
6
7
8
9
10
|
HTTP
/
1.1
302
Moved Temporarily
Date: Wed,
10
Jan
2018
18
:
56
:
45
GMT
Content
-
Type
: text
/
html
Content
-
Length:
225
Connection: Keep
-
Alive
Location: http:
/
/
www.baidu.com
/
search
/
error.html
Server: BWS
/
1.1
X
-
UA
-
Compatible: IE
=
Edge,chrome
=
1
BDPAGETYPE:
3
Set
-
Cookie: BDSVRTM
=
0
; path
=
/
|
下面咱們能夠實現本身的服務器。spa
Socket實例與服務器端編程有關的方法有如下幾個:
如今寫一個將客戶端發送來的信息發送回去的服務器:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
|
# -*- coding: utf-8 -*-
import
socket
import
sys
HOST
=
''
PORT
=
8088
s
=
socket.socket(socket.AF_INET, socket.SOCK_STREAM)
s.bind((HOST, PORT))
s.listen(
5
)
print
'開始監聽'
conn, addr
=
s.accept()
print
'Connected with '
+
addr[
0
]
+
':'
+
str
(addr[
1
])
data
=
conn.recv(
1024
)
conn.sendall(data)
conn.close()
s.close()
|
運行:
1
2
|
>>>
開始監聽
|
服務器開始監聽鏈接了。修改一下剛纔寫的客戶端程序:
1
2
3
4
5
6
7
8
9
10
11
12
|
# -*- coding: utf-8 -*-
import
socket
s
=
socket.socket(socket.AF_INET, socket.SOCK_STREAM)
host
=
'localhost'
port
=
8088
s.connect((host, port))
#創建鏈接
message
=
'GET / HTTP/1.1\r\n\r\n'
s.sendall(message)
#發送GET請求
r
=
s.recv(
4096
)
#接收數據
print
r
s.close()
#關閉套接字
|
運行,鏈接本地的服務器,服務器端輸出:
1
2
3
|
>>>
開始監聽
Connected with
127.0
.
0.1
:
60933
|
鏈接成功。客戶端輸出:
1
2
|
>>>
GET
/
HTTP
/
1.1
|
發送的消息被返回了。
這就是一個最簡單的服務器了。上述服務器只能處理一次鏈接,這顯然不是咱們想看到的,保持一直運行:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
|
# -*- coding: utf-8 -*-
import
socket
import
sys
HOST
=
''
PORT
=
8088
s
=
socket.socket(socket.AF_INET, socket.SOCK_STREAM)
s.bind((HOST, PORT))
s.listen(
5
)
print
'開始監聽'
while
True
:
conn, addr
=
s.accept()
print
'Connected with '
+
addr[
0
]
+
':'
+
str
(addr[
1
])
data
=
conn.recv(
1024
)
conn.sendall(data)
conn.close()
s.close()
|
如今就可使用客戶端無限鏈接了:
1
2
3
4
5
6
|
>>>
開始監聽
Connected with
127.0
.
0.1
:
61240
Connected with
127.0
.
0.1
:
61242
Connected with
127.0
.
0.1
:
61245
Connected with
127.0
.
0.1
:
61250
|
如今服務器端雖然能夠處理無限多個鏈接,但只能一個一個的處理,後面的客戶端鏈接只能等待前面的鏈接完成才能發送數據。要同時處理多個鏈接,可使用多線程。服務器端接收到新的鏈接後,開啓一個線程處理新鏈接,主線程去創建下一個鏈接。
服務器端:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
|
# -*- coding: utf-8 -*-
import
socket
import
threading
HOST
=
''
PORT
=
8088
s
=
socket.socket(socket.AF_INET, socket.SOCK_STREAM)
s.bind((HOST, PORT))
s.listen(
5
)
print
'開始監聽'
def
runThread(conn):
data
=
conn.recv(
1024
)
print
data
conn.sendall(data)
conn.close()
while
True
:
conn, addr
=
s.accept()
print
'Connected with '
+
addr[
0
]
+
':'
+
str
(addr[
1
])
t
=
threading.Thread(target
=
runThread, args
=
(conn,))
t.daemon
=
True
t.start()
|
客戶端啓動多個鏈接:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
|
# -*- coding: utf-8 -*-
import
socket
import
time
import
threading
def
run():
s
=
socket.socket(socket.AF_INET, socket.SOCK_STREAM)
host
=
'localhost'
port
=
8088
s.connect((host, port))
message
=
'GET / HTTP/1.1\r\n\r\n'
s.sendall(message)
print
s.recv(
4096
)
s.close()
if
__name__
=
=
'__main__'
:
for
i
in
xrange
(
4
):
t
=
threading.Thread(target
=
run)
t.start()
|
運行:
1
2
3
4
5
6
7
8
9
10
11
12
|
開始監聽
Connected with
127.0
.
0.1
:
61772
GET
/
HTTP
/
1.1
Connected with
127.0
.
0.1
:
61773
GET
/
HTTP
/
1.1
Connected with
127.0
.
0.1
:
61774
GET
/
HTTP
/
1.1
Connected with
127.0
.
0.1
:
61775
GET
/
HTTP
/
1.1
|
UDP與TCP的不一樣之處在於UDP是不用創建鏈接的。
在此須要使用s.recvfrom()與s.sendto()方法,前者與s.recv()相同,但返回(data, addr)的元組,addr爲數據發送端的套接字地址,後者發送數據時須要加入要發送的遠程地址。
服務器:
1
2
3
4
5
6
7
8
9
|
# -*- coding: utf-8 -*-
import
socket
s
=
socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
s.bind(('',
10000
))
while
True
:
data, addr
=
s.recvfrom(
1024
)
print
'接收到%s的鏈接'
%
str
(addr)
s.sendto(data, addr)
|
客戶端:
1
2
3
4
5
6
7
8
|
# -*- coding: utf-8 -*-
import
socket
s
=
socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
s.sendto(
'Hello World'
, (
'localhost'
,
10000
))
r, addr
=
s.recvfrom(
1024
)
print
r
s.close()
|
運行:
1
2
3
4
|
>>>
接收到(
'127.0.0.1'
,
64112
)的鏈接
>>>
Hello World
|