网络编程
1.CS架构和BS架构
CS:
client<======================>server
客户端 服务端
BS:
browser<======================>server
浏览器 服务端
2.osi协议
见Linux
5.套接字
5.1简介
起源:
一开始,套接字被设计用在同一台主机上多个应用程序之间的通讯。这也被称进程间通讯,或IPC。
分为2个种族:
基于文件类型的套接字家族:AF_UNIX
unix一切皆文件,基于文件的套接字调用的就是底层的文件系统来取数据,两个套接字进程运行在同一机器,可以通过访问同一个文件系统间接完成通信。
基于网络类型的套接字家族:AF_INET
现在常用的。
5.2基于TCP协议的socket模板
"""
服务端
"""
import socket
phone = socket.socket(socket.AF_INET, socket.SOCK_STREAM) # 配置套接字家族和tcp模式,生成服务端,可以不传参数,默认是基于网络的TCP
phone.bind(('127.0.0.1', 8082)) # 绑定自己的IP和端口号
phone.listen(5) # 配置半链接池
while True: # 链接循环,可以不停的建立链接
con, add = phone.accept() # 接收链接,返回链接和对方的(IP,端口号)
while True: # 通信循环
try: # 在windows系统中,如果客户端非法断开链接,则会抛出异常
con.send("你好啊,hello".encode("utf-8")) # 发送消息,只能发bytes类型,bytes只能由字符串转换得到
data = con.recv(1024) # 接收消息,设置一次接收的字节数
# if len(data) ==0: # 在UNIX系统中,如果接收到空,则意味着客户端非法断开链接,在windows中不用写
# break
# else:
# print(data.decode("utf-8"))
except Exception:
break
con.close() # 关闭链接
"""
客户端
"""
import socket
phone = socket.socket(socket.AF_INET, socket.SOCK_STREAM) # 配置套接字家族和tcp模式,生成客户端
phone.connect(("127.0.0.1", 8082)) # 发起链接,发送对方的(IP,端口号)
while True:
data = phone.recv(1024)
print(data.decode("utf-8"))
data = input("输入").strip()
if data == "quit": # 设置中断通讯条件
break
phone.send(data.encode("utf-8"))
phone.close()
#注:
#配置中套接字家族有2种:AF_UNIX 或 AF_INET
#配置中传输层模式有2种:SOCK_STREAM 或 SOCK_DGRAM (即TCP和UDP协议)
#可以发送空内容,但接收不能为空,不然会认为没有接收到,继续等待;故不应发送空
#如果接收到空,则是UNIX系统中客户端非法断开链接
#发送的内容只能是bytes,不能是其它类型
5.3基于UDP协议的socket模板
"""
服务端
"""
import socket
phone = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) # 配置套接字家族和tcp模式,生成服务端
phone.bind(("127.0.0.1", 8083)) #不需要建立链接,但仍要绑定自己的iP和端口号
while True: # 链接循环,可以不停的建立链接
data, add = phone.recvfrom(1024) #接收数据,元组类型(数据,(IP,端口号))
data = data.decode("utf-8")
data = eval(data)
data = str(data) # 套接字只能发送字符串
phone.sendto(data.encode("utf-8"), add) # 发送数据,要指定对方IP和端口号
"""
客户端
"""
import socket
phone = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) # 配置套接字家族和tcp模式,生成客户端
while True:
data = input("输入").strip()
if data == "quit": # 设置中断通讯条件
break
phone.sendto(data.encode("utf-8"), ("127.0.0.1", 8083)) #发送数据,要指定对方IP和端口号
data, add = phone.recvfrom(1024) #接收数据,元组类型(数据,(IP,端口号))
print(data.decode("utf-8"))
phone.close()
5.4TCP和UDP协议的区别
#tcp协议是流式协议,发送数据为空时,底层发的就是空,所以接收会出错
#udp协议发送的是数据包,发送数据为空的时候,底层发送的数据包不为空,所以可以成功收到,不会出错
5.5粘包
5.5.1沾包的简介
定义:
粘包问题主要还是因为接收方不知道消息之间的界限,不知道一次性提取多少字节而造成的数据混乱。
发生粘包的2种情况:
1.发送端需要等缓冲区满才发送出去,造成粘包(发送数据时间间隔很短,数据了很小,会合到一起,产生粘包)
2.接收方不及时接收缓冲区的包,造成多个包接收(客户端发送了一段数据,服务端只收了一小部分,服务端下次再收的时候还是从缓冲区拿上次遗留的数据,产生粘包)
注:
#TCP一次取出缓存中指定大小的数据,不管是不是一次发的,故会沾包;udp收不满也只收一次的数据,收不完就扔,故不会沾包。
#udp中sendto和recvfrom是一一对应的,tcp中不需要。
5.5.2解决办法
核心思想:加头,如果是字符串,头就是长度;如果是文件,头就是字典,先将字典json成字符串。
模板:
服务端
import struct
data_len= len(data)
data_len = struct.pack('i',data_len)
con.send(data_len)
客户端
res = struct.unpack('i', phone.recv(4)) # 接收头部,即数据长度,并将其由bytes型转化成int型,放入元组
data_size=res[0] #unpack返回的是一个元组(len,)
5.6.基于socketserver实现并发编程
5.6.1TCP服务端
"""
服务端
"""
import socketserver
class My_bingfa(socketserver.BaseRequestHandler): #自定义一个类,必须继承socketserver.BaseRequestHandler
def handle(self): #必须有一个handler,用来写通讯循环的
print(self.server) #self.request就是socket对象,相当于phone
print(self.request) #self.request就是通讯链接,相当于con
print(self.client_address) #self.client_address就是(地址,端口号)
while True: #通讯循环
data = input("输入:").strip()
res = self.request.send(data.encode("utf-8"))
phone=socketserver.ThreadingTCPServer(('127.0.0.1',8080),My_bingfa) #指定TCP协议,绑定IP和端口号,绑定上面自定义的类
phone.serve_forever() #启动服务
5.6.2UDP服务端
"""
服务端
"""
import socketserver
class My_bingfa(socketserver.BaseRequestHandler): #自定义一个类,必须继承socketserver.BaseRequestHandler
def handle(self): #必须有一个handler,用来写通讯循环的
print(self.request) #self.request是元组(客户端发送的数据,socket对象)
print(self.client_address) #self.client_address就是(地址,端口号)
while True: #通讯循环
data = input("输入:").strip()
res = self.request[1].sendto(data.encode("utf-8"),self.client_address)
phone=socketserver.ThreadingUDPServer(('127.0.0.1',8080),My_bingfa) #指定UDP协议,绑定IP和端口号,绑定上面自定义的类
phone.serve_forever() #启动服务
5.6.3客户端
并发编程是指同时有多个客户端访问服务端,但客户端并不需要特殊处理。故客户端仍保用以前的就可以。
补充:
https的默认端口是443
http的默认端口是80
mysql的默认端口是3306