1,软件开发的架构
1)C/S架构
C/S即:Client与Server ,中文意思:客户端与服务器端架构,这种架构也是从用户层面(也可以是物理层面)来划分的。
2)B/S架构
B/S即:Browser与Server,中文意思:浏览器端与服务器端架构,这种架构是从用户层面来划分的。
Browser浏览器,其实也是一种Client客户端,只是这个客户端不需要大家去安装什么应用程序,只需在浏览器上通过HTTP请求服务器端相关的资源(网页资源),客户端Browser浏览器就能进行增删改查。
2,网络基础
1)ip
IP地址是指互联网协议地址(英语:Internet Protocol Address,又译为网际协议地址),是IP Address的缩写。IP地址是IP协议提供的一种统一的地址格式,它为互联网上的每一个网络和每一台主机分配一个逻辑地址,以此来屏蔽物理地址的差异。
2)端口
"端口"是英文port的意译,可以认为是设备与外界通讯交流的出口。
3)ip地址精确到具体的一台电脑,而端口精确到具体的程序。
3,osi七层模型
人们按照分工不同把互联网协议从逻辑上划分了层级:
4,socket概念
socket层
5,理解socket
其实站在你的角度上看,socket就是一个模块。我们通过调用模块中已经实现的方法建立两个进程之间的连接和通信。 也有人将socket说成ip+port,因为ip是用来标识互联网中的一台主机的位置,而port是用来标识这台机器上的一个应用程序。 所以我们只要确立了ip和port就能找到一个应用程序,并且使用socket模块来与之通信。
6,tcp协议和udp协议
TCP(Transmission Control Protocol)可靠的、面向连接的协议(eg:打电话)、传输效率低全双工通信(发送缓存&接收缓存)、面向字节流。使用TCP的应用:Web浏览器;电子邮件、文件传输程序。
UDP(User Datagram Protocol)不可靠的、无连接的服务,传输效率高(发送前时延小),一对一、一对多、多对一、多对多、面向报文,尽最大努力服务,无拥塞控制。使用UDP的应用:域名系统 (DNS);视频流;IP语音(VoIP)。
1)基于TCP协议的socket
tcp是基于链接的,必须先启动服务端,然后再启动客户端去链接服务端
server端
import socket sk = socket.socket() 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() #关闭服务器套接字(可选)
client端
import socket sk = socket.socket() # 创建客户套接字 sk.connect(('127.0.0.1',8898)) # 尝试连接服务器 sk.send(b'hello!') ret = sk.recv(1024) # 对话(发送/接收) print(ret) sk.close() # 关闭客户套接字
2)基于UDP协议的socket
udp是无链接的,先启动哪一端都不会报错
server端
import socket sk = socket.socket(type=socket.SOCK_DGRAM) #DGRAM datagram sk.bind(('127.0.0.1',8080)) #只有服务端有的 msg,addr = sk.recvfrom(1024) print(msg.decode('utf-8')) sk.sendto(b'bye',addr) sk.close()
# udp的server 不需要进行监听也不需要建立连接
# 在启动服务之后只能被动的等待客户端发送消息过来
# 客户端发送消息的同时还会 自带地址信息
# 消息回复的时候 不仅需要发送消息,还需要把对方的地址填写上
client端
import socket sk = socket.socket(type=socket.SOCK_DGRAM) ip_port = ('127.0.0.1',8080) sk.sendto(b'hello',ip_port) ret,addr = sk.recvfrom(1024) print(ret.decode('utf-8')) sk.close()
# client端不需要connect 因为UDP协议是不需要建立连接的
# 直接了解到对方的ip和端口信息就发送数据就行了
# sendto和recvfrom的使用方法是完全和server端一致的
3)基于UDP协议的qq聊天
server
import socket sk = socket.socket(type=socket.SOCK_DGRAM) sk.bind(('127.0.0.1',8080)) while True: msg,addr = sk.recvfrom(1024) print(addr) print(msg.decode('utf-8')) info = input('>>>').encode('utf-8') sk.sendto(info,addr) sk.close()
clientT
import socket sk = socket.socket(type=socket.SOCK_DGRAM) ip_port = ('127.0.0.1',8080) while True: info = input('tiger :') info = ('\033[34m来自王宝强的消息 :%s\033[0m'%info).encode('utf-8') sk.sendto(info,ip_port) msg,addr = sk.recvfrom(1024) print(msg.decode('utf-8')) sk.close()
client2
import socket sk = socket.socket(type=socket.SOCK_DGRAM) ip_port = ('127.0.0.1',8080) while True: info = input('贾乃亮 :') info = ('\033[32m来自贾乃亮的消息 :%s\033[0m'%info).encode('utf-8') sk.sendto(info,ip_port) msg,addr = sk.recvfrom(1024) print(msg.decode('utf-8')) sk.close()
7,socket参数的详解
socket.socket(family=AF_INET,type=SOCK_STREAM,proto=0,fileno=None)
8,黏包
让我们基于tcp先制作一个远程执行命令的程序(命令ls -l ;pwd)
基于tcp协议实现的黏包
import socket import subprocess sk = socket.socket() sk.connect(('127.0.0.1',8090)) while True: cmd = sk.recv(1024).decode('gbk') ret = subprocess.Popen(cmd,shell=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE) std_out = 'stdout :'+(ret.stdout.read()).decode('gbk') std_err = 'stderr :'+(ret.stderr.read()).decode('gbk') print(std_out) print(std_err) sk.send(std_out.encode('utf-8')) sk.send(std_err.encode('utf-8')) sk.close()
基于udp协议实现的黏包
import socket sk = socket.socket() sk.bind(('127.0.0.1',8090)) sk.listen() conn,addr = sk.accept() while True: cmd = input('>>>') conn.send(cmd.encode('utf-8')) ret = conn.recv(1024).decode('utf-8') print(ret) conn.close() sk.close()
9,黏包原因
TCP协议中的数据传递
1)tcp协议的拆包机制
1 当发送端缓冲区的长度大于网卡的MTU时,tcp会将这次发送的数据拆成几个数据包发送出去。 2 MTU是Maximum Transmission Unit的缩写。意思是网络上传送的最大数据包。MTU的单位是字节。 大部分网络设备的MTU都是1500。如果本机的MTU比网关的MTU大,大的数据包就会被拆开来传送,这样会产生很多数据包碎片,增加丢包率,降低网络速度。
2)基于tcp协议特点的黏包现象成因
UDP不会发生黏包
UDP(user datagram protocol,用户数据报协议)是无连接的,面向消息的,提供高效率服务。
不会使用块的合并优化算法,, 由于UDP支持的是一对多的模式,所以接收端的skbuff(套接字缓冲区)采用了链式结构来记录每一个到达的UDP包,在每个UDP包中就有了消息头(消息来源地址,端口等信息),这样,对于接收端来说,就容易进行区分处理了。 即面向消息的通信是有消息保护边界的。
对于空消息:tcp是基于数据流的,于是收发的消息不能为空,这就需要在客户端和服务端都添加空消息的处理机制,防止程序卡住,而udp是基于数据报的,即便是你输入的是空内容(直接回车),也可以被发送,udp协议会帮你封装上消息头发送过去。
不可靠不黏包的udp协议:udp的recvfrom是阻塞的,一个recvfrom(x)必须对唯一一个sendinto(y),收完了x个字节的数据就算完成,若是y;x数据就丢失,这意味着udp根本不会粘包,但是会丢数据,不可靠。
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
用UDP协议发送时,用sendto函数最大能发送数据的长度为:65535- IP头(20) – UDP头(8)=65507字节。用sendto函数发送数据时,如果发送数据长度大于该值,则函数会返回错误。(丢弃这个包,不进行发送)
用TCP协议发送时,由于TCP是数据流协议,因此不存在包大小的限制(暂不考虑缓冲区的大小),这是指在用send函数时,数据长度参数不受限制。而实际上,所指定的这段数据并不一定会一次性发送出去,如果这段数据比较长,会被分段发送,如果比较短,可能会等待和下一次数据一起发送。