Python socket

Python的網絡編程主要支持兩種網絡協議:TCP和UDP。這兩種協議都經過叫Socket的編程抽象進行處理。Socket起源於Unix,是相似於文件的存在,能夠像文件同樣進行I/O、打開、關閉等操做,最主要的是它能夠實現網絡上不一樣主機的進程間通訊,因此基本上Socket是任何一種網絡通信中最基礎的內容。html

Python中創建一個套接字很簡單:python

1
2
import  socket
=  socket.socket(family,  type )

地址族

family爲地址族,該族指定要使用的網絡協議,主要使用的有:chrome

  • AF_INET:IPv4協議(TCP,UDP)
  • AF_INET6:IPv6協議(TCP,UDP)
  • AF_UNIX:UNIX域協議,用於同一臺機器的進程間通信

套接字類型

type爲套接字類型,指定給定的協議組中使用的通訊類型:編程

  • SOCK_STREAM:用於TCP
  • SOCK_DGRAM:用於UDP

TCP和UDP都是基於Client/Server的編程模型,因此Socket編程也分爲客戶端和服務器端,以TCP爲例:服務器

TCP客戶端編程

要獲取遠程主機的ip地址,可使用socket標準庫提供的gethostbyname()方法:網絡

1
2
3
>>>  import  socket
>>> socket.gethostbyname( 'www.baidu.com' )
'115.239.211.112'

socket套接字實例s可用於客戶端的方法有如下幾個:多線程

  • s.connect(addr):鏈接服務器端套接字。addr格式取決於地址族,對於IPv4來講,是一個包含ip地址與端口的元組,(host, port)。鏈接失敗會報socket.error錯誤。
  • s.sendall(string):嘗試發送全部數據,成功則返回None,失敗則報異常。
  • s.recv(bufsize):接收數據,bufsize指定接收的最大數據量。
  • s.close():關閉套接字

OK,如今能夠用socket向遠程主機發送一個HTTP GET請求了:socket

1
2
3
4
5
6
7
8
9
10
11
12
13
# -*- coding: utf-8 -*-
import  socket
 
=  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請求
=  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

TCP服務器端編程

Socket實例與服務器端編程有關的方法有如下幾個:

  • s.bind(addr):addr也是(host, port)形式的元組,將套接字綁定到特定的地址和端口上。空字符串表示任意地址,'broadcast'能夠用作發送廣播信息。
  • s.listen(backlog):開始監聽鏈接,backlog爲最大掛起鏈接次數。
  • s.accept:返回元組(conn,addr),conn爲新的套接字,能夠用來發送和接收數據。addr是客戶端的套接字地址。
  • s.recv()、s.sendall()和s.close()與客戶端同。

如今寫一個將客戶端發送來的信息發送回去的服務器:

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
 
=  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
 
=  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請求
=  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
 
=  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
 
=  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 ])
     =  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():
     =  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  in  xrange ( 4 ):
         =  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編程

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
 
=  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
 
=  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
相關文章
相關標籤/搜索