Python C/S架构,网络通信相关名词,socket编程
主要内容:
一. C/S架构
二. 网络通信的相关名词
三. socket编程
一. C/S架构和B\S架构概述
1. C/S架构: Client/Server(客户端/服务端)架构
描述: C/S架构--需要服务端部署;用户单独安装客户端,客户端软件升级,服务端要为每个用户服务.可以不联网使用.
客户端(client): 享受服务端提供的服务
服务端(server): 给客户端提供服务
硬件C/S架构: 打印机
软件C/S架构: 聊天软件(如:QQ,微信),视频软件(如:优酷视频,暴风影音)
2. B\S架构: Browser/Server(浏览器/服务器)架构
描述: 需要服务端部署;浏览器访问;服务端负责全部逻辑;升级方便;必须联网使用.
CS/BS本质相同,都是客户端与服务端通信,只是表现为不同的形式,BS架构的软件客户端固定是浏览器.
二. 网络通信的相关名词
1. 集线器(HUB): HUB是一个多端口的转发器.主要功能是对接收到的信号进行再生整形放大,以扩大网络的传输距离,同时把所有节点集中在以它为中心的节点上.将咱们所有的插上集线器的电脑连通起来.
2. 交换机(Switch): 升级版集线器.它可以为接入交换机的任意两个网络节点提供独享的电信号通路.
3. 网卡: 作用是接收电信号,有网络接口(用于插网线)
网卡是局域网中连接计算机和传输介质的接口,能实现与局域网传输介质之间的物理连接和电信号匹配.
4. MAC地址(物理地址): MAC地址又称为物理地址,硬件地址,用来定义网络设备的位置.
MAC地址是网卡决定的,固定的,全球唯一的,相当于每台电脑的上网身份证.
物理地址: 20-47-47-68-EE-DF --> 16进制的6个数表示,前三位厂商编号,后三位生产流水号
5. 多播\单播: 多播指一点对多点的通信,在IPv6中把广播看作是多播的一个特殊例子. 单播指客户端与服务器之间的点到点连接.
6. 广播风暴: 缺点是 -->不安全,容易拥堵网络.
7. IP地址: 英文是Internet Protocol Address, 指互联网协议地址. IP地址是IP协议提供的一种统一的地址格式,它为互联网上的每一个网络和每一台主机分配一个逻辑地址,以此来屏蔽物理地址的差异.
作用: 划分广播域
分类:
IPv4: 4个点份十进制 (目前使用较多)
IPv6: 6个冒号分十六进制
8. DHCP协议: 两个作用
(1)给内部网络或网络服务供应商自动分配IP地址.
(2)给用户或者内部网络管理员作为对所有计算机作中央管理的手段.
9. 子网掩码: 计算是否属于同一网段,属于同一网段的,我们称为属于同一子网.
子网掩码不能单独存在,它必须结合IP地址一起使用.子网掩码只有一个作用,就是将某个IP地址划分成网络地址和主机地址两部分.
10. DNS服务器: 具有域名解析的作用,即把域名解析为IP地址. 默认跟着网关走. 记录着所有的域名和它网站对应的那台服务器的IP地址的对应关系,理解为一个字典.
11. 路由器: 是连接因特网中各局域网,广域网的设备,它会根据信道的情况自动选择和设定路由,以最优路径,按前后顺序发送信号.
12. 路由协议: 用于确定到达路径,计算最优路径.
13. 网关: 是一个网络连接到另一个网络的"关口",也就是网络关卡. 它是公网IP,也可以说是路由器的IP地址.
14. NAT技术: 网络地址转换,将局域网的IP地址转换为公网(网关)的IP地址
15. 局域网: 也称内网
16. 端口:标识电脑上某个应用程序. 通过IP地址+端口-->我们就能唯一确定一台电脑上的某个应用程序.
如果把IP地址比作一间房子,端口就是出入这间房子的门.真正的房子只有几个门,但是一个IP地址的端口可以有65536(即:2^16)个之多!
端口是通过端口号来标记的,端口号只有整数,范围是从 0 到 65535(2^16-1). 其中0-1024是电脑内部服务使用的,不要去占用. 一般来说,自己编程时不去占用8000以内的数值.
17. 端口映射: 访问网站时自动携带一个端口号,用于直接访问服务端.
三. socket(这里是参考文档)
1. 概述:
socket通常也承做"套接字", 用于描述IP地址和端口, 是一个通信链的句柄, 应用程序通常通过"套接字"向网络发出请求或者应答网络请求.
socket起源于Unix, 而Unix/Linux基本哲学之一就是"一切皆文件", 对于文件用[打开][读写][关闭]模式来操作. socket就是该模式的一个实现,socket即是一种特殊的文件,一些socket函数就是对其进行的操作(读/写IO,打开,关闭)
2. socket和file的区别:
file模块是针对某个指定文件进行[打开][读写][关闭]
socket模块是针对 服务器端 和 客户端Socket进行[打开][读写][关闭]
3. 套接字有两种(或者称为有两个种族),分别是基于文件型的和基于网络型的:
(1)基于文件类型的套接字家族
套接字家族的名字: AF_UNIX
unix一切皆文件,基于文件的套接字调用的就是底层的文件系统来取数据,两个套接字进程运行在同一机器,可以通过访问同一个文件系统间接完成通信
(2)基于网络类型的套接字家族
套接字家族的名字: AF_INET
(还有AF_INET6被用于ipv6,还有一些其他的地址家族,不过,他们要么是只用于某个平台,要么就是已经被废弃,或者是很少被使用,或者是根本没有实现,所有地址家族中,AF_INET是使用最广泛的一个,python支持很多种地址家族,但是由于我们只关心网络编程,所以大部分时候我们只使用AF_INET)
4. TCP与UDP区别(很重要!!)
tcp协议:面向连接,消息可靠,相对udp来讲,传输速度慢,消息是面向流的,无消息保护边界(0).
udp协议:面向无连接,消息不可靠,传输速度快,消息是面向包的,有消息保护边界.
5. TCP协议下端口重用问题
当socket绑定IP地址和端口时可能出现以下异常:
OSError: [Errno 48] Address already in use
我们可以采用端口重用的方法解决这个问题:
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
# 加入一条socket配置,重用ip和端口,代码如下: import socket from socket import SOL_SOCKET,SO_REUSEADDR sk = socket.socket() sk.setsockopt(SOL_SOCKET, SO_REUSEADDR, 1) # 在bind前加上这一行代码,表示允许地址重用 sk.bind(('127.0.0.1',8898)) # 把地址绑定到套接字 sk.listen() # 监听链接 conn,addr = sk.accept() # 接受客户端链接 ret = conn.recv(1024) # 接收客户端信息 print(ret) # 打印客户端信息 conn.send(b'hi') # 向客户端发送信息 conn.close() # 关闭客户端套接字 sk.close() # 关闭服务器套接字(可选)
但是如果加上了上面的代码之后还是出现了这样的异常:
OSError: [WinError 10013] 以一种访问权限不允许的方式做了一个访问套接字的尝试
那么意味着你的电脑不支持端口重用,只能更换端口了.
6. TCP和UDP下socket差异对比图:
7. TCP协议下socket的通讯流程:
服务端(server)相关操作:
# 引入socket模块 import socket # 创建服务端对象 surver_obj = socket.socket() # 设置服务端IP和端口号 ip_port = ("IP地址", 端口号) # 绑定IP和端口 surver_obj.bind(ip_port) # 监听客户端对象(侦听客户端请求) surver_obj.listen() # accept()方法使服务端对象接受连接,并返回元组(conn, address) conn, address = surver_obj.accept() # recv(bufsize)方法接受套接字对象的数据.数据以bytes字符串形式返回 from_client_msg = conn.recv(1024) # 把bytes字符串解码成为utf-8格式的字符串 from_client_msg = from_client_msg.decode("utf-8") # 服务端通过套接字对象conn给客户端发送信息(send内部必须是bytes格式的字符串) conn.send("要发送的内容".encode("utf-8")) # 关闭新的套接字对象(理解为连接通道) conn.close() # 关闭服务端对象 surver_obj.close()
客户端(client)相关操作:
# 引入socket模块 import socket # 创建客户端对象 client_obj = socket.socket() # 拿到服务端的IP地址和端口 surver_ip_port = ("IP地址", 端口号) # 客户端对象通过 服务端的IP地址和端口 与服务端建立连接 client_obj.connect(surver_ip_port) # 客户端对象向服务端发送信息 client_obj.send("要发送的内容".encode("utf-8")) # 接收套接字对象的数据,数据以字符串的形式返回,指定每次最多可接收的数据数量是 1024 Bytes from_surver_msg = client_obj.recv(1024) # 把接收到的bytes字符串转换成utf-8格式的字符串 from_surver_msg = from_surver_msg.decode("utf-8") # 关闭客户端对象 client_obj.close()
8. UDP协议下socket的通讯流程:
服务端:
import socket # 创建一个UDP协议下的服务端对象,需要使用参数type=socket.SOCK_DGRAM udp_server = socket.socket(type=socket.SOCK_DGRAM) # 服务端拿到自己的IP地址,并给本程序分配端口号 ip_port = ("192.168.15.28", 11111) # 绑定IP地址和端口 udp_surver.bind(ip_port) # 服务端接收信息 from_client_msg, client_address = udp_surver.recvfrom(1024) # 服务端发送信息 udp_surver.sendto("服务端想要发送的内容".encode("utf-8"), client_address) # 关闭服务端 udp_surver.close()
客户端:
import socket # 创建一个UDP协议下的客户端对象,需要使用参数type=socket.SOCK_DGRAM udp_client = socket.socket(type=socket.SOCK_DGRAM) # 拿到服务端的IP地址和程序端口 surver_ip_port = ("192.168.15.28", 11111) # 客户端发送信息 udp_client.sendto("客户端想要发送的内容".encode("utf-8"), surver_ip_port) # 客户端接收信息 from_surver_msg, surver_address = udp_client.recvfrom(1024) # 关闭客户端 udp_client.close()
9. 以上代码涉及到的方法的解释
(1) obj = socket.socket(family, type, proto)
描述: 创建socket对象
参数:
family(地址族):
socket.AF_INET --> IPv4(默认)
socket.AF_INET6 --> IPv6
type(类型):
socket.SOCK_STREAM --> 面向连接(TCP协议)
socket.SOCK_DGRAM --> 面向不连接(UDP协议)
protocol(协议):
该参数一般不填,默认为0.
(默认)与特定的地址家族相关的协议,如果是0,则系统就会根据地址格式和套接类别,自动选择一个合适的协议.
(2) obj.bind(address)
描述: 将套接字绑定到地址
参数: address地址的格式取决于地址族. 例如,在AF_INET下,以元组(IP地址, 端口号)的形式表示地址
(3) obj.listen(backlog)
描述: 侦听客户端请求
参数: backlog指定在拒绝连接之前,可以挂起的最大连接数量.不写则默认为无限制(一般不写).
(4) conn, address = obj.accept()
描述: 接受连接并返回元组(conn, address), 其中conn是新的套接字对象,可以用来接收和发送数据(可以把conn理解为一个通信管道). address是连接客户端的地址(元组).
(5) obj.connect(address)
描述: 连接到address处的套接字.
参数: 一般address的格式为元组(IP地址, 端口号),如果连接出错,返回socket.error错误.
(6) obj.close()
描述: 关闭套接字
(7) obj.recv(bufsize, flags)
描述: 接受套接字的数据,数据以bytes字符串的形式返回.
参数:
bufsize --> 指定最多可接收的数据量,单位B(bytes)
flags --> flag提供有关消息的其他信息,通常可以忽略.
(8) obj.recvfrom(bufsize, flags)
描述: 与recv()类似,但返回值是(data,address).其中data是包含接收数据的字符串,address是发送数据的套接字地址(元组).该函数主要用于UDP协议.
注意:recvfrom()方法返回客户端数据(bytes字符串格式)和客户端地址,其中客户端地址address是一个元组,内部包括客户端IP地址和客户端程序端口号.
(9) obj.send(data, flags)
描述: 将data中的数据发送到连接的套接字.返回值是要发送的字节数量,该数量可能小于data的字节大小.
(10) obj.sendto(data, address)
描述: 将数据发送到套接字,address是形式为(IP地址,端口号)的元组.指定远程地址.返回值是发送的字节数.该函数主要用于UDP协议.