版权声明:本文为博主原创文章,转载本站文章请注明作者和出处,请勿用于任何商业用途。 https://blog.csdn.net/wutianxu123/article/details/82527853
10.1 理论基础
客户端/服务器架构:
服务器开放,客户端访问服务器的数据
注意!在任何客户端与服务器连接之前,都要首先启动服务器,后启动客户端。
套接字(socket):
源IP地址和目的IP地址以及源端口号和目的端口号的组合称为套接字
1、没有任何类型的通信开始之前,网络应用必须创建套接字
2、用于同一主机上进程与进程之间的通信
3、用于网络中一个主机与另一个主机之间的通信。通信(套接字)地址:(主机名:端口)
面向连接的套接字:通信之前必须先建立连接。协议为TCP。套接字类型为:SOCK_STREAM
无连接的套接字:通信之前不需要建立连接。协议为UDP。套接字类型为:SOCK_DGRAM
模块:socket
import socket
from socket import *
函数:socket
socket.socket() #socket模块下的socket函数。用于创建套接字对象
-------------------------------------------------------------------------------------
函数的语法格式:
socket(socket_family, socket_type, protocol=0)
函数解释:
socket_family: 套接字家族可以使AF_UNIX或者AF_INET
socket_type: 套接字类型可以根据是面向连接的还是非连接分为SOCK_STREAM或SOCK_DGRAM
protocol: 一般不填默认为0
常见的套接字对象方法及属性
服务器套接字:
方法 | 解释 |
---|---|
s.bind() | 绑定地址(host,port)到套接字, 在AF_INET下,以元组(host,port)的形式表示地址 |
s.listen() | 开始TCP监听。backlog指定在拒绝连接之前,操作系统可以挂起的最大连接数量。该值至少为1,大部分应用程序设为5就可以了。 |
s.accept() | 被动接受TCP客户端连接,(阻塞式)等待连接的到来 |
客户端套接字:
方法 | 解释 |
---|---|
s.connect() | 主动初始化TCP服务器连接。一般address的格式为元组(hostname,port),如果连接出错,返回socket.error错误。 |
s.connect_ex() | connect()函数的扩展版本,出错时返回出错码,而不是抛出异常 |
公共用途的套接字函数:(客户端和服务器都可用)
方法 | 解释 |
---|---|
s.recv() | 接收 TCP 消息 |
s.recv_into() | 接收 TCP 消息到指定的缓冲区 |
s.send() | 发送 TCP 消息 |
s.sendall() | 完整地发送 TCP 消息 |
s.recvfrom() | 接收 UDP 消息 |
s.recvfrom_into() | 接收 UDP 消息到指定的缓冲区 |
s.sendto() | 发送 UDP 消息 |
s.getpeername() | 连接到套接字(TCP)的远程地址 |
s.getsockname() | 当前套接字的地址 |
s.getsockopt() | 返回给定套接字选项的值 |
s.setsockopt() | 设置给定套接字选项的值 |
s.shutdown() | 关闭连接 |
s.close() | 关闭套接字 |
s.detach() | 在未关闭文件描述符的情况下关闭套接字,返回文件描述符 |
s.ioctl() | 控制套接字的模式 |
面向阻塞的套接字方法:
方法 | 解释 |
---|---|
s.setblocking() | 设置套接字的阻塞与非阻塞模式 |
s.settimeout() | 设置阻塞套接字操作的超时时间 |
s.gettimeout() | 得到阻塞套接字操作的超时时间 |
面向文件的套接字函数:
方法 | 解释 |
---|---|
s.setblocking() | 设置套接字的阻塞与非阻塞模式 |
s.settimeout() | 设置阻塞套接字操作的超时时间 |
数据属性:
扫描二维码关注公众号,回复:
3101476 查看本文章
方法 | 解释 |
---|---|
s.family() | 套接字家族 |
s.type | 套接字类型 |
s.proto | 套接字协议 |
10.2 TCP通信
面向连接的套接字
创建TCP套接字,必须使用SOCK_STREAM作为套接字类型。
TCP服务器伪代码:
ss=socket() #创建服务器套接字,即将其赋予某变量
ss.bind() #套接字与地址绑定。服务器需要占用一个端口并等待客户端连接,因此必须绑定到一个本地地址
ss.listen() #监听连接。现在我们是创建TCP服务器,所以需要监听
inf_loop: #服务器无限循环,等待客户端接入
cs=ss.accept() #accept开启一个单线程服务器,接受客户端的连接
comm_loop: #通信循环,客户端与服务端的通信数据流
cs.recv()/cs.send() #对话。接收/发送
cs.close() #关闭客户端套接字。断开与客户端的连接
ss.close() #(不会用到!)关闭服务端套接字。服务器断开与所有的连接
TCP客户端伪代码:
cs=socket() #创建客户端套接字,将其赋予某变量
cs.connect() #尝试连接服务器。就如输入服务器地址一样
comm_loop: #通信循环
cs.send()/cs.recv() #对话。发送/接收
cs.close() #关闭客户端套接字。就如关闭浏览器一样
TCP通信实例一:
上方为服务器代码,下方为客户端代码。下同
# -*- coding: UTF-8 -*-
# 服务器代码段
import socket #导入socket模块
s = socket.socket() #创建socket对象
host = socket.gethostname() #获取本地主机名
port = 12345 #设置端口
s.bind((host, port)) #将套接字绑定到服务器地址。服务器地址为:本机IP:12345
s.listen(5) #等待客户端连接。开启TCP监听
while True:
c, addr = s.accept() #建立并接受客户端连接。注意前面是两个参数。因为客户端传送了两个数据
print u'连接地址:', addr #连接地址为客户端地址
c.send(u'欢迎访问我们'.encode('gb2312')) #发送给客户端的数据
c.close() #关闭客户端套接字
-------------------------------------------------------------------------------------
# -*- coding: UTF-8 -*-
# 客户端代码段
import socket #导入socket模块
s = socket.socket() #创建socket对象
host = socket.gethostname() #获取本地主机名(服务器地址)
port = 12345 #设置端口号(服务器端口)
s.connect((host, port)) #尝试连接服务器,括号中为服务器地址
print s.recv(1024) #对话。接收服务器返回的数据。括号中为缓存大小
s.close() #关闭客户端套接字
TCP通信实例二
# -*- coding: UTF-8 -*-
#服务器代码段
from socket import * #导入socket模块
from time import ctime #导入time模块
host="" #服务器地址。为空代表可以是任何可用的地址,实验可用地址为本地地址(127.0.0.1/localhost)
port=1234 #服务器端口。没有被占用即可
huancun=1024 #设置缓存。该应用程序为1kb
addr=(host,port) #组成服务器的可访问地址
tcpfuwu=socket(AF_INET,SOCK_STREAM) #创建socket对象
tcpfuwu.bind(addr) #将套接字绑定到服务器地址(绑定后地址存在并可连)
tcpfuwu.listen(5) #开启TCP监听,连接在被转接或拒绝之前,传入连接请求的最大数为5
while True:
print u"玩命加载中请耐心等待……" #连接请求出现时即显示
tcpkehu,addr=tcpfuwu.accept() #建立并接受客户端连接。此处是单线程连接
print u"连接来自于:",addr #输出自定义数据
while True:
data=tcpkehu.recv(huancun) #接收客户端传回的数据,不超过缓存大小,超出则服务端奔溃,客户端显示乱码!
if not data: #如果接收数据为空则退出循环,后果是关闭客户端
break
tcpkehu.send('[%s]%s'%(ctime(),data)) #向客户端发送数据。[时间戳]客户端输入的数据
tcpkehu.close() #关闭客户端
tcpfuwu.close() #这条代码永不执行。关闭服务端
-------------------------------------------------------------------------------------
# -*- coding: UTF-8 -*-
#客户端代码段
from socket import *
host="127.0.0.1" #服务器地址
port=1234 #服务器端口
huancun=1024 #应用程序缓存。1kb
addr=(host,port) #组合成服务器地址
tcpkehu=socket(AF_INET,SOCK_STREAM) #创建socket对象
tcpkehu.connect(addr) #连接服务器。括号中为服务器地址
while True:
data=raw_input('>') #一种显示规则。接收键盘输入数据
if not data: #如果输入数据为空,跳出循环。后果是客户端套接字关闭
break
tcpkehu.send(data) #将输入的数据发送给服务器
data=tcpkehu.recv(huancun) #服务器调用recv函数,用于给客户端返回数据
if not data: #如果服务器终止且调用recv函数失败则跳出循环。后果是客户端套接字关闭
break
print data #输出服务器返回给客户端的数据
tcpkehu.close() #关闭客户端套接字
10.3 UDP通信
不需要TCP服务器那么多设置,因为它不是面向连接的
创建UDP套接字,必须使用SOCK_DGRAM作为套接字类型
UDP服务器伪代码:
ss=socket() #创建服务器套接字,即将其赋予某变量
ss.bind() #套接字与地址绑定
inf_loop: #服务器无限循环,等待客户端接入
cs=ss.recvfrom()/ss.sendto() #对话。接收/发送
ss.close() #(不会用到!)关闭服务器套接字
UDP客户端伪代码:
cs=socket() #创建客户端套接字。一旦创建就进入对话循环中
comm_loop: #通信循环
cs.sendto()/cs.recvfrom() #对话。发送/接收
cs.close() #关闭客户端套接字
UDP通信实例:
# -*- coding: UTF-8 -*-
#服务器代码段
from socket import *
from time import ctime
host=""
port=1234
huancun=1024
addr=(host,port)
udpfuwu=socket(AF_INET,SOCK_DGRAM)
udpfuwu.bind(addr) #注意!UDP不用调用监听传入连接(listen)
while True:
print u"玩命加载中请稍后……"
udpkehu,addr=udpfuwu.recvfrom(huancun) #接收客户端传来的数据。客户端发送两个数据给服务器,服务器就需要两个变量来接收。
udpfuwu.sendto('[%s]%s'%(ctime(),udpkehu),addr) #发送数据给客户端
print u"来自连接:",addr
udpfuwu.close()
-------------------------------------------------------------------------------------
# -*- coding: UTF-8 -*-
#客户端代码段
from socket import *
host="localhost"
port=1234
huancun=1024
addr=(host,port)
udpkehu=socket(AF_INET,SOCK_DGRAM)
while True:
data=raw_input(">")
if not data:
break
udpkehu.sendto(data,addr) #将输入的数据发送给服务器
data,addr=udpkehu.recvfrom(huancun) #前面的两个变量可以和服务器的不一样。因为我们只在客户端使用,而服务器的这两个变量我们只在服务器上使用
if not data: #如果服务器终止且调用recv函数失败则跳出循环
break
print data
udpkehu.close()
10.4 socket模块属性
数据类型:
方法 | 解释 |
---|---|
AF_UNIX、AF_INET、AF_INET6、AF_NETLINK、AF_TIPC | Python支持的套接字地址家族 |
SO_STREAM、SO_DGRAM | 套接字类型 (TCP =流, UDP =数据报) |
has_ipv6 | 表示是否支持 IPv6 的布尔标记 |
异常:
方法 | 解释 |
---|---|
error | 套接字相关错误 |
herror | 主机和地址相关的错误 |
gaierror | 地址相关的错误 |
timeout | 超时 |
常见函数:
方法 | 解释 |
---|---|
socket() | 用指定的地址家族,套接字类型和协议类型(可选)创建一个套接字对象 |
socketpair() | 用指定的地址家族,套接字类型和协议类型(可选)创建一对套接字对象 |
create_connection() | 常规函数,它接收一个地址(主机名,端口号)对,返回套接字对象 |
fromfd() | 用一个已经打开的文件描述符创建一个套接字对象 |
ssl() | 通过套接字启动一个安全套接字层(SSL)连接;不做证书验证 |
getaddrinfo() | 获取一个五元组序列形式的地址消息 |
getnameinfo() | 给定一个套接字地址,返回(主机名,端口号)二元组 |
getfqdn() | 返回完整的域名 |
gethostname() | 返回当前主机名 |
gethostbyname() | 将一个主机名映射到它的IP地址 |