套接字:通讯端点
什么是套接字?
书上说的端口是数据结构和I/O缓存区”是指硬件端口,网络编程里的端口可以理解为应用程序的ID。
说得形象点,套接字就类似我们人类的门
我们打开门,通过门外面的人可以进来
我们推开门,里面的人也可以出去
同样,外面的数据可以通过socket把它存储在本地机器的缓冲区里等待本地机器接收
本地机器的数据可以通过socket放在缓冲区里等待发送到对方机器上
当我们把门给关上时,就拒绝了和外面世界的交往。
套接字是一种具有之前所说的“通讯端点”概念的计算机网络数据结构。网络化的应用程序在开始任何通
讯之前都必需要创建套接字。就像电话的插口一样,没有它就完全没办法通讯。
套接字有两种,分别是基于文件型的和基于网络型的。
基于文件型的:
Unix 套接字是我们要介绍的第一个套接字家族。其“家族名”为AF_UNIX,由于两个进程都运行在同一台
机器上,而且这些套接字是基于文件的。所以,它们的底层结构是由文件系统来支持的。这样做相当有道
理,因为,同一台电脑上,文件系统的确是不同的进程都能访问的。
基于网络型的
它有自己的家族名字:AF_INET,或叫“地址家族:Internet”。
Python 只支持AF_UNIX,AF_NETLINK,和AF_INET 家族。由于我们只关心网络编程,所以在本章的大部分
时候,我们都只用AF_INET。
套接字的类型只有两种。一种是面向连接的套接字,即在通讯之前一定要建立一条连接,就像跟朋友打电
话时那样。这种通讯方式也被称为“虚电路”或“流套接字”。面向连接的通讯方式提供了顺序的,可靠
的,不会重复的数据传输,而且也不会被加上数据边界。这也意味着,每一个要发送的信息,可能会被拆
分成多份,每一份都会不多不少地正确到达目的地。然后被重新按顺序拼装起来,传给正在等待的应用程
序。
创建套接字:
from socket import *
tcpsock=socket(AF_INET,SOCK_STREAM)
udpsock=socket(AF_INET,SOCK_DGRAM)
套接字对象(内建)方法
函数 描述
服务器端套接字函数
s.bind() 绑定地址(主机,端口号对)到套接字
s.listen() 开始TCP 监听
s.accept() 被动接受TCP 客户的连接,(阻塞式)等待连接的到来
客户端套接字函数
s.connect() 主动初始化TCP 服务器连接
s.connect_ex() connect()函数的扩展版本,出错时返回出错码,而不是抛异常
公共用途的套接字函数
s.recv() 接收TCP 数据
s.send() 发送TCP 数据
s.sendall() 完整发送TCP 数据
s.recvfrom() 接收UDP 数据
s.sendto() 发送UDP 数据
s.getpeername() 连接到当前套接字的远端的地址
s.getsockname() 当前套接字的地址
s.getsockopt() 返回指定套接字的参数
s.setsockopt() 设置指定套接字的参数
s.close() 关闭套接字
Blocking-Oriented Socket Methods
s.setblocking() 设置套接字的阻塞与非阻塞模式
s.settimeout() 设置阻塞套接字操作的超时时间
s.gettimeout() 得到阻塞套接字操作的超时时间
面向文件的套接字的函数
s.fileno() 套接字的文件描述符
s.makefile() 创建一个与该套接字关连的文件
创建一个tcp服务器的伪代码:
ss=socket() #创建服务器套接字
ss.bind() #把地址绑定到套接字上
ss.listen() #监听链接
inf_loop #服务器无线循环
cs=ss.accept() #接受客户的链接
comm_loop #通讯循环
cs.recv()/cs.send() #对话(接受与发送)
cs.close() #关闭客户套接字
ss.close() #关闭服务器套接字(可选)
会创建一个TCP 服务器程序:
from socket import *
from time import ctime
HOST=‘‘
PORT=21567
BUFSIZ=1024
ADDR=(HOST,PORT)
tcpSerSock=socket(AF_INET,SOCK_STREAM)
tcpSerSock.bind(ADDR)
tcpSerSock.listen(5)
while True:
print ‘waiting for connection...‘
tcpCliSock,addr=tcpSerSock.accept()
print ‘... connected from:‘,addr
while True:
data=tcpCliSock.rece(BUFSIZ)
if not data:
break
tcpCliSock.send(‘[%s]%s‘%(ctime(),data))
tcpCliSock.close()
tcpSerSock.close()
创建TCP 客户端:
伪代码:
cs = socket() # 创建客户套接字
cs.connect() # 尝试连接服务器
comm_loop: # 通讯循环
cs.send()/cs.recv() # 对话(发送/接收)
cs.close() # 关闭客户套接字
实例:
from socket import *
from time import ctime,sleep
HOST=‘localhost‘
PORT=21567
BUFSIZ=1024
ADDR=(HOST,PORT)
tcpCliSock=socket(AF_INET,SOCK_STREAM)
tcpCliSock.connect(ADDR)
while True:
data=raw_input(‘>‘)
if not data:
break
tcpCliSock.send(data)
sleep(6)
data=tcpCliSock.recv(BUFSIZ)
if not data:
break
print data
tcpCliSock.close()
运行我们的客户端与服务器程序,先运行服务器端再运行客户端,因为服务器端是被动的,他要在那里等
待着。
socket.error: [Errno 10054]说明服务器端出现了error,socket.error: [Errno 10053]客户端出现了问
题
创建一个UDP 服务器:
ss = socket() # 创建一个服务器套接字
ss.bind() # 绑定服务器套接字
inf_loop: # 服务器无限循环
cs = ss.recvfrom()/ss.sendto() # 对话(接收与发送)
ss.close() # 关闭服务器套接字
实例:
from socket import *
from time import ctime,sleep
HOST=‘localhost‘
PORT=21567
BUFSIZ=1024
ADDR=(HOST,PORT)
tcpCliSock=socket(AF_INET,SOCK_STREAM)
tcpCliSock.connect(ADDR)
while True:
data=raw_input(‘>‘)
if not data:
break
tcpCliSock.send(data)
sleep(6)
data=tcpCliSock.recv(BUFSIZ)
if not data:
break
print data
tcpCliSock.close()
创建一个UDP 客户端:
伪代码:
cs = socket() # 创建客户套接字
comm_loop: # 通讯循环
cs.sendto()/cs.recvfrom() # 对话(发送/接收)
cs.close() # 关闭客户套接字
实例:
from socket import *
HOST=‘localhost‘
PORT=21567
BUFSIZ=1024
ADDR=(HOST,PORT)
udpCliSock=socket(AF_INET,SOCK_DGRAM)
while True:
data=raw_input(‘>‘)
if not data:
break
udpCliSock.sendto(data,ADDR)
data,ADDR=udpCliSock.recvfrom(BUFSIZ)
if not data:
break
print data
udpCliSock.close
套接字模块属性:
属性名字 描述
数据属性
AF_UNIX, AF_INET, AF_INET6a Python 支持的套接字家族
SO_STREAM, SO_DGRAM 套接字类型 (TCP = 流, UDP = 数据报)
has_ipv6b 表示是否支持IPv6 的标志变量
异常
error 套接字相关错误
herrora 主机和地址相关的错误
gaierrora 地址相关的错误
timeoutb 超时
函数
socket() 用指定的地址家族,套接字类型和协议类型(可选)创建一个套接字对象
socketpair()c 用指定的地址家族,套接字类型和协议类型(可选)创建一对套接字对象
fromfd() 用一个已经打开的文件描述符创建一个套接字对象数据属性
ssl()d 在套接字初始化一个安全套接字层(SSL)。不做证书验证。
getaddrinfo()a 得到地址信息
getfqdn()e 返回完整的域的名字
gethostname() 得到当前主机名
gethostbyname() 由主机名得到对应的ip 地址
gethostbyname_ex() gethostbyname()的扩展版本,返回主机名,主机所有的别名和
IP 地址列表。
gethostbyaddr() 由IP 地址得到DNS 信息,返回一个类似gethostbyname_ex()
的3 元组。
getprotobyname() 由协议名(如‘tcp‘)得到对应的号码。
getservbyname()/ 由服务名得到对应的端口号或相反
getservbyport() 两个函数中,协议名都是可选的。
ntohl()/ntohs() 把一个整数由网络字节序转为主机字节序
htonl()/htons() 把一个整数由主机字节序转为网络字节序
inet_aton()/ 把IP 地址转为32 位整型,以及反向函数。(仅对IPv4 地址有效)
inet_ntoa()
inet_pton()/ 把IP 地址转为二进制格式以及反向函数。(仅对IPv4 地址有效)
inet_ntop()b
getdefaulttimeout()/ 得到/设置默认的套接字超时时间,单位秒(浮点数)
setdefaulttimeout()
*SocketServer 模块
用*SocketServer 模块简化上面的例子:
tcp server:
from SocketServer import TCPServer as TCP, StreamRequestHandler as SRH
from time import ctime
HOST=‘‘
PORT=21567
ADDR=(HOST,PORT)
class MyRequestHandler(SRH):
def handle(self):
print ‘...connected from:‘,self.client_address
self.wfile.write(‘[%s]%s‘%(ctime(),self.rfile.readline()))
tcpServ=TCP(ADDR,MyRequestHandler)
print ‘waiting for connection...‘
tcpServ.serve_forever()
tcp client:
from socket import *
from time import ctime,sleep
HOST=‘localhost‘
核心编程
PORT=21567
BUFSIZ=1024
ADDR=(HOST,PORT)
while True:
tcpCliSock=socket(AF_INET,SOCK_STREAM)
tcpCliSock.connect(ADDR)
data=raw_input(‘>‘)
if not data:
break
tcpCliSock.send(‘%s\r\n‘%data)
data=tcpCliSock.recv(BUFSIZ)
if not data:
break
print data.strip()
tcpCliSock.close()
Twisted 框架介绍:
Twisted 是一个完全事件驱动的网络框架。它允许你使用和开发完全异步的网络应用程序和协议。
这个模块要单独安装,内容也比较多,稍后再仔细研究吧