一、网络编程
二、实现网络通讯的条件
1、物理连接介质
2、需要统一通讯标准
OSI七层协议
3、TCP建立连接的三次握手和四次挥手
三、什么是socket
Socket是应用层与TCP/IP协议族通信的中间软件抽象层,它是一组接口。在设计模式中,Socket其实就是一个门面模式,它把复杂的TCP/IP协议族隐藏在Socket接口后面,
对用户来说,一组简单的接口就是全部,让Socket去组织数据,以符合指定的协议。
所以,我们无需深入理解tcp/udp协议,socket已经为我们封装好了,我们只需要遵循socket的规定去编程,写出的程序自然就是遵循tcp/udp标准的。
也有人将socket说成ip+port,ip是用来标识互联网中的一台主机的位置,而port是用来标识这台机器上的一个应用程序,ip地址是配置到网卡上的,
而port是应用程序开启的,ip与port的绑定就标识了互联网中独一无二的一个应用程序
而程序的pid是同一台机器上不同进程或者线程的标识
socket 套接字
是介于应用层和传输层之间的封装了 网络层 数据层 物理成 可以直接调用socket接口 帮我们实现通讯
五、解决TCP的粘包问题
该模块可以把一个类型,如数字,转成固定长度的byte
1 服务端
同过导入 import Struct 模块
制作报头
发送报头
发送字典
发送真实信息
客户端f
接收报头
解析报头 获取head_size 获取字典长度 =struct(''i", head_dic)[0]
获取真实数据的字典
head_bytes = json.loads(head_bytes.decode('utf-8'))
# 获取的字典的大小的key 文件名和文件的大小
# 进行文件的操作
打开文件的 ‘wb’
while recv_size < fiel_size :
data = conn/client .recv(1024)
#别忘了写入文件的方法
# f.write(data)
recv_size += len(data)
六、基于socket 上传和下载电影
1、基于Socket大文件传输
客户上传电影
客户端上传电影 import socket import os import json import struct # client = socket.socket() # # # 建立与服务端的链接 client.connect(('127.0.0.1', 8080)) # # # 通讯循环 while True: # 获取电影列表 Movie_path = r'F:day29视频' movie_list = os.listdir(Movie_path) for index, movie in enumerate(movie_list,1): # 0,1,2,3 1 2 3 4 print(index, movie) choice = input('输入电影的编号:').strip() if choice.isdigit(): choice = int(choice)-1 # 0 1 2 3 # len() 4 0 4 v 0 1 2 3 if choice in range(0,len(movie_list)): file_name = movie_list[choice] # 拼接电影文件的路径 movie_path = os.path.join(Movie_path,file_name) # 获取每一步电影文件大小 file_size = os.path.getsize(movie_path) # 产生一个字典 file_title = '精彩电影100部' my_dic = { 'file_name': file_name, 'file_size': file_size, 'file_title': file_title, 'other': '待续' } # 序列化字典 json_dic = json.dumps(my_dic) # 转成二进制字典 json_bytes = json_dic.encode('utf-8') # 制作字典报头 head = struct.pack('i', len(json_bytes)) # 发送报头 client.send(head) # 发送二进制字典 client.send(json_bytes) # 发送真实数据 with open(movie_path, 'rb')as f: for line in f: client.send(line) else: print('序号不在范围内') else: print('请输入数字')
服务端保存电影
# 从服务端接收文件 import struct import json import socket server = socket.socket() # 接收电影 server.bind(('127.0.0.1', 8080)) # 设置半链接池 server.listen(5) # 链接通讯 while True: # 等待链接 conn, addr = server.accept() # 等待链接 # 通讯循环 while True: try: # 捕获文件 # 开始接收信息 # 接收报头 head_dic = conn.recv(4) # 解析报头 获取大小 head_size = struct.unpack('i',head_dic)[0] # 接收字典数据 head_bytes = conn.recv(head_size) # 反序列化获取真实字典 json_dic = json.loads(head_bytes.decode('utf-8')) print(json_dic) # {'file_name': '精彩电影100部', # 'file_size': 50835588, 'other': '待续'} file_name = json_dic.get('file_name') file_size = json_dic.get('file_size') recv_size = 0 with open(file_name, 'wb')as f: while recv_size < file_size: data = conn.recv(1024) f.write(data) recv_size += len(data) # print(recv_size.decode('utf-8')) print('上传成功%s' % file_name) except ConnectionRefusedError as e: print(e) break conn.close()
七、TCP 和UDP的区别
1.TCP和UDP 的特点
1 有无连接:
TCP面向连接协议, 必须建立双向通道 传输数据的可靠性 eg:打电话 相互传输数据)
Web浏览器;电子邮件、文件传输程序。
——UDP是无连接服务,数据不可靠 eg: 发短信 不需要回复
2. 传输方式:>>>(粘包的问题)
TCP的特点是介于流式协议 会将内容比较小和 时间间隔较短的 一次性发送 (产生粘包的问题)
——UDP是基于数据报传输自带报头 不会产生粘包的问题
传输效率高(发送前时延小),一对一、一对多、多对一、多对多、面向报文,尽最大努力服务,无拥塞控制。
使用UDP的应用:域名系统 (DNS);视频流;IP语音(VoIP)。
3.传输效率:
TCP 需要建立双向通道 传输效率低 --UDP 无连接服务 传输效率个高
UDP的特点
1.udp协议客户端允许发空
2.udp协议不会粘包
3.udp协议服务端不存在的情况下,客户端照样不会报错?
4.udp协议支持并发
八、实现UDP 简易版QQ通(并发)
服务端
# 服务端 import socket server = socket.socket(type=socket.SOCK_DGRAM) server.bind(('127.0.0.1', 8080)) while True: # 通讯 # 接受信息 data , addr = server.recvfrom(1024) print(data.decode('utf-8')) # 发送信息 cmd = input('>>>:').encode('utf-8') server.sendto(cmd, addr) # 记得发送对方的地址
服务端1
# 客户端 import socket # 获取对像的ip 地址 client = socket.socket(type=socket.SOCK_DGRAM) server_addrs = ('127.0.0.1', 8080) while True: # 发送信息 msg = input('输入的信息:') msg = '来自客户端1的消息:%s' % msg client.sendto(msg.encode('utf-8'), server_addrs) # 接收信息 data, addr = client.recvfrom(1024) print(data.decode('utf-8'))
客户端2
# 客户端 import socket # 获取对像的ip 地址 client = socket.socket(type=socket.SOCK_DGRAM) server_addrs = ('127.0.0.1', 8080) while True: # 发送信息 msg = input('输入的信息:') msg = '来自客户端2的消息:%s' % msg client.sendto(msg.encode('utf-8'), server_addrs) # 接收信息 data, addr = client.recvfrom(1024) print(data.decode('utf-8'))
客户端3
# 客户端 import socket # 获取对像的ip 地址 client = socket.socket(type=socket.SOCK_DGRAM) server_addrs = ('127.0.0.1', 8080) while True: # 发送信息 msg = input('输入的信息:') msg = '来自客户端3的消息:%s' % msg client.sendto(msg.encode('utf-8'), server_addrs) # 接收信息 data, addr = client.recvfrom(1024) print(data.decode('utf-8'))
七、如何实现TCP的并发
1、Socketserve模块的运用
客服端
# 基于TCP如何 实现并发 # 发送消息给服务端 import socket client = socket.socket() client.connect(('127.0.0.1', 8080)) while True: msg = input('输入信息:').strip() # 发送信息 client.send(msg.encode('utf-8')) data = client.recv(1024) print(data.decode('utf-8'))
# 基于TCP如何 实现并发 # 发送消息给服务端 import socket client = socket.socket() client.connect(('127.0.0.1', 8080)) while True: msg = input('输入信息:').strip() # 发送信息 client.send(msg.encode('utf-8')) data = client.recv(1024) print(data.decode('utf-8'))
# 基于TCP如何 实现并发 # 发送消息给服务端 import socket client = socket.socket() client.connect(('127.0.0.1', 8080)) while True: msg = input('输入信息:').strip() # 发送信息 client.send(msg.encode('utf-8')) data = client.recv(1024) print(data.decode('utf-8'))
可以同时发送信息 服务端 并发量
服务端
# 基于的socketserver 实现并发 import socketserver # class TCPServer(BaseServer): class Myserver(socketserver.BaseRequestHandler): def handle(self): while True: data = self.request.recv(1024) print(data.decode('utf-8')) print(self.client_address) # 客户端的地址 self.request.send(data.upper()) # 发送信息 # HSHSH 客户端发送过来的信息 # ('127.0.0.1', 14657) # TTT # ('127.0.0.1', 14659) # YYYY # ('127.0.0.1', 14660) # UUU # ('127.0.0.1', 14661) # OOO # ('127.0.0.1', 14663) if __name__ == '__main__': server = socketserver.ThreadingTCPServer(('127.0.0.1', 8080), Myserver) # 创建一个基于TCP的server对象 server.serve_forever() # 启动该服务对对象
2、UDP基于Sockserver实现并发
UDP服务端
# UDP实现的并发 import socketserver # 继承一个类 class Myserver(socketserver.BaseRequestHandler): def handle(self): while True: data, sock = self.request print(data.decode('utf-8')) print(self.client_address) # 发送信息 # msg = input('输入信息:').encode('utf-8') sock.sendto(data, self.client_address) if __name__ == '__main__': # 产生一个TCP 对象 server = socketserver.ThreadingUDPServer(('127.0.0.1', 8080), Myserver) # 启动改对象 server.serve_forever()
多个客户端
# UDP的并发实现 import socket # 产生一个对象 import time client = socket.socket(type=socket.SOCK_DGRAM) # 需要获取服务端的通信地址‘ server_addrs = ('127.0.0.1',8080) # 开始通讯 while True: # 发送信息 client.sendto(b'hello baby ', server_addrs) # 接收信息 data, addr = client.recvfrom(1024) print(data.decode('utf-8')) print(addr) time.sleep(1)
客户端2
# UDP的并发实现 import socket # 产生一个对象 import time client = socket.socket(type=socket.SOCK_DGRAM) # 需要获取服务端的通信地址‘ server_addrs = ('127.0.0.1',8080) # 开始通讯 while True: # 发送信息 client.sendto(b'hello baby ba', server_addrs) # 接收信息 data, addr = client.recvfrom(1024) print(data.decode('utf-8')) print(addr) time.sleep(1)
客户端
# UDP的并发实现 import socket # 产生一个对象 import time client = socket.socket(type=socket.SOCK_DGRAM) # 需要获取服务端的通信地址‘ server_addrs = ('127.0.0.1',8080) # 开始通讯 while True: # 发送信息 client.sendto(b'hello', server_addrs) # 接收信息 data, addr = client.recvfrom(1024) print(data.decode('utf-8')) print(addr) time.sleep(1)
八、异常处理(异常捕获)
1、 常见异常
# try: # name # l = [1,2,3] # l[111] # d = {'name':'jason'} # d['password'] # except NameError: # print('NameError') # except IndexError: # print('indexerror') # except KeyError: # print('keyerror') """ 错误发生之后 会立刻停止代码的运行 执行except语句 比对错误类型 """ # try: # # name # # l = [1,2,3] # # l[111] # d = {'name':'jason'} # d['password'] # # Exception # except BaseException: # 万能异常 所有的异常类型都被捕获 # print('老子天下无敌') # try: # # name # l = [1,2,3] # l[111] # # d = {'name':'jason'} # # d['password'] # except Exception: # 万能异常 所有的异常类型都被捕获 # print('老子天下无敌') # else: # print('被检测的代码没有任何的异常发生 才会走else') # finally: # print('无论被检测的代码有没有异常发生 都会在代码运行完毕之后执行我') # 主动抛异常 # if 'egon' == 'DSB': # pass # else: # raise TypeError('尽说大实话') # 关键字raise就是主动抛出异常 # l = [1,2,3] # assert len(l) < 0 # 断言 预言 # 猜某个数据的状态 猜对了 不影响代码执行 正常走 # 猜错了 直接报错 # 自定义异常 #9 自定义异常 class MyError(BaseException): def __init__(self,msg): super().__init__() self.msg=msg def __str__(self): return '<dfsdf%ssdfsdaf>' %self.msg raise MyError('我自己定义的异常') # 主动抛出异常其实就是将异常类的对象打印出来,会走__str__方法
补充如果 端口号 已被使用 already use
#加入一条socket配置,重用ip和端口 server=socket(AF_INET,SOCK_STREAM) server.setsockopt(SOL_SOCKET,SO_REUSEADDR,1) #就是它,在bind前加 server.bind(('127.0.0.1',8080))