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

我们可以采用端口重用的方法解决这个问题:

# 加入一条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协议.

posted @ 2018-10-16 10:57  咕噜噜~  阅读(757)  评论(0编辑  收藏  举报