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
相关文章
相关标签/搜索