osi七层协议各层主要的协议
# 物理层传输电信号1010101010 # 数据链路层,以太网协议,arp协议。对这些信号进行分组,同时规范了分组形式--以太网协议,头部是mac地址中间是信息, # 网络层:ip协议,arp协议帮忙找到mac地址,ip,子网掩码,网关(下面有一些简单概括) # 传输层:tcp协议,udp协议 # (socket)就是一组接口,将复杂的tcp协议和udp协议隐藏在一组接口后面,用socket去组织数据,在用户来看复杂的协议变为简单的接口 # 会话层,表示层,应用层 #应用层加tcp/UDP报头,加IP报头,加mac地址报头,然后用电信号发出去 #arp协议:A机器把请求通过交换机通过广播的方式查找B计算机,判断是不是自己的IP地址,不是的话丢弃,是的话返回计算机对应的mac地址给交换机, #交换机再通过单播的方式,返回给A机器 # ,如果不在同一个网段,再通过网关去找目标ip #socket分类: #基于文件类型的套接字家族: AF_UNIX #基于网络类型的套接字家族: AF_INET 还有 AF_INET6被用于ipv6 #IP 端口 :在同一台机器上,同一时间,只能有一个程序,占用同一个端口 #TCP:可靠的,面向连接的,(长连接),速度相对慢的 # UDP协议:不可靠,无连接,效率慢 #报文 数据包10101010就是报文 #我们在网络上传输的所有数据,都叫数据包,数据包里所有的数据都叫报文 #报文里不止有数据 还有 ip地址,mac地址,端口号,所有的报文都有报头,是各个协议规定的
# tcp协议 # 全双工的通信协议:双方都可以收发信息 # 一旦连接建立起来,那么连接两端的机器能够随意互相通信 # 面向连接的通信方式 # 数据安全不容易丢失 # 建立连接的 三次握手 ****** 建立了全双工通信,我问你,你回答我顺便问我,我回答你 三次握手 # 断开连接的 四次挥手 ****** #全双工通信,要一方发起关闭请求,另一方回应关闭,然后达成关闭一条但双工 #然后另一方发起关闭请求,这一方回应关闭,达到关闭全双工的目的,必须要进行 两次的 发起回应
# 物理层传输电信号1010101010 # 数据链路层,以太网协议,arp协议。对这些信号进行分组,同时规范了分组形式--以太网协议,头部是mac地址中间是信息, # 网络层:ip协议,arp协议帮忙找到mac地址,ip,子网掩码,网关(下面有一些简单概括) # 传输层:tcp协议,udp协议 # (socket)就是一组接口,将复杂的tcp协议和udp协议隐藏在一组接口后面,用socket去组织数据,在用户来看复杂的协议变为简单的接口 # 会话层,表示层,应用层 #应用层加tcp/UDP报头,加IP报头,加mac地址报头,然后用电信号发出去 #arp协议:A机器把请求通过交换机通过广播的方式查找B计算机,判断是不是自己的IP地址,不是的话丢弃,是的话返回计算机对应的mac地址给交换机, #交换机再通过单播的方式,返回给A机器 # ,如果不在同一个网段,再通过网关去找目标ip #socket分类: #基于文件类型的套接字家族: AF_UNIX #基于网络类型的套接字家族: AF_INET 还有 AF_INET6被用于ipv6 #IP 端口 :在同一台机器上,同一时间,只能有一个程序,占用同一个端口 #TCP:可靠的,面向连接的,(长连接),速度相对慢的 # UDP协议:不可靠,无连接,效率慢 #报文 数据包10101010就是报文 #我们在网络上传输的所有数据,都叫数据包,数据包里所有的数据都叫报文 #报文里不止有数据 还有 ip地址,mac地址,端口号,所有的报文都有报头,是各个协议规定的 # qq 微信 飞秋 网游 微博 歪歪 _基于应用的网络程序 # 百度 微博 知乎 博客园 网易 _基于浏览器的网络程序 # 网络编程中的 - C/S架构 # c client 客户端 # s server 服务端 # 网络编程中的 - B/S架构 # b broser 浏览器 # s server 服务端 # 不需要额外的安装客户端了,只需要一个网址就可以访问 # 轻量级 - 使用成本低 # B/S架构是C/S架构的一种特殊形式 # 手机上 : 浏览器 app # 两个py程序想要通信 # 写文件 # 在不同机器上的两个py程序之间想要通信 # 网络 # 网络的发展史 # 网卡\网口 # 两台机器之间 插上网线就可以通信 # 网卡上 - mac地址 # ip地址 # 是4个点分十进制 - ipv4协议 # 0.0.0.0 - 255.255.255.255 # 127.0.0.1 本机 # 内网字段 192.168.**** # 10.**** # 172.*** # 6个点分十进制 - ipv6协议 # 0.0.0.0.0.0 - 255.255.255.255.255.255 # 交换机 # 广播 # 单播 # 组播 # arp协议 : 通过IP地址获取某一台机器的mac地址 # 子网掩码 # 子网掩码 和 ip地址进行 按位 与 运算 就能得出一个机器所在的网段 # 192.168.21.36 # 11000000.10101000.00010101.00100100 #111111 # 255.255.255.0 255.255.0.0 # 11111111.11111111.11111111.00000000 #22222 1和2两处按位与 # 11000000.10101000.00010101.00000000 #局域网的网段 # 192.168.21.0 网段 #IP协议作用两个,一个是为每一台计算机分配ip地址,二是确定哪些地址在同一个网段 # 网关地址 : 整个局域网中的机器能沟通过网关ip与外界通信 # 网段 : 子网掩码 和 ip地址进行 按位 与 运算 # 端口 : 0-65535 # 8000-酷狗音乐 22-ssh 3306-mysql # python 网络应用 8000 # ip地址+端口号 : 在全网找到唯一的一台机器+唯一的应用 # 我们选择端口 : 8000之后 # tcp协议 # 全双工的通信协议:双方都可以收发信息 # 一旦连接建立起来,那么连接两端的机器能够随意互相通信 # 面向连接的通信方式 # 数据安全不容易丢失 # 建立连接的 三次握手 ****** 建立了全双工通信,我问你,你回答我顺便问我,我回答你 三次握手 # 断开连接的 四次挥手 ****** #全双工通信,要一方发起关闭请求,另一方回应关闭,然后达成关闭一条但双工 #然后另一方发起关闭请求,这一方回应关闭,达到关闭全双工的目的,必须要进行 两次的 发起回应
Socket是应用层与TCP/IP协议族通信的中间软件抽象层,它是一组接口。在设计模式中,Socket其实就是一个门面模式,它把复杂的TCP/IP协议族隐藏在Socket接口后面,对用户来说,一组简单的接口就是全部,让Socket去组织数据,以符合指定的协议。
其实站在你的角度上看,socket就是一个模块。我们通过调用模块中已经实现的方法建立两个进程之间的连接和通信。 也有人将socket说成ip+port,因为ip是用来标识互联网中的一台主机的位置,而port是用来标识这台机器上的一个应用程序。 所以我们只要确立了ip和port就能找到一个应用程序,并且使用socket模块来与之通信。 3.套接字(socket)的发展史 套接字起源于 20 世纪 70 年代加利福尼亚大学伯克利分校版本的 Unix,即人们所说的 BSD Unix。 因此,有时人们也把套接字称为“伯克利套接字”或“BSD 套接字”。一开始,套接字被设计用在同 一台主机上多个应用程序之间的通讯。这也被称进程间通讯,或 IPC。套接字有两种(或者称为有两个种族),分别是基于文件型的和基于网络型的。 基于文件类型的套接字家族 套接字家族的名字:AF_UNIX unix一切皆文件,基于文件的套接字调用的就是底层的文件系统来取数据,两个套接字进程运行在同一机器,可以通过访问同一个文件系统间接完成通信 基于网络类型的套接字家族 套接字家族的名字:AF_INET (还有AF_INET6被用于ipv6,还有一些其他的地址家族,不过,他们要么是只用于某个平台,要么就是已经被废弃,或者是很少被使用,或者是根本没有实现,所有地址家族中,AF_INET是使用最广泛的一个,python支持很多种地址家族,但是由于我们只关心网络编程,所以大部分时候我么只使用AF_INET)
TCP(Transmission Control Protocol)可靠的、面向连接的协议(eg:打电话)、传输效率低全双工通信(发送缓存&接收缓存)、面向字节流。使用TCP的应用:Web浏览器;电子邮件、文件传输程序。
UDP(User Datagram Protocol)不可靠的、无连接的服务,传输效率高(发送前时延小),一对一、一对多、多对一、多对多、面向报文,尽最大努力服务,无拥塞控制。使用UDP的应用:域名系统 (DNS);视频流;IP语音(VoIP)。
socket模块中TCP/UDP的实现:
基于TCP协议的socket:
import socket sk = socket.socket() #买电话,创建一个socket对象 # sk = socket.socket(socket.AF_INET,socket.SOCK_STREAM) #买电话 网络类型的socket类型和tcp #默认值def __init__(self, family=AF_INET, type=SOCK_STREAM, proto=0, fileno=None): #type=socket.SOCK_DARAM,UDP协议 datagram数据报文的缩写 sk.setsockopt(socket.SOL_SOCKET,socket.SO_REUSEADDR,1) #避免服务重启的时候出现 误 OSError:address already in use #sk.setsockopt(socket.SOL_SOCKET,socket.SO_REUSEADDR,1) sk.bind(('127.0.0.1',8080)) #绑定电话卡,bind方法,接受一个元组 元组里是 (ip,port) sk.listen() #监听,看谁要给我打电话 #括号内可以放参数,比如 listen(5)只允许接收5个请求,就是说这里 backlog-半链接池 大小是5,超过5个其他请求就进不来了 #防止黑客用大量的虚拟客户端访问服务端即洪水攻击,来占满backlog,让其他客户端进不来,其中一个办法就是扩大backlog的空间 conn,addr = sk.accept() #获取connection 和 address 这里conn 和addr 是两个变量,不过通用都这么写 #这里conn就是三次握手的连接 ret = conn.recv(1024) #接收远方来的电话的信息,并设定要接收多少大小,2048,4096,222,随便你设,一般是1024 print(ret) conn.send(b'hi') #接收到信息后,要回馈 ret = conn.recv(1024) print(ret.decode('utf-8')) conn.send('收到'.encode('utf-8')) conn.close() #挂电话 sk.close() #关手机 #有收必有发,收发必相等 # sever ----- client # send ----- recv # send ----- recv # recv ----- send #sever有一个send,client就要有一个receive,也可以连续send两次或者多次,client同样也要receive两次或者多次 #两边的send和recv一定是对应的,不能只接不收,不能只收不接 #正因为tcp这个收发特性,保证了他是可靠连接 # import socket # import time # sk = socket.socket() #创建一个socket对象 # #sk = socket.socket(socket.AF_INET,socket.SOCK_STREAM) #默认值def __init__(self, family=AF_INET, type=SOCK_STREAM, proto=0, fileno=None): #type=socket.SOCK_DARAM,UDP协议 # # sk.setsockopt(socket.SOL_SOCKET,socket.SO_REUSEADDR,1) # sk.bind(('127.0.0.1',8898)) #给sever端绑定一个ip和端口 # sk.listen() #listen括号内放数值 指backlog,默认指的是不限定访问人数 # conn,addr = sk.accept() #完成了三次握手,conn获取到一个客户端的链接 # #程序走在这会阻塞,等待客户端连接 # # while True: # ret = conn.recv(1024).decode('utf-8') #recv又是一个阻塞点,直到收到一个客户端发来的消息 # print(ret) # time_stamp = str(time.ctime(eval(ret))) # conn.send(bytes(time_stamp,encoding='utf-8')) # # conn.close() #关闭连接 # sk.close() #如果不关闭还会继续接收
import socket sk = socket.socket() sk.connect(('127.0.0.1',8080)) #注意这里不能用 bind,因为同一个端口,同一时间同一台计算机只能绑定一个 sk.send(b'hello') # sk.send('你好'.encode('utf-8')) #b'hello' ret = sk.recv(1024) print(ret) sk.send(bytes('中午吃什么',encoding='utf-8')) ret2 = sk.recv(1024) print(ret2.decode('utf-8')) sk.close() #有收必有发,收发必相等 #如果出现错误 OSError:address already in use #就是因为系统还没关闭通信,或者没有写关闭语句
小练习:
1.基于tcp的聊天练习
import socket,time sk = socket.socket() sk.setsockopt(socket.SOL_SOCKET,socket.SO_REUSEADDR,1) sk.bind(('127.0.0.1',8898)) sk.listen() conn,addr = sk.accept() print(addr) while True: ret = conn.recv(1024).decode('utf-8') print(ret) if ret == 'bye': conn.send(b'bye') break info = input('>>> ')+' '+str(time.strftime('%Y-%m-%d %X')) conn.send(bytes(info,encoding='utf-8')) conn.close() sk.close()
import socket,time sk = socket.socket() sk.connect(('127.0.0.1',8898)) print(time.strftime('%Y-%m-%d %X')) while True: info = input('>>> ')+' '+str(time.strftime('%Y-%m-%d %X')) sk.send(bytes(info,encoding='utf-8')) ret = sk.recv(1024).decode('utf-8') print(ret) if ret == 'bye': sk.send(b'bye') break sk.close()
2.基于TCP的记录时间练习
import socket import time sk = socket.socket() #创建一个socket对象 # sk.setsockopt(socket.SOL_SOCKET,socket.SO_REUSEADDR,1) sk.bind(('127.0.0.1',8898)) #给sever端绑定一个ip和端口 sk.listen() #listen括号内放数值 指backlog,默认指的是不限定访问人数 conn,addr = sk.accept() #完成了三次握手,conn获取到一个客户端的链接 #程序走在这会阻塞,等待客户端连接 while True: ret = conn.recv(1024).decode('utf-8') #recv又是一个阻塞点,直到收到一个客户端发来的消息 print(ret) time_stamp = str(time.ctime(eval(ret))) conn.send(bytes(time_stamp,encoding='utf-8')) conn.close() #关闭连接 sk.close() #如果不关闭还会继续接收
import socket import time sk = socket.socket() sk.connect(('127.0.0.1',8898)) while True: ret = str(time.time()) sk.send(bytes(ret,encoding='utf-8')) time_struct = sk.recv(1024).decode('utf-8') print(time_struct) time.sleep(5) sk.close()
基于UDP的socket:
#UDP是必须要等客户端主动发信息的,即被动的等待信息
#TCP主要客户端连接,连接后服务器和客户端都可以主动发信息
#客户端发送消息的同时,还会自带地址信息
#消息回复的时候,不仅需要发送消息,还需要把对方的地址填写在内
import socket sk = socket.socket(type=socket.SOCK_DGRAM) sk.bind(('127.0.0.1',8890)) msg,addr = sk.recvfrom(1024) print(msg.decode('utf-8')) sk.sendto(b'hi',addr) sk.close()
import socket sk = socket.socket(type=socket.SOCK_DGRAM) ip_port = ('127.0.0.1',8890) sk.sendto(b'hello',ip_port) ret,addr = sk.recvfrom(1024) print(ret.decode('utf-8')) sk.close() #UDP不需要进行监听,也不需要建立连接 #UDP是必须要等客户端主动发信息的,即被动的等待信息 #TCP主要客户端连接,连接后服务器和客户端都可以主动发信息 #客户端发送消息的同时,还会自带地址信息 #消息回复的时候,不仅需要发送消息,还需要把对方的地址填写在内
小练习:
1.udp实现的聊天儿
import socket sk = socket.socket(type=socket.SOCK_DGRAM) # sk.setsockopt(socket.SOL_SOCKET,socket.SO_REUSEADDR,1) sk.bind(('127.0.0.1',8803)) while True: msg,addr = sk.recvfrom(1024) print(msg.decode('utf-8')) info = ('\033[34msever: \033'+input('>>> ')).encode('utf-8') sk.sendto(info,addr) sk.close()
import socket sk = socket.socket(type=socket.SOCK_DGRAM) ip_port = ('127.0.0.1',8803) while True: info = ('\033[31mww: \033[0m' + input('>>>> ')).encode('utf-8') sk.sendto(info,ip_port) msg,addr = sk.recvfrom(1024) print(msg.decode('utf-8')) sk.close()
2.udp更新时间
import socket import time sk = socket.socket(type=socket.SOCK_DGRAM) sk.bind(('127.0.0.1',8800)) msg,addr = sk.recvfrom(1024) print(addr) ret = msg.decode('utf-8') print(ret) ret2 = time.strftime(ret) sk.sendto(ret2.encode('utf-8'),addr) sk.close()
import socket sk = socket.socket(type=socket.SOCK_DGRAM) ip_port = ('127.0.0.1',8800) time_format = '%Y-%m-%d %X %a' sk.sendto(time_format.encode('utf-8'),ip_port) msg,addr = sk.recvfrom(1024) print(addr) print(msg.decode('utf-8')) sk.close()
# TCP协议属于 : 传输层 # 面向连接 可靠的 字节流传输 长连接 # UDP协议属于 : 传输层 # 面向数据包的 无连接的 不可靠的 速度快 不占用连接 #UDP不会黏包,但是udp会丢包 #tcp会黏包,但是不会丢包 # 黏包成因: # TCP(transport control protocol,传输控制协议)是面向连接的,面向流的,提供高可靠性服务。 # 收发两端(客户端和服务器端)都要有一一成对的socket,因此,发送端为了将多个发往接收端的包,更有效的发到对方, # 使用了优化方法(Nagle算法),将多次间隔较小且数据量小的数据,合并成一个大的数据块,然后进行封包。 # 这样,接收端,就难于分辨出来了,必须提供科学的拆包机制。 即面向流的通信是无消息保护边界的。 # 当发送端缓冲区的长度大于网卡的MTU时,tcp会将这次发送的数据拆成几个数据包发送出去。 # MTU是Maximum Transmission Unit的缩写。意思是网络上传送的最大数据包。MTU的单位是字节。 # 大部分网络设备的MTU都是1500。如果本机的MTU比网关的MTU大,大的数据包就会被拆开来传送, # 这样会产生很多数据包碎片,增加丢包率,降低网络速度。 # 1.是因为tcp的拆包机制,使得消息没有边界 # 2.当发送端缓冲区的长度大于网卡的MTU时,产生了数据包碎片 #UDP不需要进行监听,也不需要建立连接 #UDP是必须要等客户端主动发信息的,即被动的等待信息 #TCP主要客户端连接,连接后服务器和客户端都可以主动发信息 #客户端发送消息的同时,还会自带地址信息 #消息回复的时候,不仅需要发送消息,还需要把对方的地址填写在内 #有收必有发,收发必相等 (当收发不相等时候,TCP黏包,UDP丢包。如果len(发送)>len(接收)TCP会产生黏包, UDP会直接报错,) # sever ----- client # send ----- recv # send ----- recv # recv ----- send #sever有一个send,client就要有一个receive,也可以连续send两次或者多次,client同样也要receive两次或者多次 #两边的send和recv一定是对应的,不能只接不收,不能只收不接 #正因为tcp这个收发特性,保证了他是可靠连接 #注意send不能发数字,要转为str # recv在自己这端的缓冲区为空时,阻塞 # recvfrom在自己这端的缓冲区为空时,就接收空